<commit_before>#pragma once

#include "result_wrapper.hpp"
#include "frontend/Logic.hpp"
#include "frontend/QF_BV.hpp"
#include "Features.hpp"
#include "API/Assertion.hpp"
#include "API/Assumption.hpp"
#include "concurrent/Threaded_Worker.hpp"
#include "support/lazy.hpp"
#include "support/protofy.hpp"

#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/future.hpp>
#include <boost/any.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/proto/core.hpp>
#include <boost/proto/context.hpp>
#include <boost/tr1/unordered_map.hpp>
#include <boost/fusion/sequence/io.hpp>
#include <boost/fusion/sequence/intrinsic/at.hpp>

#include <vector>
#include <iostream>

namespace metaSMT {

  /**
   * @brief Multi-multiple solver contexts and dispatches all calls
   * (assertions/assumptions) to both of them. The "faster" solver context
   * is used to acquire  the assignment for the variables.
   *
   * \tparam SolverContext1 a valid metaSMT context, e.g. DirectSolver_Context<...>
   * \tparam SolverContext2 another valid metaSMT context
   **/
  template<typename SolverContext1, typename SolverContext2>
  struct Threaded_Context
    : boost::proto::callable_context< Threaded_Context<SolverContext1, SolverContext2>, boost::proto::null_context >
  {
    Threaded_Context()
    : ctx1(new SolverContext1)
    , ctx2(new SolverContext2)
    , worker1(ctx1)
    , worker2(ctx2)
    , thread1(worker1)
    , thread2(worker2)
    , lastSAT(0)
    {}

    /**
     * \brief controls the running threads
     *
     * The destructor regulates the threads by interrupting and joining
     * the threads.
     *
     *
     */
    ~Threaded_Context() {
      thread1.interrupt();
      thread2.interrupt();
      thread1.join();
      thread2.join();
    }

    /** \cond */
    template<typename Result>
    struct PTFunction {
      PTFunction( boost::packaged_task<Result> * pt) : pt(pt) {}
      boost::packaged_task<Result> * pt;
      void operator() () {
        (*pt)();
      }
    };

    template<typename Result>
    static boost::function0<void> mkPT(boost::packaged_task<Result> * pt) {
      return PTFunction<Result>(pt);
    }

    template<typename Context>
    struct SolveCaller
    {
      SolveCaller( Context & ctx)
        : ctx(ctx) {}
      bool operator() ()
      {
        return metaSMT::solve(ctx);
      }
      Context & ctx;
    };

    template<typename Context, typename Expr >
    struct ReadCaller
    {
      ReadCaller( Context & ctx, Expr e)
        : ctx(ctx), e(e) {}
      result_wrapper* operator() ()
      {
        return new result_wrapper(metaSMT::read_value(ctx, e));
      }
      Context & ctx;
      Expr e;
    };

    template<typename Context, typename Command, typename Param1>
    struct CommandCaller
    {
      CommandCaller( Context & ctx, Param1 param )
        : ctx(ctx), param(param) {}
      void operator() ()
      {
        ctx.command(Command(), getValue(param) );
      }

      template <typename T>
        T getValue( T const & t) {return t;}

      template <typename T>
        T getValue( boost::shared_future<T> & t) {return t.get();}
      Context & ctx;
      Param1 param;
    };

    template<typename Context, typename Command, typename Param1>
    CommandCaller <Context, Command, Param1>
    call_command( Context & ctx, Command const & cmd, Param1 param1) {
      return CommandCaller <Context, Command, Param1>(ctx, param1);
    }

    typedef boost::fusion::vector<
      boost::shared_future<typename SolverContext1::result_type>,
      boost::shared_future<typename SolverContext2::result_type>
      > result_type;

    template<int N>
    struct unpack_future
    : proto::or_<
      proto::when<
        proto::terminal< result_type >
      , proto::_make_terminal(
          //, boost::proto::terminal <
          //	typename boost::fusion::result_of::at<result_type, boost::mpl::int_<N> >::type
          //  > ( boost::
          proto::functional::at ( proto::_value, boost::mpl::int_<N> () )
        )>
          //  , proto::terminal< proto::_ >
      , proto::nary_expr<proto::_, proto::vararg< unpack_future<N> > >
    > {};

    /** \endcond */

    /**
     * \brief evaluate an expression in both contexts
     *
     * Takes the current expression and creates a task for each Context.
     * The tasks are put into the respective queues and the future results
     * are returned as result_type.
     *
     * \param e The expression to evaluate
     * \return result_type - tuple of future results of the contexts
     *
     */
    template<typename Expr>
    result_type evaluate(Expr const & e) {

      boost::packaged_task <typename SolverContext1::result_type>* pt1
        = new boost::packaged_task<typename SolverContext1::result_type>(metaSMT::lazy(*ctx1, unpack_future<0>()(e) ));

      boost::packaged_task <typename SolverContext2::result_type>* pt2
        = new boost::packaged_task<typename SolverContext2::result_type>(metaSMT::lazy(*ctx2, unpack_future<1>()(e) ));

      worker1.push(mkPT(pt1));
      worker2.push(mkPT(pt2));

      boost::shared_future<typename SolverContext1::result_type> future1 ( pt1->get_future() );
      boost::shared_future<typename SolverContext2::result_type> future2 ( pt2->get_future() );

      result_type r(future1, future2);
      return r;
    }

    /**
     * \brief read the backend result from an expression of the last call of solve
     *
     * This method reads the result of the last call of solve for bitvectors
     * and predicates. It takes an expression and creates a task for the
     * respective context. Dependent on the result of the solver, the task is
     * put into the respective queue and the result is returned as
     * result_wrapper.
     *
     * \param e The expression to read
     * \return result_wrapper
     *
     */
    template<typename Expr>
    result_wrapper read_value (Expr const & e)
    {
      if( lastSAT == 1 )  {
        boost::packaged_task <result_wrapper*>* pt1
          = new boost::packaged_task<result_wrapper*>(
              ReadCaller<SolverContext1, Expr>(*ctx1, e) );
        worker1.push(mkPT(pt1));

        std::auto_ptr<result_wrapper> ptr( pt1->get_future().get() );
        std::cout << "read from 1: " <<  *ptr << std::endl;
        return  *ptr;

      } else if (lastSAT == 2) {
        boost::packaged_task <result_wrapper*>* pt2
          = new boost::packaged_task<result_wrapper*>(
              ReadCaller<SolverContext2, Expr>(*ctx2, e) );
        worker2.push(mkPT(pt2));

        std::auto_ptr<result_wrapper> ptr( pt2->get_future().get() );
        std::cout << "read from 2: " <<  *ptr << std::endl;
        return  *ptr;
      }
      return result_wrapper("X");
    }

    /**
     * \brief read the value from a result_type of the last call of solve
     *
     * Reads the result of the call of solve only for result_types.
     * Dependent to the result of the solver, the result_type is put
     * into a tasks and then pushed into the respective queue.
     * This method returns a result_wrapper.
     *
     * \param e result_type to read
     * \return result_wrapper
     *
     */
    result_wrapper read_value (result_type & e)
    {
      using boost::fusion::at_c;
      if( lastSAT == 1 )  {
        boost::packaged_task <result_wrapper*>* pt1
          = new boost::packaged_task<result_wrapper*>(
              ReadCaller<SolverContext1, typename SolverContext1::result_type>(*ctx1, at_c<0>(e).get()) );
        worker1.push(mkPT(pt1));

        std::auto_ptr<result_wrapper> ptr( pt1->get_future().get() );
        std::cout << "read from 1': " <<  *ptr << std::endl;
        return  *ptr;

      } else if (lastSAT == 2) {
        boost::packaged_task <result_wrapper*>* pt2
          = new boost::packaged_task<result_wrapper*>(
              ReadCaller<SolverContext2, typename SolverContext2::result_type>(*ctx2, at_c<1>(e).get()) );
        worker2.push(mkPT(pt2));

        std::auto_ptr<result_wrapper> ptr( pt2->get_future().get() );
        std::cout << "read from 2': " <<  *ptr << std::endl;
        return  *ptr;
      }
      return result_wrapper("X");

    }

    /**
     * \brief insert a result_type into queues
     *
     * Takes a result_type and split it into the respective queues.
     *
     * \param cmd  - a Command
     * \param e - a result_type
     * \return void
     *
     */
    template<typename Command>
    void command( Command const & cmd, result_type e)
    {
      worker1.push( call_command (*ctx1, cmd, boost::fusion::at_c<0>(e)) );
      worker2.push( call_command (*ctx2, cmd, boost::fusion::at_c<1>(e)) );
    }

    /**
     * \brief insert an argument into queues
     *
     * This method takes an argument and push it into the two queues.
     *
     * \param cmd - a Command
     * \param a1 - an argument
     * \return void
     *
     */

    template<typename Command, typename Arg1>
    void command( Command const & cmd, Arg1 a1)
    {
      worker1.push( call_command (*ctx1, cmd, a1) );
      worker2.push( call_command (*ctx2, cmd, a1) );
    }

    bool solve()
    {
      boost::packaged_task <bool>* pt1
        = new boost::packaged_task<bool>( SolveCaller<SolverContext1>(*ctx1) );
      boost::packaged_task <bool>* pt2
        = new boost::packaged_task<bool>( SolveCaller<SolverContext2>(*ctx2) );

      worker1.push(mkPT(pt1));
      worker2.push(mkPT(pt2));

      boost::unique_future<bool> future1 = pt1->get_future();
      boost::unique_future<bool> future2 = pt2->get_future();

      unsigned id = 1 + boost::wait_for_any(future1, future2);
      lastSAT = id;

      std::cout << "erster: " << id << std::endl;
      if( id == 1 ) {
        return future1.get();
      }

      return future2.get();
    }

    private:
      boost::shared_ptr<SolverContext1> ctx1;
      boost::shared_ptr<SolverContext2> ctx2;

      concurrent::ThreadedWorkerWrapper<SolverContext1> worker1;
      concurrent::ThreadedWorkerWrapper<SolverContext2> worker2;

      boost::thread thread1;
      boost::thread thread2;

      // the id of the solver which returned the last SAT result in solve,
      // 0: UNSAT/invalid
      // 1: ctx1
      // 2: ctx2
      unsigned lastSAT;

      // disable copying DirectSolvers;
      Threaded_Context(Threaded_Context const & );
      Threaded_Context& operator=(Threaded_Context const & );
  };

  namespace features {
  //  template<typename Context, typename Feature>
  //  struct supports< Threaded_Context<Context>, Feature>
  //  : supports<Context, Feature>::type {};

 template<typename Context1, typename Context2, typename Feature>
  struct supports< Threaded_Context<Context1, Context2>, Feature>
  : boost::mpl::and_<
    typename supports<Context1, Feature>::type,
    typename supports<Context2, Feature>::type
  >::type {};

  }

  template <typename SolverType1, typename SolverType2, typename Expr>
  typename boost::disable_if<
    typename boost::is_same<Expr, typename Threaded_Context<SolverType1, SolverType2>::result_type>::type,
    typename Threaded_Context<SolverType1, SolverType2>::result_type
  >::type
  evaluate( Threaded_Context<SolverType1, SolverType2> & ctx, Expr const & e ) {
    return ctx.evaluate(e);
  }

  template <typename SolverType1, typename SolverType2>
  typename Threaded_Context<SolverType1, SolverType2>::result_type
  evaluate( Threaded_Context<SolverType1, SolverType2> & ctx,
            typename Threaded_Context<SolverType1, SolverType2>::result_type r ) {
    return r;
  }

  template <typename SolverType1, typename SolverType2>
  bool solve( Threaded_Context<SolverType1, SolverType2> & ctx) {
    return ctx.solve();
  }

  template <typename SolverType1, typename SolverType2>
  result_wrapper
  read_value(
      Threaded_Context<SolverType1, SolverType2> & ctx
    , logic::QF_BV::bitvector const & var)
  {
    return ctx.read_value( var );
  }

  template <typename SolverType1, typename SolverType2>
  result_wrapper
  read_value(
      Threaded_Context<SolverType1, SolverType2> & ctx
    , logic::predicate const & var)
  {
    return ctx.read_value( var );
  }

  template <typename SolverType1, typename SolverType2>
  result_wrapper
  read_value(
      Threaded_Context<SolverType1,SolverType2> & ctx
    , typename Threaded_Context<SolverType1,SolverType2>::result_type var)
  {
    return ctx.read_value( var );
  }

} // namespace metaSMT

//  vim: ft=cpp:ts=2:sw=2:expandtab
<commit_msg>Threaded_Context takes a template parameter to configure join behaviour<commit_after>#pragma once

#include "result_wrapper.hpp"
#include "frontend/Logic.hpp"
#include "frontend/QF_BV.hpp"
#include "Features.hpp"
#include "API/Assertion.hpp"
#include "API/Assumption.hpp"
#include "concurrent/Threaded_Worker.hpp"
#include "support/lazy.hpp"
#include "support/protofy.hpp"

#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/future.hpp>
#include <boost/any.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/proto/core.hpp>
#include <boost/proto/context.hpp>
#include <boost/tr1/unordered_map.hpp>
#include <boost/fusion/sequence/io.hpp>
#include <boost/fusion/sequence/intrinsic/at.hpp>

#include <vector>
#include <iostream>

namespace metaSMT {

  /**
   * @brief Run two solver contexts in two threads and dispatch all calls
   * (assertions/assumptions,commands) to both of them. The "faster" solver
   * context is used to return the results and acquire variable assignments
   * assignment.
   *
   * \tparam SolverContext1 a metaSMT context to be instanciated as the first thread (e.g. DirectSolvers_Context<...>)
   * \tparam SolverContext2 another valid metaSMT context for the second thread
   *
   * \tparam  WaitForJoin Boolean value, wait for both threeads to finish
   *          in the destructor of Threaded_Context? (default: no)
   **/
  template<typename SolverContext1, typename SolverContext2, bool WaitForJoin=false>
  struct Threaded_Context
    : boost::proto::callable_context< Threaded_Context<SolverContext1, SolverContext2>, boost::proto::null_context >
  {
    Threaded_Context()
    : ctx1(new SolverContext1)
    , ctx2(new SolverContext2)
    , worker1(ctx1)
    , worker2(ctx2)
    , thread1(worker1)
    , thread2(worker2)
    , lastSAT(0)
    {}

    /**
     * \brief controls the running threads
     *
     * The destructor regulates the threads by interrupting and joining
     * the threads.
     *
     *
     */
    ~Threaded_Context() {
      thread1.interrupt();
      thread2.interrupt();
      if(WaitForJoin) {
        thread1.join();
        thread2.join();
      }
    }

    /** \cond */
    template<typename Result>
    struct PTFunction {
      PTFunction( boost::packaged_task<Result> * pt) : pt(pt) {}
      boost::packaged_task<Result> * pt;
      void operator() () {
        (*pt)();
      }
    };

    template<typename Result>
    static boost::function0<void> mkPT(boost::packaged_task<Result> * pt) {
      return PTFunction<Result>(pt);
    }

    template<typename Context>
    struct SolveCaller
    {
      SolveCaller( Context & ctx)
        : ctx(ctx) {}
      bool operator() ()
      {
        return metaSMT::solve(ctx);
      }
      Context & ctx;
    };

    template<typename Context, typename Expr >
    struct ReadCaller
    {
      ReadCaller( Context & ctx, Expr e)
        : ctx(ctx), e(e) {}
      result_wrapper* operator() ()
      {
        return new result_wrapper(metaSMT::read_value(ctx, e));
      }
      Context & ctx;
      Expr e;
    };

    template<typename Context, typename Command, typename Param1>
    struct CommandCaller
    {
      CommandCaller( Context & ctx, Param1 param )
        : ctx(ctx), param(param) {}
      void operator() ()
      {
        ctx.command(Command(), getValue(param) );
      }

      template <typename T>
        T getValue( T const & t) {return t;}

      template <typename T>
        T getValue( boost::shared_future<T> & t) {return t.get();}
      Context & ctx;
      Param1 param;
    };

    template<typename Context, typename Command, typename Param1>
    CommandCaller <Context, Command, Param1>
    call_command( Context & ctx, Command const & cmd, Param1 param1) {
      return CommandCaller <Context, Command, Param1>(ctx, param1);
    }

    typedef boost::fusion::vector<
      boost::shared_future<typename SolverContext1::result_type>,
      boost::shared_future<typename SolverContext2::result_type>
      > result_type;

    template<int N>
    struct unpack_future
    : proto::or_<
      proto::when<
        proto::terminal< result_type >
      , proto::_make_terminal(
          //, boost::proto::terminal <
          //	typename boost::fusion::result_of::at<result_type, boost::mpl::int_<N> >::type
          //  > ( boost::
          proto::functional::at ( proto::_value, boost::mpl::int_<N> () )
        )>
          //  , proto::terminal< proto::_ >
      , proto::nary_expr<proto::_, proto::vararg< unpack_future<N> > >
    > {};

    /** \endcond */

    /**
     * \brief evaluate an expression in both contexts
     *
     * Takes the current expression and creates a task for each Context.
     * The tasks are put into the respective queues and the future results
     * are returned as result_type.
     *
     * \param e The expression to evaluate
     * \return result_type - tuple of future results of the contexts
     *
     */
    template<typename Expr>
    result_type evaluate(Expr const & e) {

      boost::packaged_task <typename SolverContext1::result_type>* pt1
        = new boost::packaged_task<typename SolverContext1::result_type>(metaSMT::lazy(*ctx1, unpack_future<0>()(e) ));

      boost::packaged_task <typename SolverContext2::result_type>* pt2
        = new boost::packaged_task<typename SolverContext2::result_type>(metaSMT::lazy(*ctx2, unpack_future<1>()(e) ));

      worker1.push(mkPT(pt1));
      worker2.push(mkPT(pt2));

      boost::shared_future<typename SolverContext1::result_type> future1 ( pt1->get_future() );
      boost::shared_future<typename SolverContext2::result_type> future2 ( pt2->get_future() );

      result_type r(future1, future2);
      return r;
    }

    /**
     * \brief read the backend result from an expression of the last call of solve
     *
     * This method reads the result of the last call of solve for bitvectors
     * and predicates. It takes an expression and creates a task for the
     * respective context. Dependent on the result of the solver, the task is
     * put into the respective queue and the result is returned as
     * result_wrapper.
     *
     * \param e The expression to read
     * \return result_wrapper
     *
     */
    template<typename Expr>
    result_wrapper read_value (Expr const & e)
    {
      if( lastSAT == 1 )  {
        boost::packaged_task <result_wrapper*>* pt1
          = new boost::packaged_task<result_wrapper*>(
              ReadCaller<SolverContext1, Expr>(*ctx1, e) );
        worker1.push(mkPT(pt1));

        std::auto_ptr<result_wrapper> ptr( pt1->get_future().get() );
        std::cout << "read from 1: " <<  *ptr << std::endl;
        return  *ptr;

      } else if (lastSAT == 2) {
        boost::packaged_task <result_wrapper*>* pt2
          = new boost::packaged_task<result_wrapper*>(
              ReadCaller<SolverContext2, Expr>(*ctx2, e) );
        worker2.push(mkPT(pt2));

        std::auto_ptr<result_wrapper> ptr( pt2->get_future().get() );
        std::cout << "read from 2: " <<  *ptr << std::endl;
        return  *ptr;
      }
      return result_wrapper("X");
    }

    /**
     * \brief read the value from a result_type of the last call of solve
     *
     * Reads the result of the call of solve only for result_types.
     * Dependent to the result of the solver, the result_type is put
     * into a tasks and then pushed into the respective queue.
     * This method returns a result_wrapper.
     *
     * \param e result_type to read
     * \return result_wrapper
     *
     */
    result_wrapper read_value (result_type & e)
    {
      using boost::fusion::at_c;
      if( lastSAT == 1 )  {
        boost::packaged_task <result_wrapper*>* pt1
          = new boost::packaged_task<result_wrapper*>(
              ReadCaller<SolverContext1, typename SolverContext1::result_type>(*ctx1, at_c<0>(e).get()) );
        worker1.push(mkPT(pt1));

        std::auto_ptr<result_wrapper> ptr( pt1->get_future().get() );
        std::cout << "read from 1': " <<  *ptr << std::endl;
        return  *ptr;

      } else if (lastSAT == 2) {
        boost::packaged_task <result_wrapper*>* pt2
          = new boost::packaged_task<result_wrapper*>(
              ReadCaller<SolverContext2, typename SolverContext2::result_type>(*ctx2, at_c<1>(e).get()) );
        worker2.push(mkPT(pt2));

        std::auto_ptr<result_wrapper> ptr( pt2->get_future().get() );
        std::cout << "read from 2': " <<  *ptr << std::endl;
        return  *ptr;
      }
      return result_wrapper("X");

    }

    /**
     * \brief insert a result_type into queues
     *
     * Takes a result_type and split it into the respective queues.
     *
     * \param cmd  - a Command
     * \param e - a result_type
     * \return void
     *
     */
    template<typename Command>
    void command( Command const & cmd, result_type e)
    {
      worker1.push( call_command (*ctx1, cmd, boost::fusion::at_c<0>(e)) );
      worker2.push( call_command (*ctx2, cmd, boost::fusion::at_c<1>(e)) );
    }

    /**
     * \brief insert an argument into queues
     *
     * This method takes an argument and push it into the two queues.
     *
     * \param cmd - a Command
     * \param a1 - an argument
     * \return void
     *
     */

    template<typename Command, typename Arg1>
    void command( Command const & cmd, Arg1 a1)
    {
      worker1.push( call_command (*ctx1, cmd, a1) );
      worker2.push( call_command (*ctx2, cmd, a1) );
    }

    bool solve()
    {
      boost::packaged_task <bool>* pt1
        = new boost::packaged_task<bool>( SolveCaller<SolverContext1>(*ctx1) );
      boost::packaged_task <bool>* pt2
        = new boost::packaged_task<bool>( SolveCaller<SolverContext2>(*ctx2) );

      worker1.push(mkPT(pt1));
      worker2.push(mkPT(pt2));

      boost::unique_future<bool> future1 = pt1->get_future();
      boost::unique_future<bool> future2 = pt2->get_future();

      unsigned id = 1 + boost::wait_for_any(future1, future2);
      lastSAT = id;

      std::cout << "erster: " << id << std::endl;
      if( id == 1 ) {
        return future1.get();
      }

      return future2.get();
    }

    private:
      boost::shared_ptr<SolverContext1> ctx1;
      boost::shared_ptr<SolverContext2> ctx2;

      concurrent::ThreadedWorkerWrapper<SolverContext1> worker1;
      concurrent::ThreadedWorkerWrapper<SolverContext2> worker2;

      boost::thread thread1;
      boost::thread thread2;

      // the id of the solver which returned the last SAT result in solve,
      // 0: UNSAT/invalid
      // 1: ctx1
      // 2: ctx2
      unsigned lastSAT;

      // disable copying DirectSolvers;
      Threaded_Context(Threaded_Context const & );
      Threaded_Context& operator=(Threaded_Context const & );
  };

  namespace features {
  //  template<typename Context, typename Feature>
  //  struct supports< Threaded_Context<Context>, Feature>
  //  : supports<Context, Feature>::type {};

 template<typename Context1, typename Context2, typename Feature>
  struct supports< Threaded_Context<Context1, Context2>, Feature>
  : boost::mpl::and_<
    typename supports<Context1, Feature>::type,
    typename supports<Context2, Feature>::type
  >::type {};

  }

  template <typename SolverType1, typename SolverType2, typename Expr>
  typename boost::disable_if<
    typename boost::is_same<Expr, typename Threaded_Context<SolverType1, SolverType2>::result_type>::type,
    typename Threaded_Context<SolverType1, SolverType2>::result_type
  >::type
  evaluate( Threaded_Context<SolverType1, SolverType2> & ctx, Expr const & e ) {
    return ctx.evaluate(e);
  }

  template <typename SolverType1, typename SolverType2>
  typename Threaded_Context<SolverType1, SolverType2>::result_type
  evaluate( Threaded_Context<SolverType1, SolverType2> & ctx,
            typename Threaded_Context<SolverType1, SolverType2>::result_type r ) {
    return r;
  }

  template <typename SolverType1, typename SolverType2>
  bool solve( Threaded_Context<SolverType1, SolverType2> & ctx) {
    return ctx.solve();
  }

  template <typename SolverType1, typename SolverType2>
  result_wrapper
  read_value(
      Threaded_Context<SolverType1, SolverType2> & ctx
    , logic::QF_BV::bitvector const & var)
  {
    return ctx.read_value( var );
  }

  template <typename SolverType1, typename SolverType2>
  result_wrapper
  read_value(
      Threaded_Context<SolverType1, SolverType2> & ctx
    , logic::predicate const & var)
  {
    return ctx.read_value( var );
  }

  template <typename SolverType1, typename SolverType2>
  result_wrapper
  read_value(
      Threaded_Context<SolverType1,SolverType2> & ctx
    , typename Threaded_Context<SolverType1,SolverType2>::result_type var)
  {
    return ctx.read_value( var );
  }

} // namespace metaSMT

//  vim: ft=cpp:ts=2:sw=2:expandtab
<|endoftext|>


<commit_before>#include <sysexits.h>
#include <unistd.h>
#include <iostream>

#include "dwarf.h"
#include "dump.h"
#include "elfinfo.h"
#include "procinfo.h"

extern "C" {
#include "proc_service.h"
}

static int usage(void);
std::ostream &
Process::pstack(std::ostream &os, const PstackOptions &options)
{
    ps_pstop(this);

    std::list<ThreadStack> threadStacks;

    // get its back trace.

    listThreads(
        [&threadStacks, this](const td_thrhandle_t *thr) {
            CoreRegisters regs;
            td_err_e the;
#ifdef __linux__
            the = td_thr_getgregs(thr, (elf_greg_t *) &regs);
#else
            the = td_thr_getgregs(thr, &regs);
#endif
            if (the == TD_OK) {
                threadStacks.push_back(ThreadStack());
                td_thr_get_info(thr, &threadStacks.back().info);
                threadStacks.back().unwind(*this, regs);
            }
    });

    if (threadStacks.empty()) {
        // get the register for the process itself, and use those.
        CoreRegisters regs;
        getRegs(ps_getpid(this),  &regs);
        threadStacks.push_back(ThreadStack());
        threadStacks.back().unwind(*this, regs);
    }

    ps_pcontinue(this);
    /*
     * resume at this point - maybe a bit optimistic if a shared library gets
     * unloaded while we print stuff out, but worth the risk, normally.
     */
    const char *sep = "";
    for (auto &s : threadStacks) {
        dumpStackText(os, s, options);
        os << "\n";
        sep = ", ";
    }
    return os;
}

int
emain(int argc, char **argv)
{
    int error, i, c;
    pid_t pid;
    std::string execFile;
    std::shared_ptr<ElfObject> exec;

    PstackOptions options;

    while ((c = getopt(argc, argv, "d:D:hsvn")) != -1) {
        switch (c) {
        case 'D':
        case 'd': {
            /* Undocumented option to dump image contents */
            auto dumpobj = std::make_shared<ElfObject>(std::make_shared<FileReader>(optarg, -1));
            if (c == 'D')
                std::cout << "{ \"elf\": ";
            std::cout << *dumpobj;
            if (c == 'D') {
                DwarfInfo dwarf(dumpobj);
                std::cout << ", \"dwarf\": " << dwarf << "}";
            }
            return 0;
        }
        case 'h':
            usage();
            return (0);
        case 's':
            options += PstackOptions::nosrc;
            break;
        case 'v':
            debug = &std::clog;
            break;
        case 'n':
            noDebugLibs = true;
            break;
        default:
            return usage();
        }
    }

    if (optind == argc)
        return usage();

    for (error = 0, i = optind; i < argc; i++) {
        pid = atoi(argv[i]);
        if (pid == 0 || (kill(pid, 0) == -1 && errno == ESRCH)) {
            // It's a file: should be ELF, treat core and exe differently
            auto obj = std::make_shared<ElfObject>(std::make_shared<FileReader>(argv[i]));
            if (obj->getElfHeader().e_type == ET_CORE) {
                CoreProcess proc(exec, obj, std::vector<std::pair<std::string, std::string>>());
                proc.load();
                proc.pstack(std::cout, options);
            } else {
                exec = obj;
            }
        } else {
            LiveProcess proc(exec, pid);
            proc.load();
            proc.pstack(std::cout, options);
        }
    }
    return (error);
}

int
main(int argc, char **argv)
{
    try {
        emain(argc, argv);
    }
    catch (std::exception &ex) {
        std::clog << "error: " << ex.what() << std::endl;
    }
}

static int
usage(void)
{
    std::clog <<
        "usage: pstack\n\t"
        "[-<D|d> <elf object>]        dump details of ELF object (D => show DWARF info\n"
        "[-D <elf object>]            dump details of ELF object (including DWARF info)\n\t"
        "or\n\t"
        "[-h]                         show this message\n"
        "or\n\t"
        "[-v]                         include verbose information to stderr\n\t"
        "[-s]                         don't include source-level details\n\t"
        "[<pid>|<core>|<executable>]* list cores and pids to examine. An executable\n\t"
        "                             will override use of in-core or in-process information\n\t"
        "                             to predict location of the executable\n"
        ;
    return (EX_USAGE);
}
<commit_msg>Invert meaning of "-n"<commit_after>#include <sysexits.h>
#include <unistd.h>
#include <iostream>

#include "dwarf.h"
#include "dump.h"
#include "elfinfo.h"
#include "procinfo.h"

extern "C" {
#include "proc_service.h"
}

static int usage(void);
std::ostream &
Process::pstack(std::ostream &os, const PstackOptions &options)
{
    ps_pstop(this);

    std::list<ThreadStack> threadStacks;

    // get its back trace.

    listThreads(
        [&threadStacks, this](const td_thrhandle_t *thr) {
            CoreRegisters regs;
            td_err_e the;
#ifdef __linux__
            the = td_thr_getgregs(thr, (elf_greg_t *) &regs);
#else
            the = td_thr_getgregs(thr, &regs);
#endif
            if (the == TD_OK) {
                threadStacks.push_back(ThreadStack());
                td_thr_get_info(thr, &threadStacks.back().info);
                threadStacks.back().unwind(*this, regs);
            }
    });

    if (threadStacks.empty()) {
        // get the register for the process itself, and use those.
        CoreRegisters regs;
        getRegs(ps_getpid(this),  &regs);
        threadStacks.push_back(ThreadStack());
        threadStacks.back().unwind(*this, regs);
    }

    ps_pcontinue(this);
    /*
     * resume at this point - maybe a bit optimistic if a shared library gets
     * unloaded while we print stuff out, but worth the risk, normally.
     */
    const char *sep = "";
    for (auto &s : threadStacks) {
        dumpStackText(os, s, options);
        os << "\n";
        sep = ", ";
    }
    return os;
}

int
emain(int argc, char **argv)
{
    int error, i, c;
    pid_t pid;
    std::string execFile;
    std::shared_ptr<ElfObject> exec;

    PstackOptions options;
    noDebugLibs = true;

    while ((c = getopt(argc, argv, "d:D:hsvn")) != -1) {
        switch (c) {
        case 'D':
        case 'd': {
            /* Undocumented option to dump image contents */
            auto dumpobj = std::make_shared<ElfObject>(std::make_shared<FileReader>(optarg, -1));
            if (c == 'D')
                std::cout << "{ \"elf\": ";
            std::cout << *dumpobj;
            if (c == 'D') {
                DwarfInfo dwarf(dumpobj);
                std::cout << ", \"dwarf\": " << dwarf << "}";
            }
            return 0;
        }
        case 'h':
            usage();
            return (0);
        case 's':
            options += PstackOptions::nosrc;
            break;
        case 'v':
            debug = &std::clog;
            break;
        case 'n':
            noDebugLibs = false;
            break;
        default:
            return usage();
        }
    }

    if (optind == argc)
        return usage();

    for (error = 0, i = optind; i < argc; i++) {
        pid = atoi(argv[i]);
        if (pid == 0 || (kill(pid, 0) == -1 && errno == ESRCH)) {
            // It's a file: should be ELF, treat core and exe differently
            auto obj = std::make_shared<ElfObject>(std::make_shared<FileReader>(argv[i]));
            if (obj->getElfHeader().e_type == ET_CORE) {
                CoreProcess proc(exec, obj, std::vector<std::pair<std::string, std::string>>());
                proc.load();
                proc.pstack(std::cout, options);
            } else {
                exec = obj;
            }
        } else {
            LiveProcess proc(exec, pid);
            proc.load();
            proc.pstack(std::cout, options);
        }
    }
    return (error);
}

int
main(int argc, char **argv)
{
    try {
        emain(argc, argv);
    }
    catch (std::exception &ex) {
        std::clog << "error: " << ex.what() << std::endl;
    }
}

static int
usage(void)
{
    std::clog <<
        "usage: pstack\n\t"
        "[-<D|d> <elf object>]        dump details of ELF object (D => show DWARF info\n"
        "[-D <elf object>]            dump details of ELF object (including DWARF info)\n\t"
        "or\n\t"
        "[-h]                         show this message\n"
        "or\n\t"
        "[-v]                         include verbose information to stderr\n\t"
        "[-s]                         don't include source-level details\n\t"
        "[<pid>|<core>|<executable>]* list cores and pids to examine. An executable\n\t"
        "                             will override use of in-core or in-process information\n\t"
        "                             to predict location of the executable\n"
        ;
    return (EX_USAGE);
}
<|endoftext|>


<commit_before>// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <map>
#include <string>
#include <vector>

#include "rcutils/logging_macros.h"

#include "rmw/error_handling.h"
#include "rmw/rmw.h"
#include "rmw/types.h"

#include "rmw_fastrtps_cpp/identifier.hpp"
#include "demangle.hpp"
#include "rmw_fastrtps_cpp/custom_participant_info.hpp"

extern "C"
{
rmw_ret_t
rmw_count_publishers(
  const rmw_node_t * node,
  const char * topic_name,
  size_t * count)
{
  // safechecks

  if (!node) {
    RMW_SET_ERROR_MSG("null node handle");
    return RMW_RET_ERROR;
  }
  // Get participant pointer from node
  if (node->implementation_identifier != eprosima_fastrtps_identifier) {
    RMW_SET_ERROR_MSG("node handle not from this implementation");
    return RMW_RET_ERROR;
  }

  CustomParticipantInfo * impl = static_cast<CustomParticipantInfo *>(node->data);

  std::map<std::string, std::vector<std::string>> unfiltered_topics;
  WriterInfo * slave_target = impl->secondaryPubListener;
  slave_target->mapmutex.lock();
  for (auto it : slave_target->topicNtypes) {
    for (auto & itt : it.second) {
      // truncate the ROS specific prefix
      auto topic_fqdn = _demangle_if_ros_topic(it.first);
      unfiltered_topics[topic_fqdn].push_back(itt);
    }
  }
  slave_target->mapmutex.unlock();

  // get count
  auto it = unfiltered_topics.find(topic_name);
  if (it == unfiltered_topics.end()) {
    *count = 0;
  } else {
    *count = it->second.size();
  }

  RCUTILS_LOG_DEBUG_NAMED(
    "rmw_fastrtps_cpp",
    "looking for subscriber topic: %s", topic_name)
  for (auto it : unfiltered_topics) {
    RCUTILS_LOG_DEBUG_NAMED(
      "rmw_fastrtps_cpp",
      "available topic: %s", it.first.c_str())
  }
  RCUTILS_LOG_DEBUG_NAMED(
    "rmw_fastrtps_cpp",
    "number of matches: %zu", *count)

  return RMW_RET_OK;
}

rmw_ret_t
rmw_count_subscribers(
  const rmw_node_t * node,
  const char * topic_name,
  size_t * count)
{
  // safechecks

  if (!node) {
    RMW_SET_ERROR_MSG("null node handle");
    return RMW_RET_ERROR;
  }
  // Get participant pointer from node
  if (node->implementation_identifier != eprosima_fastrtps_identifier) {
    RMW_SET_ERROR_MSG("node handle not from this implementation");
    return RMW_RET_ERROR;
  }

  CustomParticipantInfo * impl = static_cast<CustomParticipantInfo *>(node->data);

  std::map<std::string, std::vector<std::string>> unfiltered_topics;
  ReaderInfo * slave_target = impl->secondarySubListener;
  slave_target->mapmutex.lock();
  for (auto it : slave_target->topicNtypes) {
    for (auto & itt : it.second) {
      // truncate the ROS specific prefix
      auto topic_fqdn = _demangle_if_ros_topic(it.first);
      unfiltered_topics[topic_fqdn].push_back(itt);
    }
  }
  slave_target->mapmutex.unlock();

  // get_count
  auto it = unfiltered_topics.find(topic_name);
  if (it == unfiltered_topics.end()) {
    *count = 0;
  } else {
    *count = it->second.size();
  }

  RCUTILS_LOG_DEBUG_NAMED(
    "rmw_fastrtps_cpp",
    "looking for subscriber topic: %s", topic_name)
  for (auto it : unfiltered_topics) {
    RCUTILS_LOG_DEBUG_NAMED(
      "rmw_fastrtps_cpp",
      "available topic: %s", it.first.c_str())
  }
  RCUTILS_LOG_DEBUG_NAMED(
    "rmw_fastrtps_cpp",
    "number of matches: %zu", *count)

  return RMW_RET_OK;
}
}  // extern "C"
<commit_msg>Remove string allocation in the count of subscribers and publishers<commit_after>// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <map>
#include <string>
#include <vector>

#include "rcutils/logging_macros.h"

#include "rmw/error_handling.h"
#include "rmw/rmw.h"
#include "rmw/types.h"

#include "rmw_fastrtps_cpp/identifier.hpp"
#include "demangle.hpp"
#include "rmw_fastrtps_cpp/custom_participant_info.hpp"

extern "C"
{
rmw_ret_t
rmw_count_publishers(
  const rmw_node_t * node,
  const char * topic_name,
  size_t * count)
{
  // safechecks

  if (!node) {
    RMW_SET_ERROR_MSG("null node handle");
    return RMW_RET_ERROR;
  }
  // Get participant pointer from node
  if (node->implementation_identifier != eprosima_fastrtps_identifier) {
    RMW_SET_ERROR_MSG("node handle not from this implementation");
    return RMW_RET_ERROR;
  }

  CustomParticipantInfo * impl = static_cast<CustomParticipantInfo *>(node->data);

  WriterInfo * slave_target = impl->secondaryPubListener;
  slave_target->mapmutex.lock();
  *count = 0;
  for (auto it : slave_target->topicNtypes) {
    auto topic_fqdn = _demangle_if_ros_topic(it.first);
    if (topic_fqdn == topic_name) {
      *count += it.second.size();
    }
  }
  slave_target->mapmutex.unlock();

  RCUTILS_LOG_DEBUG_NAMED(
    "rmw_fastrtps_cpp",
    "looking for subscriber topic: %s, number of matches: %zu",
    topic_name, *count)

  return RMW_RET_OK;
}

rmw_ret_t
rmw_count_subscribers(
  const rmw_node_t * node,
  const char * topic_name,
  size_t * count)
{
  // safechecks

  if (!node) {
    RMW_SET_ERROR_MSG("null node handle");
    return RMW_RET_ERROR;
  }
  // Get participant pointer from node
  if (node->implementation_identifier != eprosima_fastrtps_identifier) {
    RMW_SET_ERROR_MSG("node handle not from this implementation");
    return RMW_RET_ERROR;
  }

  CustomParticipantInfo * impl = static_cast<CustomParticipantInfo *>(node->data);

  ReaderInfo * slave_target = impl->secondarySubListener;
  *count = 0;
  slave_target->mapmutex.lock();
  for (auto it : slave_target->topicNtypes) {
    auto topic_fqdn = _demangle_if_ros_topic(it.first);
    if (topic_fqdn == topic_name) {
      *count += it.second.size();
    }
  }
  slave_target->mapmutex.unlock();

  RCUTILS_LOG_DEBUG_NAMED(
    "rmw_fastrtps_cpp",
    "looking for subscriber topic: %s, number of matches: %zu",
    topic_name, *count)

  return RMW_RET_OK;
}
}  // extern "C"
<|endoftext|>


<commit_before>/**
 * @file kmeans_test.cpp
 * @author Ryan Curtin
 */
#include <mlpack/core.hpp>

#include <mlpack/methods/kmeans/kmeans.hpp>
#include <mlpack/methods/kmeans/allow_empty_clusters.hpp>

#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"

using namespace mlpack;
using namespace mlpack::kmeans;

BOOST_AUTO_TEST_SUITE(KMeansTest);

// Generate dataset; written transposed because it's easier to read.
arma::mat kMeansData("  0.0   0.0;" // Class 1.
                     "  0.3   0.4;"
                     "  0.1   0.0;"
                     "  0.1   0.3;"
                     " -0.2  -0.2;"
                     " -0.1   0.3;"
                     " -0.4   0.1;"
                     "  0.2  -0.1;"
                     "  0.3   0.0;"
                     " -0.3  -0.3;"
                     "  0.1  -0.1;"
                     "  0.2  -0.3;"
                     " -0.3   0.2;"
                     " 10.0  10.0;" // Class 2.
                     " 10.1   9.9;"
                     "  9.9  10.0;"
                     " 10.2   9.7;"
                     " 10.2   9.8;"
                     "  9.7  10.3;"
                     "  9.9  10.1;"
                     "-10.0   5.0;" // Class 3.
                     " -9.8   5.1;"
                     " -9.9   4.9;"
                     "-10.0   4.9;"
                     "-10.2   5.2;"
                     "-10.1   5.1;"
                     "-10.3   5.3;"
                     "-10.0   4.8;"
                     " -9.6   5.0;"
                     " -9.8   5.1;");

/**
 * 30-point 3-class test case for K-Means, with no overclustering.
 */
BOOST_AUTO_TEST_CASE(KMeansNoOverclusteringTest)
{
  KMeans<> kmeans; // No overclustering.

  arma::Col<size_t> assignments;
  kmeans.Cluster((arma::mat) trans(kMeansData), 3, assignments);

  // Now make sure we got it all right.  There is no restriction on how the
  // clusters are ordered, so we have to be careful about that.
  size_t firstClass = assignments(0);

  for (size_t i = 1; i < 13; i++)
    BOOST_REQUIRE_EQUAL(assignments(i), firstClass);

  size_t secondClass = assignments(13);

  // To ensure that class 1 != class 2.
  BOOST_REQUIRE_NE(firstClass, secondClass);

  for (size_t i = 13; i < 20; i++)
    BOOST_REQUIRE_EQUAL(assignments(i), secondClass);

  size_t thirdClass = assignments(20);

  // To ensure that this is the third class which we haven't seen yet.
  BOOST_REQUIRE_NE(firstClass, thirdClass);
  BOOST_REQUIRE_NE(secondClass, thirdClass);

  for (size_t i = 20; i < 30; i++)
    BOOST_REQUIRE_EQUAL(assignments(i), thirdClass);
}

/**
 * 30-point 3-class test case for K-Means, with overclustering.
 */
BOOST_AUTO_TEST_CASE(KMeansOverclusteringTest)
{
  KMeans<> kmeans(1000, 4.0); // Overclustering factor of 4.0.

  arma::Col<size_t> assignments;
  kmeans.Cluster((arma::mat) trans(kMeansData), 3, assignments);

  // Now make sure we got it all right.  There is no restriction on how the
  // clusters are ordered, so we have to be careful about that.
  size_t firstClass = assignments(0);

  for (size_t i = 1; i < 13; i++)
    BOOST_REQUIRE_EQUAL(assignments(i), firstClass);

  size_t secondClass = assignments(13);

  // To ensure that class 1 != class 2.
  BOOST_REQUIRE_NE(firstClass, secondClass);

  for (size_t i = 13; i < 20; i++)
    BOOST_REQUIRE_EQUAL(assignments(i), secondClass);

  size_t thirdClass = assignments(20);

  // To ensure that this is the third class which we haven't seen yet.
  BOOST_REQUIRE_NE(firstClass, thirdClass);
  BOOST_REQUIRE_NE(secondClass, thirdClass);

  for (size_t i = 20; i < 30; i++)
    BOOST_REQUIRE_EQUAL(assignments(i), thirdClass);
}

/**
 * Make sure the empty cluster policy class does nothing.
 */
BOOST_AUTO_TEST_CASE(AllowEmptyClusterTest)
{
  arma::Col<size_t> assignments;
  assignments.randu(30);
  arma::Col<size_t> assignmentsOld = assignments;

  arma::mat centroids;
  centroids.randu(30, 3); // This doesn't matter.

  arma::Col<size_t> counts(3);
  counts[0] = accu(assignments == 0);
  counts[1] = accu(assignments == 1);
  counts[2] = 0;
  arma::Col<size_t> countsOld = counts;

  // Make sure the method doesn't modify any points.
  BOOST_REQUIRE_EQUAL(AllowEmptyClusters::EmptyCluster(kMeansData, 2, centroids,
      counts, assignments), 0);

  // Make sure no assignments were changed.
  for (size_t i = 0; i < assignments.n_elem; i++)
    BOOST_REQUIRE_EQUAL(assignments[i], assignmentsOld[i]);

  // Make sure no counts were changed.
  for (size_t i = 0; i < 3; i++)
    BOOST_REQUIRE_EQUAL(counts[i], countsOld[i]);
}

/**
 * Make sure the max variance method finds the correct point.
 */
BOOST_AUTO_TEST_CASE(MaxVarianceNewClusterTest)
{
  // Five points.
  arma::mat data("0.4 1.0 5.0 -2.0 -2.5;"
                 "1.0 0.8 0.7  5.1  5.2;");

  // Point 2 is the mis-clustered point we're looking for to be moved.
  arma::Col<size_t> assignments("0 0 0 1 1");

  arma::mat centroids(2, 3);
  centroids.col(0) = (1.0 / 3.0) * (data.col(0) + data.col(1) + data.col(2));
  centroids.col(1) = 0.5 * (data.col(3) + data.col(4));
  centroids(0, 2) = 0;
  centroids(1, 2) = 0;

  arma::Col<size_t> counts("3 2 0");

  // This should only change one point.
  BOOST_REQUIRE_EQUAL(MaxVarianceNewCluster::EmptyCluster(data, 2, centroids,
      counts, assignments), 1);

  // Ensure that the cluster assignments are right.
  BOOST_REQUIRE_EQUAL(assignments[0], 0);
  BOOST_REQUIRE_EQUAL(assignments[1], 0);
  BOOST_REQUIRE_EQUAL(assignments[2], 2);
  BOOST_REQUIRE_EQUAL(assignments[3], 1);
  BOOST_REQUIRE_EQUAL(assignments[4], 1);

  // Ensure that the counts are right.
  BOOST_REQUIRE_EQUAL(counts[0], 2);
  BOOST_REQUIRE_EQUAL(counts[1], 2);
  BOOST_REQUIRE_EQUAL(counts[2], 1);
}

/**
 * Make sure the random partitioner seems to return valid results.
 */
BOOST_AUTO_TEST_CASE(RandomPartitionTest)
{
  arma::mat data;
  data.randu(2, 1000); // One thousand points.

  arma::Col<size_t> assignments;

  // We'll ask for 18 clusters (arbitrary).
  RandomPartition::Cluster(data, 18, assignments);

  // Ensure that the right number of assignments were given.
  BOOST_REQUIRE_EQUAL(assignments.n_elem, 1000);

  // Ensure that no value is greater than 17 (the maximum valid cluster).
  for (size_t i = 0; i < 1000; i++)
    BOOST_REQUIRE_LT(assignments[i], 18);
}

#ifdef ARMA_HAS_SPMAT
/**
 * Make sure sparse k-means works okay.
 */
BOOST_AUTO_TEST_CASE(SparseKMeansTest)
{
  // Huge dimensionality, few points.
  arma::SpMat<double> data(5000, 12);
  data(14, 0) = 6.4;
  data(14, 1) = 6.3;
  data(14, 2) = 6.5;
  data(14, 3) = 6.2;
  data(14, 4) = 6.1;
  data(14, 5) = 6.6;
  data(1402, 6) = -3.2;
  data(1402, 7) = -3.3;
  data(1402, 8) = -3.1;
  data(1402, 9) = -3.4;
  data(1402, 10) = -3.5;
  data(1402, 11) = -3.0;

  arma::Col<size_t> assignments;

  KMeans<> kmeans; // Default options.

  kmeans.Cluster(data, 2, assignments);

  size_t clusterOne = assignments[0];
  size_t clusterTwo = assignments[6];

  BOOST_REQUIRE_EQUAL(assignments[0], clusterOne);
  BOOST_REQUIRE_EQUAL(assignments[1], clusterOne);
  BOOST_REQUIRE_EQUAL(assignments[2], clusterOne);
  BOOST_REQUIRE_EQUAL(assignments[3], clusterOne);
  BOOST_REQUIRE_EQUAL(assignments[4], clusterOne);
  BOOST_REQUIRE_EQUAL(assignments[5], clusterOne);
  BOOST_REQUIRE_EQUAL(assignments[6], clusterTwo);
  BOOST_REQUIRE_EQUAL(assignments[7], clusterTwo);
  BOOST_REQUIRE_EQUAL(assignments[8], clusterTwo);
  BOOST_REQUIRE_EQUAL(assignments[9], clusterTwo);
  BOOST_REQUIRE_EQUAL(assignments[10], clusterTwo);
  BOOST_REQUIRE_EQUAL(assignments[11], clusterTwo);
}
#endif // ARMA_HAS_SPMAT

BOOST_AUTO_TEST_SUITE_END();
<commit_msg>Exclude Armadillo 3.4 from K-Means sparse test because sparse var() was not implemented in that particular release.<commit_after>/**
 * @file kmeans_test.cpp
 * @author Ryan Curtin
 */
#include <mlpack/core.hpp>

#include <mlpack/methods/kmeans/kmeans.hpp>
#include <mlpack/methods/kmeans/allow_empty_clusters.hpp>

#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"

using namespace mlpack;
using namespace mlpack::kmeans;

BOOST_AUTO_TEST_SUITE(KMeansTest);

// Generate dataset; written transposed because it's easier to read.
arma::mat kMeansData("  0.0   0.0;" // Class 1.
                     "  0.3   0.4;"
                     "  0.1   0.0;"
                     "  0.1   0.3;"
                     " -0.2  -0.2;"
                     " -0.1   0.3;"
                     " -0.4   0.1;"
                     "  0.2  -0.1;"
                     "  0.3   0.0;"
                     " -0.3  -0.3;"
                     "  0.1  -0.1;"
                     "  0.2  -0.3;"
                     " -0.3   0.2;"
                     " 10.0  10.0;" // Class 2.
                     " 10.1   9.9;"
                     "  9.9  10.0;"
                     " 10.2   9.7;"
                     " 10.2   9.8;"
                     "  9.7  10.3;"
                     "  9.9  10.1;"
                     "-10.0   5.0;" // Class 3.
                     " -9.8   5.1;"
                     " -9.9   4.9;"
                     "-10.0   4.9;"
                     "-10.2   5.2;"
                     "-10.1   5.1;"
                     "-10.3   5.3;"
                     "-10.0   4.8;"
                     " -9.6   5.0;"
                     " -9.8   5.1;");

/**
 * 30-point 3-class test case for K-Means, with no overclustering.
 */
BOOST_AUTO_TEST_CASE(KMeansNoOverclusteringTest)
{
  KMeans<> kmeans; // No overclustering.

  arma::Col<size_t> assignments;
  kmeans.Cluster((arma::mat) trans(kMeansData), 3, assignments);

  // Now make sure we got it all right.  There is no restriction on how the
  // clusters are ordered, so we have to be careful about that.
  size_t firstClass = assignments(0);

  for (size_t i = 1; i < 13; i++)
    BOOST_REQUIRE_EQUAL(assignments(i), firstClass);

  size_t secondClass = assignments(13);

  // To ensure that class 1 != class 2.
  BOOST_REQUIRE_NE(firstClass, secondClass);

  for (size_t i = 13; i < 20; i++)
    BOOST_REQUIRE_EQUAL(assignments(i), secondClass);

  size_t thirdClass = assignments(20);

  // To ensure that this is the third class which we haven't seen yet.
  BOOST_REQUIRE_NE(firstClass, thirdClass);
  BOOST_REQUIRE_NE(secondClass, thirdClass);

  for (size_t i = 20; i < 30; i++)
    BOOST_REQUIRE_EQUAL(assignments(i), thirdClass);
}

/**
 * 30-point 3-class test case for K-Means, with overclustering.
 */
BOOST_AUTO_TEST_CASE(KMeansOverclusteringTest)
{
  KMeans<> kmeans(1000, 4.0); // Overclustering factor of 4.0.

  arma::Col<size_t> assignments;
  kmeans.Cluster((arma::mat) trans(kMeansData), 3, assignments);

  // Now make sure we got it all right.  There is no restriction on how the
  // clusters are ordered, so we have to be careful about that.
  size_t firstClass = assignments(0);

  for (size_t i = 1; i < 13; i++)
    BOOST_REQUIRE_EQUAL(assignments(i), firstClass);

  size_t secondClass = assignments(13);

  // To ensure that class 1 != class 2.
  BOOST_REQUIRE_NE(firstClass, secondClass);

  for (size_t i = 13; i < 20; i++)
    BOOST_REQUIRE_EQUAL(assignments(i), secondClass);

  size_t thirdClass = assignments(20);

  // To ensure that this is the third class which we haven't seen yet.
  BOOST_REQUIRE_NE(firstClass, thirdClass);
  BOOST_REQUIRE_NE(secondClass, thirdClass);

  for (size_t i = 20; i < 30; i++)
    BOOST_REQUIRE_EQUAL(assignments(i), thirdClass);
}

/**
 * Make sure the empty cluster policy class does nothing.
 */
BOOST_AUTO_TEST_CASE(AllowEmptyClusterTest)
{
  arma::Col<size_t> assignments;
  assignments.randu(30);
  arma::Col<size_t> assignmentsOld = assignments;

  arma::mat centroids;
  centroids.randu(30, 3); // This doesn't matter.

  arma::Col<size_t> counts(3);
  counts[0] = accu(assignments == 0);
  counts[1] = accu(assignments == 1);
  counts[2] = 0;
  arma::Col<size_t> countsOld = counts;

  // Make sure the method doesn't modify any points.
  BOOST_REQUIRE_EQUAL(AllowEmptyClusters::EmptyCluster(kMeansData, 2, centroids,
      counts, assignments), 0);

  // Make sure no assignments were changed.
  for (size_t i = 0; i < assignments.n_elem; i++)
    BOOST_REQUIRE_EQUAL(assignments[i], assignmentsOld[i]);

  // Make sure no counts were changed.
  for (size_t i = 0; i < 3; i++)
    BOOST_REQUIRE_EQUAL(counts[i], countsOld[i]);
}

/**
 * Make sure the max variance method finds the correct point.
 */
BOOST_AUTO_TEST_CASE(MaxVarianceNewClusterTest)
{
  // Five points.
  arma::mat data("0.4 1.0 5.0 -2.0 -2.5;"
                 "1.0 0.8 0.7  5.1  5.2;");

  // Point 2 is the mis-clustered point we're looking for to be moved.
  arma::Col<size_t> assignments("0 0 0 1 1");

  arma::mat centroids(2, 3);
  centroids.col(0) = (1.0 / 3.0) * (data.col(0) + data.col(1) + data.col(2));
  centroids.col(1) = 0.5 * (data.col(3) + data.col(4));
  centroids(0, 2) = 0;
  centroids(1, 2) = 0;

  arma::Col<size_t> counts("3 2 0");

  // This should only change one point.
  BOOST_REQUIRE_EQUAL(MaxVarianceNewCluster::EmptyCluster(data, 2, centroids,
      counts, assignments), 1);

  // Ensure that the cluster assignments are right.
  BOOST_REQUIRE_EQUAL(assignments[0], 0);
  BOOST_REQUIRE_EQUAL(assignments[1], 0);
  BOOST_REQUIRE_EQUAL(assignments[2], 2);
  BOOST_REQUIRE_EQUAL(assignments[3], 1);
  BOOST_REQUIRE_EQUAL(assignments[4], 1);

  // Ensure that the counts are right.
  BOOST_REQUIRE_EQUAL(counts[0], 2);
  BOOST_REQUIRE_EQUAL(counts[1], 2);
  BOOST_REQUIRE_EQUAL(counts[2], 1);
}

/**
 * Make sure the random partitioner seems to return valid results.
 */
BOOST_AUTO_TEST_CASE(RandomPartitionTest)
{
  arma::mat data;
  data.randu(2, 1000); // One thousand points.

  arma::Col<size_t> assignments;

  // We'll ask for 18 clusters (arbitrary).
  RandomPartition::Cluster(data, 18, assignments);

  // Ensure that the right number of assignments were given.
  BOOST_REQUIRE_EQUAL(assignments.n_elem, 1000);

  // Ensure that no value is greater than 17 (the maximum valid cluster).
  for (size_t i = 0; i < 1000; i++)
    BOOST_REQUIRE_LT(assignments[i], 18);
}

#ifdef ARMA_HAS_SPMAT
// Can't do this test on Armadillo 3.4; var(SpBase) is not implemented.
#if !((ARMA_MAJOR_VERSION == 3) && (ARMA_MINOR_VERSION == 4))

/**
 * Make sure sparse k-means works okay.
 */
BOOST_AUTO_TEST_CASE(SparseKMeansTest)
{
  // Huge dimensionality, few points.
  arma::SpMat<double> data(5000, 12);
  data(14, 0) = 6.4;
  data(14, 1) = 6.3;
  data(14, 2) = 6.5;
  data(14, 3) = 6.2;
  data(14, 4) = 6.1;
  data(14, 5) = 6.6;
  data(1402, 6) = -3.2;
  data(1402, 7) = -3.3;
  data(1402, 8) = -3.1;
  data(1402, 9) = -3.4;
  data(1402, 10) = -3.5;
  data(1402, 11) = -3.0;

  arma::Col<size_t> assignments;

  KMeans<> kmeans; // Default options.

  kmeans.Cluster(data, 2, assignments);

  size_t clusterOne = assignments[0];
  size_t clusterTwo = assignments[6];

  BOOST_REQUIRE_EQUAL(assignments[0], clusterOne);
  BOOST_REQUIRE_EQUAL(assignments[1], clusterOne);
  BOOST_REQUIRE_EQUAL(assignments[2], clusterOne);
  BOOST_REQUIRE_EQUAL(assignments[3], clusterOne);
  BOOST_REQUIRE_EQUAL(assignments[4], clusterOne);
  BOOST_REQUIRE_EQUAL(assignments[5], clusterOne);
  BOOST_REQUIRE_EQUAL(assignments[6], clusterTwo);
  BOOST_REQUIRE_EQUAL(assignments[7], clusterTwo);
  BOOST_REQUIRE_EQUAL(assignments[8], clusterTwo);
  BOOST_REQUIRE_EQUAL(assignments[9], clusterTwo);
  BOOST_REQUIRE_EQUAL(assignments[10], clusterTwo);
  BOOST_REQUIRE_EQUAL(assignments[11], clusterTwo);
}
#endif // Exclude Armadillo 3.4.
#endif // ARMA_HAS_SPMAT

BOOST_AUTO_TEST_SUITE_END();
<|endoftext|>


<commit_before>/*
    Copyright (c) 2019 Xavier Leclercq

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    IN THE SOFTWARE.
*/

#include "wxlinechartpanel.h"

wxLineChartPanel::wxLineChartPanel(wxWindow* parent)
    : wxPanel(parent)
{
}
<commit_msg>Update wxlinechartpanel.cpp<commit_after>/*
    Copyright (c) 2019 Xavier Leclercq

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    IN THE SOFTWARE.
*/

#include "wxlinechartpanel.h"
#include <wx/sizer.h>

wxLineChartPanel::wxLineChartPanel(wxWindow* parent)
    : wxPanel(parent)
{
    // Create the data for the line chart widget
    wxVector<wxString> labels;
    labels.push_back("January");
    labels.push_back("February");
    labels.push_back("March");
    labels.push_back("April");
    labels.push_back("May");
    labels.push_back("June");
    labels.push_back("July");
    wxLineChartData chartData(labels);

    // Add the first dataset
    wxVector<wxDouble> points1;
    points1.push_back(3);
    points1.push_back(-2.5);
    points1.push_back(-1.2);
    points1.push_back(3);
    points1.push_back(6);
    points1.push_back(5);
    points1.push_back(1);
    wxLineChartDataset::ptr dataset1(new wxLineChartDataset(
        "My First Dataset", wxColor(220, 220, 220),
        wxColor(255, 255, 255), wxColor(220, 220, 220, 0x33),
        points1));
    chartData.AddDataset(dataset1);

    // Add the second dataset
    wxVector<wxDouble> points2;
    points2.push_back(1);
    points2.push_back(-1.33);
    points2.push_back(2.5);
    points2.push_back(7);
    points2.push_back(3);
    points2.push_back(-1.8);
    points2.push_back(0.4);
    wxLineChartDataset::ptr dataset2(new wxLineChartDataset(
        "My Second Dataset", wxColor(151, 187, 205),
        wxColor(255, 255, 255), wxColor(151, 187, 205, 0x33),
        points2));
    chartData.AddDataset(dataset2);

    // Create the line chart widget from the constructed data
    m_lineChart = new wxLineChartCtrl(this, wxID_ANY, chartData,
        wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);

    // Set up the sizer for the panel
    wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
    sizer->Add(m_lineChart, 1, wxEXPAND);

    SetSizer(sizer);
}
<|endoftext|>


<commit_before><commit_msg>*** empty log message ***<commit_after><|endoftext|>


<commit_before>/*-------------FDTD.cpp-------------------------------------------------------//
*
*              Finite Difference Time Domain
*
* Purpose: To replicate the results of our invisible lense raytracer with
*          FDTD. Woo!
*
*   Notes: Most of this is coming from the following link:
*             http://www.eecs.wsu.edu/~schneidj/ufdtd/chap3.pdf
*             http://www.eecs.wsu.edu/~schneidj/ufdtd/chap8.pdf
*
*-----------------------------------------------------------------------------*/

#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>

static const size_t space = 200;

struct Bound{
    int x,y;
};

struct Loss{
    std::vector <double> EzH = std::vector<double>(space * space, 0),
                         EzE = std::vector<double>(space * space, 0),
                         HyE = std::vector<double>(space * space, 0),
                         HyH = std::vector<double>(space * space, 0),
                         HxE = std::vector<double>(space * space, 0),
                         HxH = std::vector<double>(space * space, 0);
};

struct Loss1d{
    std::vector <double> EzH = std::vector<double>(space, 0),
                         EzE = std::vector<double>(space, 0),
                         HyE = std::vector<double>(space, 0),
                         HyH = std::vector<double>(space, 0),
                         HxE = std::vector<double>(space, 0),
                         HxH = std::vector<double>(space, 0);
};

struct Field{
    std::vector <double> Hx = std::vector<double>(space * space, 0),
                         Hy = std::vector<double>(space * space, 0),
                         Ez = std::vector<double>(space * space, 0);
};

struct Field1d{
    std::vector <double> Hx = std::vector<double>(space, 0),
                         Hy = std::vector<double>(space, 0),
                         Ez = std::vector<double>(space, 0);
    int t;
};

#define EzH(i, j) EzH[(i) + (j) *  space]
#define EzE(i, j) EzE[(i) + (j) *  space]
#define HyH(i, j) HyH[(i) + (j) *  space]
#define HyE(i, j) HyE[(i) + (j) *  space]
#define HxH(i, j) HxH[(i) + (j) *  space]
#define HxE(i, j) HxE[(i) + (j) *  space]
#define Hx(i, j) Hx[(i) + (j) *  space]
#define Hy(i, j) Hy[(i) + (j) *  space]
#define Ez(i, j) Ez[(i) + (j) *  space]

void FDTD(Field EM, Field1d EM1d,
          const int final_time, const double eps, const int space,
          std::ofstream& output);

// Adding ricker solutuion
double ricker(int time, int loc, double Cour);

// 2 dimensional functions for E / H movement
Field Hupdate2d(Field EM, Loss lass, int t);
Field Eupdate2d(Field EM, Loss lass, int t);

// 1 dimensional update functions for E / H
Field1d Hupdate1d(Field1d EM1d, Loss lass, int t);
Field1d Eupdate1d(Field1d EM1d, Loss lass, int t);

// Creating loss
Loss createloss2d(Loss lass, double eps, double Cour, double loss);
Loss1d createloss1d(Loss1d lass1d, double eps, double Cour, double loss);

// TFSF boundaries
Field TFSF(Field EM, Loss lass, Field1d EM1d, Loss1d lass1d, double Cour);

/*----------------------------------------------------------------------------//
* MAIN
*-----------------------------------------------------------------------------*/

int main(){

    // defines output
    std::ofstream output("FDTD.dat", std::ofstream::out);

    int final_time = 1000;
    double eps = 377.0;

    // define initial E and H fields
    // std::vector<double> Ez(space, 0.0), Hy(space, 0.0);
    Field EM;
    Field1d EM1d;
    EM1d.t = 0;

    FDTD(EM, EM1d, final_time, eps, space, output);

}

/*----------------------------------------------------------------------------//
* SUBROUTINES
*-----------------------------------------------------------------------------*/

// This is the function we writs the bulk of the code in
void FDTD(Field EM, Field1d EM1d,
          const int final_time, const double eps, const int space,
          std::ofstream& output){

    // For magnetic field:
    // double offset = 0.00005;

    // for electric field:
    //double offset = 0.05;
    double loss = 0.0;
    double Cour = 1.0 / sqrt(2.0);

    Loss lass;
    lass  = createloss2d(lass, eps, Cour, loss);
    Loss1d lass1d;
    lass1d = createloss1d(lass1d, eps, Cour, loss);

    // Time looping
    for (int t = 0; t < final_time; t++){

        EM = Hupdate2d(EM, lass, t);
        //EM = TFSF(EM, lass, EM1d, lass1d, Cour);
        EM = Eupdate2d(EM,lass,t);
        EM.Ez(0,100) = ricker(t, 0, Cour);

        // Outputting to a file
        int check = 5;
        if (t % check == 0){
            for (int dx = 0; dx < space; dx = dx + check){
                for (int dy = 0; dy < space; dy = dy + check){
                    output << t << '\t' << dx <<'\t' << dy << '\t'
                           << EM.Ez(dx, dy) << '\t' << EM.Hy(dx, dy)
                           << '\t' << EM.Hx(dx, dy) << '\t' << '\n';
                    //output << Ez(dx,dy) + (t * offset) << '\n';
                    //output << Hy[dx] + (t * offset) << '\n';
                }
            }

            output << '\n' << '\n';
        }

    }
}

// Adding the ricker solution
double ricker(int time, int loc, double Cour){
    double Ricky;
    double temp_const = 3.14159*((Cour*(double)time - (double)loc)/20.0 - 1.0);
    temp_const = temp_const * temp_const;
    Ricky = (1.0 - 2.0 * temp_const) * exp(-temp_const);
    return Ricky;

}

// 2 dimensional functions for E / H movement
Field Hupdate2d(Field EM, Loss lass, int t){
    // update magnetic field, x direction
    for (size_t dx = 0; dx < space; dx++){
        for (size_t dy = 0; dy < space - 1; dy++){
           EM.Hx(dx,dy) = lass.HxH(dx,dy) * EM.Hx(dx, dy)
                       - lass.HxE(dx,dy) * (EM.Ez(dx,dy + 1)
                                            - EM.Ez(dx,dy));
        }
    }

    // update magnetic field, y direction
    for (size_t dx = 0; dx < space - 1; dx++){
        for (size_t dy = 0; dy < space; dy++){
           EM.Hy(dx,dy) = lass.HyH(dx,dy) * EM.Hy(dx,dy)
                      + lass.HyE(dx,dy) * (EM.Ez(dx + 1,dy)
                                            - EM.Ez(dx,dy));
        }
    }

    return EM;

}

Field Eupdate2d(Field EM, Loss lass, int t){
    // update electric field
    for (size_t dx = 1; dx < space - 1; dx++){
        for (size_t dy = 1; dy < space - 1; dy++){
           EM.Ez(dx,dy) = lass.EzE(dx,dy) * EM.Ez(dx,dy)
                       + lass.EzH(dx,dy) * ((EM.Hy(dx, dy)
                                         - EM.Hy(dx - 1, dy))
                                         - (EM.Hx(dx,dy)
                                         - EM.Hx(dx, dy - 1)));
        }
    }
    return EM;
}

// 1 dimensional update functions for E / H
Field1d Hupdate1d(Field1d EM1d, Loss1d lass1d, int t){
    // update magnetic field, y direction
    for (size_t dx = 0; dx < space - 1; dx++){
        EM1d.Hy[dx] = lass1d.HyH[dx] * EM1d.Hy[dx]
                  + lass1d.HyE[dx] * (EM1d.Ez[dx + 1] - EM1d.Ez[dx]);
    }

    return EM1d;
}

Field1d Eupdate1d(Field1d EM1d, Loss1d lass1d, int t){
    // update electric field, y direction
    for (size_t dx = 1; dx < space - 1; dx++){
        EM1d.Ez[dx] = lass1d.EzE[dx] * EM1d.Ez[dx]
                  + lass1d.EzH[dx] * (EM1d.Hy[dx] - EM1d.Hy[dx - 1]);
    }

    return EM1d;

}

// Creating loss
Loss createloss2d(Loss lass, double eps, double Cour, double loss){
    for (size_t dx = 0; dx < space; dx++){
        for (size_t dy = 0; dy < space; dy++){
            //if (dx > 100 && dx < 150){
/*
                lass.EzH(dx, dy) = Cour * eps;
                lass.EzE(dx, dy) = 1.0;
                lass.HyH(dx, dy) = 1.0;
                lass.HyE(dx, dy) = Cour / eps;
                lass.HxE(dx, dy) = Cour / eps;
                lass.HxH(dx, dy) = 1.0;
*/

                lass.EzH(dx, dy) =  eps / 9.0 /(1.0 - loss);
                lass.EzE(dx, dy) = (1.0 - loss) / (1.0 + loss);
                lass.HyH(dx, dy) = (1.0 - loss) / (1.0 + loss);
                lass.HyE(dx, dy) = (1.0 / eps) / (1.0 + loss);
                lass.HxE(dx, dy) = (1.0 / eps) / (1.0 + loss);
                lass.HxH(dx, dy) = (1.0 - loss) / (1.0 + loss);
            //}
            /*
            else{
                lass.EzH(dx, dy) =  Cour * eps;
                lass.EzE(dx, dy) = 1.0;
                lass.HyH(dx, dy) = 1.0;
                lass.HyE(dx, dy) = Cour / eps;
                lass.HxE(dx, dy) = Cour / eps;
                lass.HxH(dx, dy) = 1.0;

                lass.EzH(dx, dy) =  eps;
                lass.EzE(dx, dy) = 1.0;
                lass.HyH(dx, dy) = 1.0;
                lass.HyE(dx, dy) = (1.0 / eps);
                lass.HxE(dx, dy) = (1.0 / eps);
                lass.HxH(dx, dy) = 1.0;

            }
            */
        }
    }

    return lass;
}
Loss1d createloss1d(Loss1d lass1d, double eps, double Cour, double loss){
    for (size_t dx = 0; dx < space; dx++){
            //if (dx > 100 && dx < 150){
/*
            lass1d.EzH[dx ]= Cour * eps;
            lass1d.EzE[dx] = 1.0;
            lass1d.HyH[dx] = 1.0;
            lass1d.HyE[dx] = Cour / eps;
*/

            lass1d.EzH[dx] =  eps / 9.0 /(1.0 - loss);
            lass1d.EzE[dx] = (1.0 - loss) / (1.0 + loss);
            lass1d.HyH[dx] = (1.0 - loss) / (1.0 + loss);
            lass1d.HyE[dx] = (1.0 / eps) / (1.0 + loss);
        //}
        /*
        else{
            lass1d.EzH[dx] =  Cour * eps;
            lass1d.EzE[dx] = 1.0;
            lass1d.HyH[dx] = 1.0;
            lass1d.HyE[dx] = Cour / eps;

            lass1d.EzH[dx] =  eps;
            lass1d.EzE[dx] = 1.0;
            lass1d.HyH[dx] = 1.0;
            lass1d.HyE[dx] = (1.0 / eps);

        }
        */
    }

    return lass1d;

}

// TFSF boundaries
Field TFSF(Field EM, Loss lass, Field1d EM1d, Loss1d lass1d, double Cour){
    // TFSF boundary
    Bound first, last;
    first.x = 50; last.x = 150;
    first.y = 50; last.y = 150;

    // Updating along left edge
    int dx = first.x - 1;
    for (int dy = first.y; dy <= last.y; dy++){
        EM.Hy(dx,dy) -= lass.HyE(dx, dy) * EM1d.Ez(dx+1, dy);
    }

    // Update along right edge!
    dx = last.x;
    for (int dy = first.y; dy <= last.y; dy++){
        EM.Hy(dx,dy) += lass.HyE(dx, dy) * EM1d.Ez(dx, dy);
    }

    // Update along bot
    int dy = first.y - 1;
    for (int dx = first.x; dx <= last.x; dx++){
        EM.Hx(dx,dy) += lass.HxE(dx, dy) * EM1d.Ez(dx, dy);
    }

    // Updating along top
    dy = last.y;
    for (int dx = first.x; dx <= last.x; dx++){
        EM.Hx(dx,dy) -= lass.HxE(dx, dy) * EM1d.Ez(dx, dy);
    }

    // Insert 1d grid stuff here. Update magnetic and electric field
    Hupdate1d(EM1d, lass1d, EM1d.t);
    Eupdate1d(EM1d, lass1d, EM1d.t);
    EM1d.Ez[0] = ricker(EM1d.t,0, Cour);
    EM1d.t++;

    // Check mag instead of ricker.
    // Updating Ez along left
    dx = first.x;
    for (int dy = first.y; dy <= last.y; dy++){
        EM.Ez(dx, dy) -= lass.EzH(dx, dy) * EM1d.Hy(dx - 1, dy);
    }

    // Update along right
    dx = last.x;
    for (int dy = first.y; dy <= last.y; dy++){
        EM.Ez(dx, dy) += lass.EzH(dx, dy) * EM1d.Hy(dx, dy);
    }

    return EM;

}
<commit_msg>everything but the TFSF in 2d works<commit_after>/*-------------FDTD.cpp-------------------------------------------------------//
*
*              Finite Difference Time Domain
*
* Purpose: To replicate the results of our invisible lense raytracer with
*          FDTD. Woo!
*
*   Notes: Most of this is coming from the following link:
*             http://www.eecs.wsu.edu/~schneidj/ufdtd/chap3.pdf
*             http://www.eecs.wsu.edu/~schneidj/ufdtd/chap8.pdf
*
*-----------------------------------------------------------------------------*/

#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>

static const size_t space = 200;

struct Bound{
    int x,y;
};

struct Loss{
    std::vector <double> EzH = std::vector<double>(space * space, 0),
                         EzE = std::vector<double>(space * space, 0),
                         HyE = std::vector<double>(space * space, 0),
                         HyH = std::vector<double>(space * space, 0),
                         HxE = std::vector<double>(space * space, 0),
                         HxH = std::vector<double>(space * space, 0);
};

struct Loss1d{
    std::vector <double> EzH = std::vector<double>(space, 0),
                         EzE = std::vector<double>(space, 0),
                         HyE = std::vector<double>(space, 0),
                         HyH = std::vector<double>(space, 0),
                         HxE = std::vector<double>(space, 0),
                         HxH = std::vector<double>(space, 0);
};

struct Field{
    std::vector <double> Hx = std::vector<double>(space * space, 0),
                         Hy = std::vector<double>(space * space, 0),
                         Ez = std::vector<double>(space * space, 0);
    int t;
};

struct Field1d{
    std::vector <double> Hx = std::vector<double>(space, 0),
                         Hy = std::vector<double>(space, 0),
                         Ez = std::vector<double>(space, 0);
};

#define EzH(i, j) EzH[(i) + (j) *  space]
#define EzE(i, j) EzE[(i) + (j) *  space]
#define HyH(i, j) HyH[(i) + (j) *  space]
#define HyE(i, j) HyE[(i) + (j) *  space]
#define HxH(i, j) HxH[(i) + (j) *  space]
#define HxE(i, j) HxE[(i) + (j) *  space]
#define Hx(i, j) Hx[(i) + (j) *  space]
#define Hy(i, j) Hy[(i) + (j) *  space]
#define Ez(i, j) Ez[(i) + (j) *  space]

void FDTD(Field EM, Field1d EM1d,
          const int final_time, const double eps, const int space,
          std::ofstream& output);

// Adding ricker solutuion
double ricker(int time, int loc, double Cour);

// 2 dimensional functions for E / H movement
Field Hupdate2d(Field EM, Loss lass, int t);
Field Eupdate2d(Field EM, Loss lass, int t);

// 1 dimensional update functions for E / H
Field1d Hupdate1d(Field1d EM1d, Loss lass, int t);
Field1d Eupdate1d(Field1d EM1d, Loss lass, int t);

// Creating loss
Loss createloss2d(Loss lass, double eps, double Cour, double loss);
Loss1d createloss1d(Loss1d lass1d, double eps, double Cour, double loss);

// TFSF boundaries
Field TFSF(Field EM, Loss lass, Field1d EM1d, Loss1d lass1d, double Cour);

/*----------------------------------------------------------------------------//
* MAIN
*-----------------------------------------------------------------------------*/

int main(){

    // defines output
    std::ofstream output("FDTD.dat", std::ofstream::out);

    int final_time = 1000;
    double eps = 377.0;

    // define initial E and H fields
    // std::vector<double> Ez(space, 0.0), Hy(space, 0.0);
    Field EM;
    Field1d EM1d;
    EM.t = 0;

    FDTD(EM, EM1d, final_time, eps, space, output);

}

/*----------------------------------------------------------------------------//
* SUBROUTINES
*-----------------------------------------------------------------------------*/

// This is the function we writs the bulk of the code in
void FDTD(Field EM, Field1d EM1d,
          const int final_time, const double eps, const int space,
          std::ofstream& output){

    // For magnetic field:
    // double offset = 0.00005;

    // for electric field:
    //double offset = 0.05;
    double loss = 0.0;
    double Cour = 1.0 / sqrt(2.0);

    Loss lass;
    lass  = createloss2d(lass, eps, Cour, loss);
    Loss1d lass1d;
    lass1d = createloss1d(lass1d, eps, Cour, loss);

    // Time looping
    for (int t = 0; t < final_time; t++){

        EM = Hupdate2d(EM, lass, t);
        EM = TFSF(EM, lass, EM1d, lass1d, Cour);
        EM = Eupdate2d(EM,lass,t);
        //EM.Ez(0,100) = ricker(t, 0, Cour);

        // Outputting to a file
        int check = 5;
        if (t % check == 0){
            for (int dx = 0; dx < space; dx = dx + check){
                for (int dy = 0; dy < space; dy = dy + check){
                    output << t << '\t' << dx <<'\t' << dy << '\t'
                           << EM.Ez(dx, dy) << '\t' << EM.Hy(dx, dy)
                           << '\t' << EM.Hx(dx, dy) << '\t' << '\n';
                    //output << Ez(dx,dy) + (t * offset) << '\n';
                    //output << Hy[dx] + (t * offset) << '\n';
                }
            }

            output << '\n' << '\n';
        }

    }
}

// Adding the ricker solution
double ricker(int time, int loc, double Cour){
    double Ricky;
    double temp_const = 3.14159*((Cour*(double)time - (double)loc)/20.0 - 1.0);
    temp_const = temp_const * temp_const;
    Ricky = (1.0 - 2.0 * temp_const) * exp(-temp_const);
    return Ricky;

}

// 2 dimensional functions for E / H movement
Field Hupdate2d(Field EM, Loss lass, int t){
    // update magnetic field, x direction
    for (size_t dx = 0; dx < space; dx++){
        for (size_t dy = 0; dy < space - 1; dy++){
           EM.Hx(dx,dy) = lass.HxH(dx,dy) * EM.Hx(dx, dy)
                       - lass.HxE(dx,dy) * (EM.Ez(dx,dy + 1)
                                            - EM.Ez(dx,dy));
        }
    }

    // update magnetic field, y direction
    for (size_t dx = 0; dx < space - 1; dx++){
        for (size_t dy = 0; dy < space; dy++){
           EM.Hy(dx,dy) = lass.HyH(dx,dy) * EM.Hy(dx,dy)
                      + lass.HyE(dx,dy) * (EM.Ez(dx + 1,dy)
                                            - EM.Ez(dx,dy));
        }
    }

    return EM;

}

Field Eupdate2d(Field EM, Loss lass, int t){
    // update electric field
    for (size_t dx = 1; dx < space - 1; dx++){
        for (size_t dy = 1; dy < space - 1; dy++){
           EM.Ez(dx,dy) = lass.EzE(dx,dy) * EM.Ez(dx,dy)
                       + lass.EzH(dx,dy) * ((EM.Hy(dx, dy)
                                         - EM.Hy(dx - 1, dy))
                                         - (EM.Hx(dx,dy)
                                         - EM.Hx(dx, dy - 1)));
        }
    }
    return EM;
}

// 1 dimensional update functions for E / H
Field1d Hupdate1d(Field1d EM1d, Loss1d lass1d, int t){
    // update magnetic field, y direction
    for (size_t dx = 0; dx < space - 1; dx++){
        EM1d.Hy[dx] = lass1d.HyH[dx] * EM1d.Hy[dx]
                  + lass1d.HyE[dx] * (EM1d.Ez[dx + 1] - EM1d.Ez[dx]);
    }

    return EM1d;
}

Field1d Eupdate1d(Field1d EM1d, Loss1d lass1d, int t){
    // update electric field, y direction
    for (size_t dx = 1; dx < space - 1; dx++){
        EM1d.Ez[dx] = lass1d.EzE[dx] * EM1d.Ez[dx]
                  + lass1d.EzH[dx] * (EM1d.Hy[dx] - EM1d.Hy[dx - 1]);
    }

    return EM1d;

}

// Creating loss
Loss createloss2d(Loss lass, double eps, double Cour, double loss){
    for (size_t dx = 0; dx < space; dx++){
        for (size_t dy = 0; dy < space; dy++){
            //if (dx > 100 && dx < 150){
/*
                lass.EzH(dx, dy) = Cour * eps;
                lass.EzE(dx, dy) = 1.0;
                lass.HyH(dx, dy) = 1.0;
                lass.HyE(dx, dy) = Cour / eps;
                lass.HxE(dx, dy) = Cour / eps;
                lass.HxH(dx, dy) = 1.0;
*/

                lass.EzH(dx, dy) =  eps / 9.0 /(1.0 - loss);
                lass.EzE(dx, dy) = (1.0 - loss) / (1.0 + loss);
                lass.HyH(dx, dy) = (1.0 - loss) / (1.0 + loss);
                lass.HyE(dx, dy) = (1.0 / eps) / (1.0 + loss);
                lass.HxE(dx, dy) = (1.0 / eps) / (1.0 + loss);
                lass.HxH(dx, dy) = (1.0 - loss) / (1.0 + loss);
            //}
            /*
            else{
                lass.EzH(dx, dy) =  Cour * eps;
                lass.EzE(dx, dy) = 1.0;
                lass.HyH(dx, dy) = 1.0;
                lass.HyE(dx, dy) = Cour / eps;
                lass.HxE(dx, dy) = Cour / eps;
                lass.HxH(dx, dy) = 1.0;

                lass.EzH(dx, dy) =  eps;
                lass.EzE(dx, dy) = 1.0;
                lass.HyH(dx, dy) = 1.0;
                lass.HyE(dx, dy) = (1.0 / eps);
                lass.HxE(dx, dy) = (1.0 / eps);
                lass.HxH(dx, dy) = 1.0;

            }
            */
        }
    }

    return lass;
}
Loss1d createloss1d(Loss1d lass1d, double eps, double Cour, double loss){
    for (size_t dx = 0; dx < space; dx++){
            //if (dx > 100 && dx < 150){

            lass1d.EzH[dx ]= Cour * eps;
            lass1d.EzE[dx] = 1.0;
            lass1d.HyH[dx] = 1.0;
            lass1d.HyE[dx] = Cour / eps;
/*

            lass1d.EzH[dx] =  eps / 9.0 /(1.0 - loss);
            lass1d.EzE[dx] = (1.0 - loss) / (1.0 + loss);
            lass1d.HyH[dx] = (1.0 - loss) / (1.0 + loss);
*/
            lass1d.HyE[dx] = (1.0 / eps) / (1.0 + loss);
        //}
        /*
        else{
            lass1d.EzH[dx] =  Cour * eps;
            lass1d.EzE[dx] = 1.0;
            lass1d.HyH[dx] = 1.0;
            lass1d.HyE[dx] = Cour / eps;

            lass1d.EzH[dx] =  eps;
            lass1d.EzE[dx] = 1.0;
            lass1d.HyH[dx] = 1.0;
            lass1d.HyE[dx] = (1.0 / eps);

        }
        */
    }

    return lass1d;

}

// TFSF boundaries
Field TFSF(Field EM, Loss lass, Field1d EM1d, Loss1d lass1d, double Cour){
    // TFSF boundary
    Bound first, last;
    first.x = 50; last.x = 150;
    first.y = 50; last.y = 150;

    // Updating along left edge
    int dx = first.x - 1;
    for (int dy = first.y; dy <= last.y; dy++){
        EM.Hy(dx,dy) -= lass.HyE(dx, dy) * EM1d.Ez[dx+1];
    }

    // Update along right edge!
    dx = last.x;
    for (int dy = first.y; dy <= last.y; dy++){
        EM.Hy(dx,dy) += lass.HyE(dx, dy) * EM1d.Ez[dx];
    }

    // Update along bot
    int dy = first.y - 1;
    for (int dx = first.x; dx <= last.x; dx++){
        EM.Hx(dx,dy) += lass.HxE(dx, dy) * EM1d.Ez[dx];
    }

    // Updating along top
    dy = last.y;
    for (int dx = first.x; dx <= last.x; dx++){
        EM.Hx(dx,dy) -= lass.HxE(dx, dy) * EM1d.Ez[dx];
    }

    // Insert 1d grid stuff here. Update magnetic and electric field
    Hupdate1d(EM1d, lass1d, EM.t);
    Eupdate1d(EM1d, lass1d, EM.t);
    EM1d.Ez[100] = ricker(EM.t,0, Cour);
    EM.t++;
    std::cout << EM.t << '\n';

    // Check mag instead of ricker.
    // Updating Ez along left
    dx = first.x;
    for (int dy = first.y; dy <= last.y; dy++){
        EM.Ez(dx, dy) -= lass.EzH(dx, dy) * EM1d.Hy[dx - 1];
    }

    // Update along right
    dx = last.x;
    for (int dy = first.y; dy <= last.y; dy++){
        EM.Ez(dx, dy) += lass.EzH(dx, dy) * EM1d.Hy[dx];
    }

    return EM;

}
<|endoftext|>


<commit_before>#include <gtest/gtest.h>
#include <shogun/io/SGIO.h>
#include <stdexcept>

#include <spdlog/sinks/base_sink.h>
#include <spdlog/version.h>

#include <mutex>

using namespace shogun;
using namespace shogun::io;

TEST(SGIO, exception)
{
	EXPECT_THROW(error("Error"), ShogunException);
	EXPECT_THROW(error<std::invalid_argument>("Error"), std::invalid_argument);
	EXPECT_THROW(require(0, "Error"), ShogunException);
	EXPECT_THROW(
	    require<std::invalid_argument>(0, "Error"), std::invalid_argument);
}

// same as spdlog's ostream_sink, but with public message counter
class StreamSink : public spdlog::sinks::base_sink<std::mutex>
{
public:
	explicit StreamSink(std::ostream& os) : counter(0), ostream_(os)
	{
	}

	std::atomic<int32_t> counter;

protected:
	void sink_it_(const spdlog::details::log_msg& msg) override
	{
#if ((SPDLOG_VER_MAJOR == 1) && (SPDLOG_VER_MINOR < 4))
		fmt::memory_buffer formatted;
		sink::formatter_->format(msg, formatted);
#else
		spdlog::memory_buf_t formatted;
		formatter_->format(msg, formatted);
#endif
		ostream_.write(
		    formatted.data(), static_cast<std::streamsize>(formatted.size()));
		ostream_.flush();
		counter++;
	}

	void flush_() override
	{
		ostream_.flush();
	}

	std::ostream& ostream_;
};

bool ends_with(const std::string& long_str, const std::string& short_str)
{
	return long_str.compare(
	           long_str.length() - short_str.length(), short_str.length(),
	           short_str) == 0 ||
	       long_str.compare(
	           long_str.length() - short_str.length() - 1, short_str.length(),
	           short_str) == 0;
}

TEST(SGIO, custom_stderr_sink)
{
	const std::string stderr_log = "Testing that \'error\' writes to stderr.";

	std::ostringstream stderr_stream;
	auto stderr_sink = std::make_shared<StreamSink>(stderr_stream);
	env()->io()->redirect_stderr(stderr_sink);

	EXPECT_THROW(error("{}", stderr_log), ShogunException);

	// busy waiting since it's an async logger (probably not the best way)
	while (stderr_sink->counter.load(std::memory_order_relaxed) != 1)
		;

	std::string stderr_str = stderr_stream.str();

	// check that stderr_str ends with stderr_log
	EXPECT_TRUE(ends_with(stderr_str, stderr_log));

	env()->io()->init_default_sink();
}

TEST(SGIO, custom_stdout_sink)
{
	const std::string stdout_log1 = "Testing unformatted ";
	const std::string stdout_log2 = "print function with custom stdout sink.\n";

	std::ostringstream stdout_stream;
	auto stdout_sink = std::make_shared<StreamSink>(stdout_stream);
	env()->io()->redirect_stdout(stdout_sink);

	print("{}", stdout_log1);
	print("{}", stdout_log2);

	// busy waiting since it's an async logger (probably not the best way)
	while (stdout_sink->counter.load(std::memory_order_relaxed) != 2)
		;

	std::string stdout_str = stdout_stream.str();

	EXPECT_EQ(stdout_stream.str(), stdout_log1 + stdout_log2);

	env()->io()->init_default_sink();
}

TEST(SGIO, loglevels_redirection)
{
	const std::string test_msg = "TEST\n";

	std::ostringstream stdout_stream;
	std::ostringstream stderr_stream;
	auto stdout_sink = std::make_shared<StreamSink>(stdout_stream);
	auto stderr_sink = std::make_shared<StreamSink>(stderr_stream);
	env()->io()->redirect_stdout(stdout_sink);
	env()->io()->redirect_stderr(stderr_sink);

	EMessageType loglevels[] = {MSG_TRACE,    MSG_DEBUG, MSG_INFO,
	                            MSG_WARN,       MSG_ERROR, MSG_CRITICAL,
	                            MSG_MESSAGEONLY};
	env()->io()->set_loglevel(loglevels[0]);

	for (EMessageType loglevel : loglevels)
	{
		env()->io()->message(loglevel, {}, "{}", test_msg);
		while (stdout_sink->counter.load(std::memory_order_relaxed) == 0 &&
		       stderr_sink->counter.load(std::memory_order_relaxed) == 0)
			;

		if (loglevel != MSG_ERROR)
		{
			EXPECT_EQ(stdout_sink->counter.load(std::memory_order_relaxed), 1);
			EXPECT_TRUE(ends_with(stdout_stream.str(), test_msg));
		}
		else
		{
			EXPECT_EQ(stderr_sink->counter.load(std::memory_order_relaxed), 1);
			EXPECT_TRUE(ends_with(stderr_stream.str(), test_msg));
		}

		stdout_sink->counter = 0;
		stderr_sink->counter = 0;
	}

	env()->io()->init_default_sink();
}<commit_msg>Fix SGIO unittests<commit_after>#include <gtest/gtest.h>
#include <shogun/io/SGIO.h>
#include <stdexcept>

#include <spdlog/sinks/base_sink.h>
#include <spdlog/version.h>

#include <mutex>

using namespace shogun;
using namespace shogun::io;

std::mutex& get_io_test_mutex()
{
	static std::mutex test_mutex;
	return test_mutex;
}

TEST(SGIO, exception)
{
	std::lock_guard<std::mutex> lock(get_io_test_mutex());
	EXPECT_THROW(error("Error"), ShogunException);
	EXPECT_THROW(error<std::invalid_argument>("Error"), std::invalid_argument);
	EXPECT_THROW(require(0, "Error"), ShogunException);
	EXPECT_THROW(
	    require<std::invalid_argument>(0, "Error"), std::invalid_argument);
}

// same as spdlog's ostream_sink, but with public message counter
class StreamSink : public spdlog::sinks::base_sink<std::mutex>
{
public:
	static constexpr char SIGNATURE[] = "stream_sink_signature";

	explicit StreamSink(std::ostream& os) : counter(0), ostream_(os)
	{
	}
	static std::string sign_msg(const char* msg)
	{
		std::string key_msg(SIGNATURE);
		key_msg.append(msg);
		return key_msg;
	}
	int32_t get_counter()
	{
		std::lock_guard<std::mutex> lock(mutex_);
		return counter;
	}
	void set_counter(int32_t val)
	{
		std::lock_guard<std::mutex> lock(mutex_);
		counter = val;
	}

protected:
	bool check_msg_signed(const char* msg)
	{
		using namespace std;
		// end(SIGNATURE) - 1 not to compare null terminator
		return equal(begin(SIGNATURE), end(SIGNATURE) - 1, msg);
	}
	// all methods that call sink_it_ are guarded by `mutex_`
	void sink_it_(const spdlog::details::log_msg& msg) override
	{
		if (!check_msg_signed(msg.payload.data()))
			return;
#if ((SPDLOG_VER_MAJOR == 1) && (SPDLOG_VER_MINOR < 4))
		fmt::memory_buffer formatted;
		sink::formatter_->format(msg, formatted);
#else
		spdlog::memory_buf_t formatted;
		formatter_->format(msg, formatted);
#endif
		ostream_.write(
		    formatted.data(), static_cast<std::streamsize>(formatted.size()));
		ostream_.flush();
		counter++;
	}

	void flush_() override
	{
		ostream_.flush();
	}

private:
	int32_t counter;
	std::ostream& ostream_;
};

bool ends_with(const std::string& long_str, const std::string& short_str)
{
	return long_str.compare(
	           long_str.length() - short_str.length(), short_str.length(),
	           short_str) == 0 ||
	       long_str.compare(
	           long_str.length() - short_str.length() - 1, short_str.length(),
	           short_str) == 0;
}

TEST(SGIO, custom_stderr_sink)
{
	std::lock_guard<std::mutex> lock(get_io_test_mutex());
	const std::string stderr_log =
	    StreamSink::sign_msg("Testing that \'error\' writes to stderr.");

	std::ostringstream stderr_stream;
	auto stderr_sink = std::make_shared<StreamSink>(stderr_stream);
	env()->io()->redirect_stderr(stderr_sink);

	EXPECT_THROW(error("{}", stderr_log), ShogunException);

	// busy waiting since it's an async logger (probably not the best way)
	while (stderr_sink->get_counter() != 1)
		;

	std::string stderr_str = stderr_stream.str();

	// check that stderr_str ends with stderr_log
	EXPECT_TRUE(ends_with(stderr_str, stderr_log));

	env()->io()->init_default_sink();
}

TEST(SGIO, custom_stdout_sink)
{
	std::lock_guard<std::mutex> lock(get_io_test_mutex());
	const std::string stdout_log1 =
	    StreamSink::sign_msg("Testing unformatted ");
	const std::string stdout_log2 =
	    StreamSink::sign_msg("print function with custom stdout sink.\n");

	std::ostringstream stdout_stream;
	auto stdout_sink = std::make_shared<StreamSink>(stdout_stream);
	env()->io()->redirect_stdout(stdout_sink);

	print("{}", stdout_log1);
	print("{}", stdout_log2);

	// busy waiting since it's an async logger (probably not the best way)
	while (stdout_sink->get_counter() != 2)
		;

	std::string stdout_str = stdout_stream.str();

	EXPECT_EQ(stdout_stream.str(), stdout_log1 + stdout_log2);

	env()->io()->init_default_sink();
}

TEST(SGIO, loglevels_redirection)
{
	std::lock_guard<std::mutex> lock(get_io_test_mutex());
	const std::string test_msg = StreamSink::sign_msg("TEST\n");

	std::ostringstream stdout_stream;
	std::ostringstream stderr_stream;
	auto stdout_sink = std::make_shared<StreamSink>(stdout_stream);
	auto stderr_sink = std::make_shared<StreamSink>(stderr_stream);
	env()->io()->redirect_stdout(stdout_sink);
	env()->io()->redirect_stderr(stderr_sink);

	EMessageType loglevels[] = {MSG_TRACE,      MSG_DEBUG, MSG_INFO,
	                            MSG_WARN,       MSG_ERROR, MSG_CRITICAL,
	                            MSG_MESSAGEONLY};
	env()->io()->set_loglevel(loglevels[0]);

	for (EMessageType loglevel : loglevels)
	{
		env()->io()->message(loglevel, {}, "{}", test_msg);
		while (stdout_sink->get_counter() == 0 &&
		       stderr_sink->get_counter() == 0)
			;

		if (loglevel != MSG_ERROR)
		{
			EXPECT_EQ(stdout_sink->get_counter(), 1);
			EXPECT_TRUE(ends_with(stdout_stream.str(), test_msg));
		}
		else
		{
			EXPECT_EQ(stderr_sink->get_counter(), 1);
			EXPECT_TRUE(ends_with(stderr_stream.str(), test_msg));
		}

		stdout_sink->set_counter(0);
		stderr_sink->set_counter(0);
	}

	env()->io()->init_default_sink();
}<|endoftext|>