<commit_before>
/*!
	\file

	\author Igor Mironchik (igor.mironchik at gmail dot com).

	Copyright (c) 2013-2017 Igor Mironchik

	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.
*/

/*!
	\file

	\warning If you are including this file you should include command.hpp too.
*/

#ifndef ARGS__GROUPS_HPP__INCLUDED
#define ARGS__GROUPS_HPP__INCLUDED

// Args include.
#include "group_iface.hpp"
#include "types.hpp"

// C++ include.
#include <algorithm>

namespace Args {

//
// OnlyOneGroup
//

//! Group of args where only one argument can be defined.
class OnlyOneGroup final
	:	public GroupIface
{
public:
	template< typename T >
	explicit OnlyOneGroup( T && name,
		bool required = false )
		:	GroupIface( std::forward< T > ( name ), required )
	{
	}

	virtual ~OnlyOneGroup()
	{
	}

	//! \return Is this argument defined?
	bool isDefined() const override
	{
		for( const auto & arg : children() )
		{
			if( arg->isDefined() )
				return true;
		}

		return false;
	}

protected:
	/*!
		Check correctness of the argument before parsing.

		Implementation of this method must add his flag
		and name to the flags and names.
	*/
	void checkCorrectnessBeforeParsing(
		//! All known flags.
		StringList & flags,
		//! All known names.
		StringList & names ) const override
	{
		GroupIface::checkCorrectnessBeforeParsing( flags, names );

		for( const auto & arg : children() )
		{
			if( arg->isRequired() )
				throw BaseException( String( SL( "Required argument \"" ) ) +
					arg->name() +
					SL( "\" is not allowed to be in OnlyOne group \"" ) +
					name() + SL( "\"." ) );
		}
	}

	//! Check correctness of the argument after parsing.
	void checkCorrectnessAfterParsing() const override
	{
		GroupIface::checkCorrectnessAfterParsing();

		ArgIface * defined = nullptr;

		for( const auto & arg : children() )
		{
			if( arg->isDefined() )
			{
				if( defined )
					throw BaseException( String( SL( "Only one argument can "
						"be defined in OnlyOne group \"" ) ) + name() + SL( "\". " ) +
						SL( "Whereas defined \"" ) + defined->name() +
						SL( "\" and \"" ) + arg->name() + SL( "\"." ) );
				else
					defined = arg;
			}
		}
	}
}; // class OnlyOneGroup

//
// AllOfGroup
//

//! Group of args where all arguments should be defined.
class AllOfGroup final
	:	public GroupIface
{
public:
	template< typename T >
	explicit AllOfGroup( T && name,
		bool required = false )
		:	GroupIface( std::forward< T > ( name ), required )
	{
	}

	virtual ~AllOfGroup()
	{
	}

	//! \return Is this argument defined?
	bool isDefined() const override
	{
		for( const auto & arg : children() )
		{
			if( !arg->isDefined() )
				return false;
		}

		return true;
	}

protected:
	/*!
		Check correctness of the argument before parsing.

		Implementation of this method must add his flag
		and name to the flags and names.
	*/
	void checkCorrectnessBeforeParsing(
		//! All known flags.
		StringList & flags,
		//! All known names.
		StringList & names ) const override
	{
		GroupIface::checkCorrectnessBeforeParsing( flags, names );

		for( const auto & arg : children() )
		{
			if( arg->isRequired() )
				throw BaseException( String( SL( "Required argument \"" ) ) +
					arg->name() +
					SL( "\" is not allowed to " ) +
					SL( "be in AllOf group \"" ) + name() + SL( "\"." ) );
		}
	}

	//! Check correctness of the argument after parsing.
	void checkCorrectnessAfterParsing() const override
	{
		GroupIface::checkCorrectnessAfterParsing();

		bool defined = false;

		const bool all = std::all_of( children().cbegin(), children().cend(),
			[ & ] ( const auto & arg )
			{
				if( arg->isDefined() )
					defined = true;

				return arg->isDefined();
			} );

		if( defined && !all )
			throw BaseException( String( SL( "All arguments in "
				"AllOf group \"" ) ) + name() + SL( "\" should be defined." ) );
	}
}; // class AllOfGroup

//
// AtLeastOneGroup
//

//! Group of args where at least one argument should be defined.
class AtLeastOneGroup final
	:	public GroupIface
{
public:
	template< typename T >
	explicit AtLeastOneGroup( T && name,
		bool required = false )
		:	GroupIface( std::forward< T > ( name ), required )
	{
	}

	virtual ~AtLeastOneGroup()
	{
	}

	//! \return Is this argument defined?
	bool isDefined() const override
	{
		for( const auto & arg : children() )
		{
			if( arg->isDefined() )
				return true;
		}

		return false;
	}

protected:
	/*!
		Check correctness of the argument before parsing.

		Implementation of this method must add his flag
		and name to the flags and names.
	*/
	void checkCorrectnessBeforeParsing(
		//! All known flags.
		StringList & flags,
		//! All known names.
		StringList & names ) const override
	{
		GroupIface::checkCorrectnessBeforeParsing( flags, names );

		for( const auto & arg : children() )
		{
			if( arg->isRequired() )
				throw BaseException( String( SL( "Required argument \"" ) ) +
					arg->name() +
					SL( "\" is not allowed to " ) +
					SL( "be in AtLeastOne group \"" ) + name() + SL( "\"." ) );
		}
	}
}; // class AtLeastOneGroup

} /* namespace Args */

#endif // ARGS__GROUPS_HPP__INCLUDED
<commit_msg>Replaced some code with std algorithms.<commit_after>
/*!
	\file

	\author Igor Mironchik (igor.mironchik at gmail dot com).

	Copyright (c) 2013-2017 Igor Mironchik

	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.
*/

/*!
	\file

	\warning If you are including this file you should include command.hpp too.
*/

#ifndef ARGS__GROUPS_HPP__INCLUDED
#define ARGS__GROUPS_HPP__INCLUDED

// Args include.
#include "group_iface.hpp"
#include "types.hpp"

// C++ include.
#include <algorithm>

namespace Args {

//
// OnlyOneGroup
//

//! Group of args where only one argument can be defined.
class OnlyOneGroup final
	:	public GroupIface
{
public:
	template< typename T >
	explicit OnlyOneGroup( T && name,
		bool required = false )
		:	GroupIface( std::forward< T > ( name ), required )
	{
	}

	virtual ~OnlyOneGroup()
	{
	}

	//! \return Is this argument defined?
	bool isDefined() const override
	{
		return std::any_of( children().cbegin(), children().cend(),
			[] ( const auto & arg ) { return arg->isDefined(); } );
	}

protected:
	/*!
		Check correctness of the argument before parsing.

		Implementation of this method must add his flag
		and name to the flags and names.
	*/
	void checkCorrectnessBeforeParsing(
		//! All known flags.
		StringList & flags,
		//! All known names.
		StringList & names ) const override
	{
		GroupIface::checkCorrectnessBeforeParsing( flags, names );

		for( const auto & arg : children() )
		{
			if( arg->isRequired() )
				throw BaseException( String( SL( "Required argument \"" ) ) +
					arg->name() +
					SL( "\" is not allowed to be in OnlyOne group \"" ) +
					name() + SL( "\"." ) );
		}
	}

	//! Check correctness of the argument after parsing.
	void checkCorrectnessAfterParsing() const override
	{
		GroupIface::checkCorrectnessAfterParsing();

		ArgIface * defined = nullptr;

		for( const auto & arg : children() )
		{
			if( arg->isDefined() )
			{
				if( defined )
					throw BaseException( String( SL( "Only one argument can "
						"be defined in OnlyOne group \"" ) ) + name() + SL( "\". " ) +
						SL( "Whereas defined \"" ) + defined->name() +
						SL( "\" and \"" ) + arg->name() + SL( "\"." ) );
				else
					defined = arg;
			}
		}
	}
}; // class OnlyOneGroup

//
// AllOfGroup
//

//! Group of args where all arguments should be defined.
class AllOfGroup final
	:	public GroupIface
{
public:
	template< typename T >
	explicit AllOfGroup( T && name,
		bool required = false )
		:	GroupIface( std::forward< T > ( name ), required )
	{
	}

	virtual ~AllOfGroup()
	{
	}

	//! \return Is this argument defined?
	bool isDefined() const override
	{
		return !std::any_of( children().cbegin(), children().cend(),
			[] ( const auto & arg ) { return !arg->isDefined(); } );
	}

protected:
	/*!
		Check correctness of the argument before parsing.

		Implementation of this method must add his flag
		and name to the flags and names.
	*/
	void checkCorrectnessBeforeParsing(
		//! All known flags.
		StringList & flags,
		//! All known names.
		StringList & names ) const override
	{
		GroupIface::checkCorrectnessBeforeParsing( flags, names );

		for( const auto & arg : children() )
		{
			if( arg->isRequired() )
				throw BaseException( String( SL( "Required argument \"" ) ) +
					arg->name() +
					SL( "\" is not allowed to " ) +
					SL( "be in AllOf group \"" ) + name() + SL( "\"." ) );
		}
	}

	//! Check correctness of the argument after parsing.
	void checkCorrectnessAfterParsing() const override
	{
		GroupIface::checkCorrectnessAfterParsing();

		bool defined = false;

		const bool all = std::all_of( children().cbegin(), children().cend(),
			[ & ] ( const auto & arg )
			{
				if( arg->isDefined() )
					defined = true;

				return arg->isDefined();
			} );

		if( defined && !all )
			throw BaseException( String( SL( "All arguments in "
				"AllOf group \"" ) ) + name() + SL( "\" should be defined." ) );
	}
}; // class AllOfGroup

//
// AtLeastOneGroup
//

//! Group of args where at least one argument should be defined.
class AtLeastOneGroup final
	:	public GroupIface
{
public:
	template< typename T >
	explicit AtLeastOneGroup( T && name,
		bool required = false )
		:	GroupIface( std::forward< T > ( name ), required )
	{
	}

	virtual ~AtLeastOneGroup()
	{
	}

	//! \return Is this argument defined?
	bool isDefined() const override
	{
		return std::any_of( children().cbegin(), children().cend(),
			[] ( const auto & arg ) { return arg->isDefined(); } );
	}

protected:
	/*!
		Check correctness of the argument before parsing.

		Implementation of this method must add his flag
		and name to the flags and names.
	*/
	void checkCorrectnessBeforeParsing(
		//! All known flags.
		StringList & flags,
		//! All known names.
		StringList & names ) const override
	{
		GroupIface::checkCorrectnessBeforeParsing( flags, names );

		for( const auto & arg : children() )
		{
			if( arg->isRequired() )
				throw BaseException( String( SL( "Required argument \"" ) ) +
					arg->name() +
					SL( "\" is not allowed to " ) +
					SL( "be in AtLeastOne group \"" ) + name() + SL( "\"." ) );
		}
	}
}; // class AtLeastOneGroup

} /* namespace Args */

#endif // ARGS__GROUPS_HPP__INCLUDED
<|endoftext|>


<commit_before>/*
    Copyright 2008 Brain Research Institute, Melbourne, Australia

    Written by J-Donald Tournier, 27/06/08.

    This file is part of MRtrix.

    MRtrix is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    MRtrix is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with MRtrix.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <gsl/gsl_version.h>

#include "app.h"
#include "progressbar.h"
#include "file/path.h"
#include "file/config.h"

//#define NUM_DEFAULT_OPTIONS 5

namespace MR {

  namespace {

    const char* busy[] = {
      ".    ",
      " .   ",
      "  .  ",
      "   . ",
      "    .",
      "   . ",
      "  .  ",
      " .   "
    };

    void display_func_cmdline (ProgressInfo& p)
    {
      if (p.as_percentage)
        fprintf (stderr, "\r%s: %s %3zu%%", App::name().c_str(), p.text.c_str(), size_t(p.value));
      else
        fprintf (stderr, "\r%s: %s %s", App::name().c_str(), p.text.c_str(), busy[p.value%8]);
    }

    void done_func_cmdline (ProgressInfo& p)
    {
      if (p.as_percentage)
        fprintf (stderr, "\r%s: %s %3u%%\n", App::name().c_str(), p.text.c_str(), 100);
      else
        fprintf (stderr, "\r%s: %s  - ok\n", App::name().c_str(), p.text.c_str());
    }
  }

  void cmdline_print (const std::string& msg) { std::cout << msg; }

  void cmdline_error (const std::string& msg)
  {
    if (App::log_level) std::cerr << App::name() << ": " << msg << "\n";
  }

  void cmdline_info  (const std::string& msg)
  {
    if (App::log_level > 1) std::cerr << App::name() << " [INFO]: " <<  msg << "\n";
  }

  void cmdline_debug (const std::string& msg)
  {
    if (App::log_level > 2) std::cerr << App::name() << " [DEBUG]: " <<  msg << "\n";
  }

  namespace {

    void print_formatted_paragraph (const std::string& header, const std::string& text, int header_indent, int indent, int width)
    {
      int current = fprintf (stderr, "%-*s%-*s", header_indent, "", indent-header_indent-1, header.c_str());

      std::string::size_type start = 0, end;
      bool newline = false;
      do {
        end = start;
        while (!isspace(text [end]) && end < text.size()) end++;
        std::string token (text.substr (start, end-start));
        if (newline || current + (int) token.size() + 1 >= width)
          current = fprintf (stderr, "\n%*s%s", indent, "", token.c_str()) - 1;
        else current += fprintf (stderr, " %s", token.c_str());
        newline = text[end] == '\n';
        start = end + 1;
      } while (end < text.size());
      fprintf (stderr, "\n");
    }

  }

#define HELP_WIDTH  80

#define HELP_PURPOSE_INDENT 0, 4, HELP_WIDTH
#define HELP_ARG_INDENT 8, 20, HELP_WIDTH
#define HELP_OPTION_INDENT 2, 20, HELP_WIDTH
#define HELP_OPTION_ARG_INDENT 20, 24, HELP_WIDTH

  int App::log_level = 1;
  std::string     App::application_name;
  const char**    App::command_description = NULL;
  const Argument* App::command_arguments = NULL;
  const Option*   App::command_options = NULL;
  const size_t*   App::version = NULL;
  const char*     App::copyright = NULL;
  const char*     App::author = NULL;

  const Option App::default_options[] = {
    Option ("info", "display information messages."),
    Option ("quiet", "do not display information messages or progress status."),
    Option ("debug", "display debugging messages."),
    Option ("help", "display this information page and exit."),
    Option ("version", "display version information and exit."),
    Option()
  };

  const Option* App::match_option (const char* stub) const
  {
    std::vector<const Option*> candidates;
    std::string root (stub);

    for (const Option* opt = command_options; *opt; ++opt)
      if (root.compare (0, root.size(), opt->id, root.size()) == 0)
        candidates.push_back (opt);

    for (const Option* opt = default_options; *opt; ++opt)
      if (root.compare (0, root.size(), opt->id, root.size()) == 0)
        candidates.push_back (opt);

    if (candidates.size() == 0)
      return (NULL);

    if (candidates.size() == 1)
      return (candidates[0]);

    for (std::vector<const Option*>::const_iterator opt = candidates.begin(); opt != candidates.end(); ++opt)
      if (root == (*opt)->id)
        return (*opt);

    root = "several matches possible for option \"-" + root + "\": \"-" + candidates[0]->id;

    for (std::vector<const Option*>::const_iterator opt = candidates.begin()+1; opt != candidates.end(); ++opt)
      root += std::string (", \"-") + (*opt)->id + "\"";

    throw Exception (root);
  }

  App::App (int argc, char** argv, const char** cmd_desc, const MR::Argument* cmd_args, const MR::Option* cmd_opts,
      const size_t* cmd_version, const char* cmd_author, const char* cmd_copyright)
  {
#ifdef WINDOWS
    // force stderr to be unbuffered, and stdout to be line-buffered:
    setvbuf (stderr, NULL, _IONBF, 0);
    setvbuf (stdout, NULL, _IOLBF, 0);
#endif

    command_description = cmd_desc;
    command_arguments = cmd_args;
    command_options = cmd_opts;
    author = cmd_author;
    version = cmd_version;
    copyright = cmd_copyright;

    if (argc == 2) {
      if (strcmp (argv[1], "__print_full_usage__") == 0) {
        print_full_usage ();
        throw (0);
      }
    }

    application_name = Path::basename (argv[0]);
#ifdef WINDOWS
    if (Path::has_suffix (application_name, ".exe"))
      application_name.erase (application_name.size()-4);
#endif

    log_level = 1;

    ProgressBar::display_func = display_func_cmdline;
    ProgressBar::done_func = done_func_cmdline;

    print = cmdline_print;
    error = cmdline_error;
    info = cmdline_info;
    debug = cmdline_debug;

    sort_arguments (argc, argv);

    srand (time (NULL));

    File::Config::init ();
  }

  App::~App () { }

  void App::sort_arguments (int argc, char** argv)
  {
    for (int n = 1; n < argc; n++) {
      const char* arg = argv[n];
      if (arg[0] == '-' && arg[1] && !isdigit(arg[1]) && arg[1] != '.') {

        while (*arg == '-') arg++;
        const Option* opt = match_option (arg);

        if (!opt)
          throw Exception (std::string ("unknown option \"-") + arg + "\"");

        else if (opt == default_options) { // info
          if (log_level < 2)
            log_level = 2;
        }
        else if (opt == default_options+1) { // quiet
          log_level = 0;
          ProgressBar::display = false;
        }
        else if (opt == default_options+2) { // debug
          log_level = 3;
        }
        else if (opt == default_options+3) { // help
          print_help ();
          throw 0;
        }
        else if (opt == default_options+4) { // version
          std::printf (
              "== %s %zu.%zu.%zu ==\n"
              "%d bit %s version, built " __DATE__ " against MRtrix %zu.%zu.%zu, using GSL %s\n"
              "Author: %s\n"
              "%s\n",

              App::name().c_str(), version[0], version[1], version[2],
              int(8*sizeof(size_t)),
#ifdef NDEBUG
              "release"
#else
              "debug"
#endif
              , mrtrix_major_version, mrtrix_minor_version, mrtrix_micro_version, gsl_version,
              ( author ? author : "J-Donald Tournier (d.tournier@brain.org.au)" ),
              ( copyright ? copyright :
                "Copyright (C) 2008 Brain Research Institute, Melbourne, Australia.\n"
                "This is free software; see the source for copying conditions.\n"
                "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." )
              );

          throw 0;
        }
        else { // command option
          if (n + int(opt->args.size()) >= argc)
            throw Exception (std::string ("not enough parameters to option \"-") + opt->id + "\"");

          option.push_back (ParsedOption (opt->id, argv+n+1));
        }
      }
      else argument.push_back (argv[n]);
    }
  }

  void App::parse_arguments ()
  {
    size_t num_args_required = 0, num_command_arguments = 0;
    bool has_optional_arguments = false;

    for (const Argument* arg = App::command_arguments; *arg; arg++) {
      num_command_arguments++;
      if (arg->flags & Optional) has_optional_arguments = true;
      else num_args_required++;
      if (arg->flags & AllowMultiple) has_optional_arguments = true;
    }

    if (has_optional_arguments && num_args_required > argument.size())
      throw Exception ("expected at least " + str (num_args_required)
          + " arguments (" + str(argument.size()) + " supplied)");

    if (!has_optional_arguments && num_args_required != argument.size())
      throw Exception ("expected exactly " + str (num_args_required)
          + " arguments (" + str (argument.size()) + " supplied)");

    size_t optional_argument = std::numeric_limits<size_t>::max();
    for (size_t n = 0; n < argument.size(); n++) {

      if (n < optional_argument)
        if (command_arguments[n].flags & (Optional | AllowMultiple) )
          optional_argument = n;

      size_t index = n;
      if (n >= optional_argument) {
        if (int(num_args_required - optional_argument) < int(argument.size()-n))
          index = optional_argument;
        else
          index = num_args_required - argument.size() + n + (command_arguments[optional_argument].flags & Optional ? 1 : 0);
      }

      if (index >= num_command_arguments)
        throw Exception ("too many arguments");

      command_arguments[index].check (argument[n]);
    }

    for (const Option* opt = command_options; *opt; ++opt) {
      size_t count = 0;
      for (std::vector<ParsedOption>::const_iterator popt = option.begin();
          popt != option.end(); ++popt)
        if (popt->id == opt->id)
          count++;

      if (count < 1 && !(opt->flags & Optional))
        throw Exception (std::string ("mandatory option \"") + opt->id + "\" must be specified");

      if (count > 1 && !(opt->flags & AllowMultiple))
        throw Exception (std::string ("multiple instances of option \"") +  opt->id + "\" are not allowed");
    }

  }

  void App::print_help () const
  {
    fprintf (stderr, "%s: part of the MRtrix package\n\n", App::name().c_str());
    if (command_description[0]) {
      print_formatted_paragraph ("", command_description[0], HELP_PURPOSE_INDENT);
      fprintf (stderr, "\n");
      for (const char** p = command_description+1; *p; p++) {
        print_formatted_paragraph ("", *p, HELP_PURPOSE_INDENT);
        fprintf (stderr, "\n");
      }
    }
    else fprintf (stderr, "(no description available)\n\n");

    fprintf (stderr, "SYNTAX: %s [ options ]", App::name().c_str());
    for (const Argument* arg = command_arguments; *arg; ++arg) {

      if (arg->flags & Optional)
        fprintf (stderr, " [");

      fprintf (stderr, " %s", arg->id);

      if (arg->flags & AllowMultiple) {
        if (!(arg->flags & Optional))
          fprintf (stderr, " [ %s", arg->id);
        fprintf (stderr, " ...");
      }
      if (arg->flags & (Optional | AllowMultiple))
        fprintf (stderr, " ]");
    }
    fprintf (stderr, "\n\n");

    for (const Argument* arg = command_arguments; *arg; ++arg)
      print_formatted_paragraph (std::string("- ") + arg->id + " ", arg->desc, HELP_ARG_INDENT);
    fprintf (stderr, "\n");

    fprintf (stderr, "OPTIONS:\n\n");
    for (const Option* opt = command_options; *opt; ++opt) {
      std::string text ("-");
      text += opt->id;

      for (std::vector<Argument>::const_iterator optarg = opt->args.begin();
          optarg != opt->args.end(); ++optarg)
        text += std::string (" ") + optarg->id;

      print_formatted_paragraph (text + " ", opt->desc, HELP_OPTION_INDENT);
      // TODO: add argument defaults like this:
      //print_formatted_paragraph (text + " ", opt->desc + opt->arg_defaults(), HELP_OPTION_INDENT);

      fprintf (stderr, "\n");
    }

    fprintf (stderr, "Standard options:\n");
    for (const Option* opt = default_options; *opt; ++opt)
      print_formatted_paragraph (std::string("-") + opt->id, opt->desc, HELP_OPTION_INDENT);
    fprintf (stderr, "\n");
  }

  void App::print_full_usage () const
  {
    for (const char** p = command_description; *p; p++)
      std::cout << *p << "\n";

    for (const Argument* arg = command_arguments; arg; ++arg)
      arg->print_usage();

    for (const Option* opt = command_options; opt; ++opt)
      opt->print_usage();

    for (const Option* opt = default_options; opt; ++opt)
      opt->print_usage();
  }

}

<commit_msg>fix bug in updated command-line parsing interface<commit_after>/*
    Copyright 2008 Brain Research Institute, Melbourne, Australia

    Written by J-Donald Tournier, 27/06/08.

    This file is part of MRtrix.

    MRtrix is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    MRtrix is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with MRtrix.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <gsl/gsl_version.h>

#include "app.h"
#include "debug.h"
#include "progressbar.h"
#include "file/path.h"
#include "file/config.h"

//#define NUM_DEFAULT_OPTIONS 5

namespace MR {

  namespace {

    const char* busy[] = {
      ".    ",
      " .   ",
      "  .  ",
      "   . ",
      "    .",
      "   . ",
      "  .  ",
      " .   "
    };

    void display_func_cmdline (ProgressInfo& p)
    {
      if (p.as_percentage)
        fprintf (stderr, "\r%s: %s %3zu%%", App::name().c_str(), p.text.c_str(), size_t(p.value));
      else
        fprintf (stderr, "\r%s: %s %s", App::name().c_str(), p.text.c_str(), busy[p.value%8]);
    }

    void done_func_cmdline (ProgressInfo& p)
    {
      if (p.as_percentage)
        fprintf (stderr, "\r%s: %s %3u%%\n", App::name().c_str(), p.text.c_str(), 100);
      else
        fprintf (stderr, "\r%s: %s  - ok\n", App::name().c_str(), p.text.c_str());
    }
  }

  void cmdline_print (const std::string& msg) { std::cout << msg; }

  void cmdline_error (const std::string& msg)
  {
    if (App::log_level) std::cerr << App::name() << ": " << msg << "\n";
  }

  void cmdline_info  (const std::string& msg)
  {
    if (App::log_level > 1) std::cerr << App::name() << " [INFO]: " <<  msg << "\n";
  }

  void cmdline_debug (const std::string& msg)
  {
    if (App::log_level > 2) std::cerr << App::name() << " [DEBUG]: " <<  msg << "\n";
  }

  namespace {

    void print_formatted_paragraph (const std::string& header, const std::string& text, int header_indent, int indent, int width)
    {
      int current = fprintf (stderr, "%-*s%-*s", header_indent, "", indent-header_indent-1, header.c_str());

      std::string::size_type start = 0, end;
      bool newline = false;
      do {
        end = start;
        while (!isspace(text [end]) && end < text.size()) end++;
        std::string token (text.substr (start, end-start));
        if (newline || current + (int) token.size() + 1 >= width)
          current = fprintf (stderr, "\n%*s%s", indent, "", token.c_str()) - 1;
        else current += fprintf (stderr, " %s", token.c_str());
        newline = text[end] == '\n';
        start = end + 1;
      } while (end < text.size());
      fprintf (stderr, "\n");
    }

  }

#define HELP_WIDTH  80

#define HELP_PURPOSE_INDENT 0, 4, HELP_WIDTH
#define HELP_ARG_INDENT 8, 20, HELP_WIDTH
#define HELP_OPTION_INDENT 2, 20, HELP_WIDTH
#define HELP_OPTION_ARG_INDENT 20, 24, HELP_WIDTH

  int App::log_level = 1;
  std::string     App::application_name;
  const char**    App::command_description = NULL;
  const Argument* App::command_arguments = NULL;
  const Option*   App::command_options = NULL;
  const size_t*   App::version = NULL;
  const char*     App::copyright = NULL;
  const char*     App::author = NULL;

  const Option App::default_options[] = {
    Option ("info", "display information messages."),
    Option ("quiet", "do not display information messages or progress status."),
    Option ("debug", "display debugging messages."),
    Option ("help", "display this information page and exit."),
    Option ("version", "display version information and exit."),
    Option()
  };

  const Option* App::match_option (const char* stub) const
  {
    std::vector<const Option*> candidates;
    std::string root (stub);

    for (const Option* opt = command_options; *opt; ++opt)
      if (root.compare (0, root.size(), opt->id, root.size()) == 0)
        candidates.push_back (opt);

    for (const Option* opt = default_options; *opt; ++opt)
      if (root.compare (0, root.size(), opt->id, root.size()) == 0)
        candidates.push_back (opt);

    if (candidates.size() == 0)
      return (NULL);

    if (candidates.size() == 1)
      return (candidates[0]);

    for (std::vector<const Option*>::const_iterator opt = candidates.begin(); opt != candidates.end(); ++opt)
      if (root == (*opt)->id)
        return (*opt);

    root = "several matches possible for option \"-" + root + "\": \"-" + candidates[0]->id;

    for (std::vector<const Option*>::const_iterator opt = candidates.begin()+1; opt != candidates.end(); ++opt)
      root += std::string (", \"-") + (*opt)->id + "\"";

    throw Exception (root);
  }

  App::App (int argc, char** argv, const char** cmd_desc, const MR::Argument* cmd_args, const MR::Option* cmd_opts,
      const size_t* cmd_version, const char* cmd_author, const char* cmd_copyright)
  {
#ifdef WINDOWS
    // force stderr to be unbuffered, and stdout to be line-buffered:
    setvbuf (stderr, NULL, _IONBF, 0);
    setvbuf (stdout, NULL, _IOLBF, 0);
#endif

    command_description = cmd_desc;
    command_arguments = cmd_args;
    command_options = cmd_opts;
    author = cmd_author;
    version = cmd_version;
    copyright = cmd_copyright;

    if (argc == 2) {
      if (strcmp (argv[1], "__print_full_usage__") == 0) {
        print_full_usage ();
        throw (0);
      }
    }

    application_name = Path::basename (argv[0]);
#ifdef WINDOWS
    if (Path::has_suffix (application_name, ".exe"))
      application_name.erase (application_name.size()-4);
#endif

    log_level = 1;

    ProgressBar::display_func = display_func_cmdline;
    ProgressBar::done_func = done_func_cmdline;

    print = cmdline_print;
    error = cmdline_error;
    info = cmdline_info;
    debug = cmdline_debug;

    sort_arguments (argc, argv);

    srand (time (NULL));

    File::Config::init ();
  }

  App::~App () { }

  void App::sort_arguments (int argc, char** argv)
  {
    for (int n = 1; n < argc; n++) {
      const char* arg = argv[n];
      if (arg[0] == '-' && arg[1] && !isdigit(arg[1]) && arg[1] != '.') {

        while (*arg == '-') arg++;
        const Option* opt = match_option (arg);

        if (!opt)
          throw Exception (std::string ("unknown option \"-") + arg + "\"");

        else if (opt == default_options) { // info
          if (log_level < 2)
            log_level = 2;
        }
        else if (opt == default_options+1) { // quiet
          log_level = 0;
          ProgressBar::display = false;
        }
        else if (opt == default_options+2) { // debug
          log_level = 3;
        }
        else if (opt == default_options+3) { // help
          print_help ();
          throw 0;
        }
        else if (opt == default_options+4) { // version
          std::printf (
              "== %s %zu.%zu.%zu ==\n"
              "%d bit %s version, built " __DATE__ " against MRtrix %zu.%zu.%zu, using GSL %s\n"
              "Author: %s\n"
              "%s\n",

              App::name().c_str(), version[0], version[1], version[2],
              int(8*sizeof(size_t)),
#ifdef NDEBUG
              "release"
#else
              "debug"
#endif
              , mrtrix_major_version, mrtrix_minor_version, mrtrix_micro_version, gsl_version,
              ( author ? author : "J-Donald Tournier (d.tournier@brain.org.au)" ),
              ( copyright ? copyright :
                "Copyright (C) 2008 Brain Research Institute, Melbourne, Australia.\n"
                "This is free software; see the source for copying conditions.\n"
                "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." )
              );

          throw 0;
        }
        else { // command option
          if (n + int(opt->args.size()) >= argc)
            throw Exception (std::string ("not enough parameters to option \"-") + opt->id + "\"");

          option.push_back (ParsedOption (opt->id, argv+n+1));
          n += opt->args.size();
        }
      }
      else {
        argument.push_back (argv[n]);
        VAR (argv[n]);
      }
    }
  }

  void App::parse_arguments ()
  {
    size_t num_args_required = 0, num_command_arguments = 0;
    bool has_optional_arguments = false;

    for (const Argument* arg = App::command_arguments; *arg; arg++) {
      num_command_arguments++;
      if (arg->flags & Optional) has_optional_arguments = true;
      else num_args_required++;
      if (arg->flags & AllowMultiple) has_optional_arguments = true;
    }

    if (has_optional_arguments && num_args_required > argument.size())
      throw Exception ("expected at least " + str (num_args_required)
          + " arguments (" + str(argument.size()) + " supplied)");

    if (!has_optional_arguments && num_args_required != argument.size())
      throw Exception ("expected exactly " + str (num_args_required)
          + " arguments (" + str (argument.size()) + " supplied)");

    size_t optional_argument = std::numeric_limits<size_t>::max();
    for (size_t n = 0; n < argument.size(); n++) {

      if (n < optional_argument)
        if (command_arguments[n].flags & (Optional | AllowMultiple) )
          optional_argument = n;

      size_t index = n;
      if (n >= optional_argument) {
        if (int(num_args_required - optional_argument) < int(argument.size()-n))
          index = optional_argument;
        else
          index = num_args_required - argument.size() + n + (command_arguments[optional_argument].flags & Optional ? 1 : 0);
      }

      if (index >= num_command_arguments)
        throw Exception ("too many arguments");

      command_arguments[index].check (argument[n]);
    }

    for (const Option* opt = command_options; *opt; ++opt) {
      size_t count = 0;
      for (std::vector<ParsedOption>::const_iterator popt = option.begin();
          popt != option.end(); ++popt)
        if (popt->id == opt->id)
          count++;

      if (count < 1 && !(opt->flags & Optional))
        throw Exception (std::string ("mandatory option \"") + opt->id + "\" must be specified");

      if (count > 1 && !(opt->flags & AllowMultiple))
        throw Exception (std::string ("multiple instances of option \"") +  opt->id + "\" are not allowed");
    }

  }

  void App::print_help () const
  {
    fprintf (stderr, "%s: part of the MRtrix package\n\n", App::name().c_str());
    if (command_description[0]) {
      print_formatted_paragraph ("", command_description[0], HELP_PURPOSE_INDENT);
      fprintf (stderr, "\n");
      for (const char** p = command_description+1; *p; p++) {
        print_formatted_paragraph ("", *p, HELP_PURPOSE_INDENT);
        fprintf (stderr, "\n");
      }
    }
    else fprintf (stderr, "(no description available)\n\n");

    fprintf (stderr, "SYNTAX: %s [ options ]", App::name().c_str());
    for (const Argument* arg = command_arguments; *arg; ++arg) {

      if (arg->flags & Optional)
        fprintf (stderr, " [");

      fprintf (stderr, " %s", arg->id);

      if (arg->flags & AllowMultiple) {
        if (!(arg->flags & Optional))
          fprintf (stderr, " [ %s", arg->id);
        fprintf (stderr, " ...");
      }
      if (arg->flags & (Optional | AllowMultiple))
        fprintf (stderr, " ]");
    }
    fprintf (stderr, "\n\n");

    for (const Argument* arg = command_arguments; *arg; ++arg)
      print_formatted_paragraph (std::string("- ") + arg->id + " ", arg->desc, HELP_ARG_INDENT);
    fprintf (stderr, "\n");

    fprintf (stderr, "OPTIONS:\n\n");
    for (const Option* opt = command_options; *opt; ++opt) {
      std::string text ("-");
      text += opt->id;

      for (std::vector<Argument>::const_iterator optarg = opt->args.begin();
          optarg != opt->args.end(); ++optarg)
        text += std::string (" ") + optarg->id;

      print_formatted_paragraph (text + " ", opt->desc, HELP_OPTION_INDENT);
      // TODO: add argument defaults like this:
      //print_formatted_paragraph (text + " ", opt->desc + opt->arg_defaults(), HELP_OPTION_INDENT);

      fprintf (stderr, "\n");
    }

    fprintf (stderr, "Standard options:\n");
    for (const Option* opt = default_options; *opt; ++opt)
      print_formatted_paragraph (std::string("-") + opt->id, opt->desc, HELP_OPTION_INDENT);
    fprintf (stderr, "\n");
  }

  void App::print_full_usage () const
  {
    for (const char** p = command_description; *p; p++)
      std::cout << *p << "\n";

    for (const Argument* arg = command_arguments; arg; ++arg)
      arg->print_usage();

    for (const Option* opt = command_options; opt; ++opt)
      opt->print_usage();

    for (const Option* opt = default_options; opt; ++opt)
      opt->print_usage();
  }

}

<|endoftext|>


<commit_before>#ifndef COFFEE_MILL_COMMON_DEFERED_READER_HPP
#define COFFEE_MILL_COMMON_DEFERED_READER_HPP
#include <mill/common/Trajectory.hpp>
#include <string_view>
#include <optional>
#include <iterator>
#include <cstddef>

namespace mill
{

class ReaderIterator;
class ReaderIteratorSentinel;

class DeferedReaderBase
{
  public:
    using trajectory_type = Trajectory;
    using snapshot_type   = Snapshot;
    using particle_type   = Particle;
    using vector_type     = Particle::vector_type;
    using attribute_container_type = trajectory_type::attribute_container_type;

  public:

    virtual ~DeferedReaderBase() = default;

    virtual attribute_container_type read_header()                         = 0;
    virtual trajectory_type          read()                                = 0;
    virtual std::optional<snapshot_type> read_frame()                      = 0;
    virtual std::optional<snapshot_type> read_frame(const std::size_t idx) = 0;

    ReaderIterator         begin();
    ReaderIteratorSentinel end();

    virtual void             rewind()    = 0;
    virtual bool             is_eof()    const noexcept = 0;
    virtual std::size_t      current()   const noexcept = 0;
    virtual std::string_view file_name() const noexcept = 0;
};

class ReaderIterator
{
  public:
    using iterator_category = std::input_iterator_tag;
    using difference_type   = std::ptrdiff_t;
    using value_type        = Snapshot;
    using reference         = value_type&;
    using pointer           = value_type*;

  public:
    explicit ReaderIterator(DeferedReaderBase& reader)
        : current_(reader.read_frame()), reader_(std::addressof(reader))
    {}
    ~ReaderIterator() = default;

    ReaderIterator(const ReaderIterator&) = default;
    ReaderIterator(ReaderIterator&&)      = default;
    ReaderIterator& operator=(const ReaderIterator&) = default;
    ReaderIterator& operator=(ReaderIterator&&)      = default;

    value_type& operator*()
    {
        if(!current_)
        {
            using namespace std::literals::string_literals;
            throw std::out_of_range("ReaderIterator: "s +
                    std::string(reader_->file_name()) +
                    " does not have any more snapshot"s);
        }
        return *current_;
    }
    value_type* operator->()
    {
        if(!current_)
        {
            using namespace std::literals::string_literals;
            throw std::out_of_range("ReaderIterator: "s +
                    std::string(reader_->file_name()) +
                    " does not have any more snapshot"s);
        }
        return std::addressof(*current_);
    }

    ReaderIterator& operator++()
    {
        current_ = reader_->read_frame();
        return *this;
    }
    ReaderIterator  operator++(int)
    {
        current_ = reader_->read_frame();
        return *this;
    }

    bool             has_value() const noexcept {return current_.has_value();}
    bool             is_eof()    const noexcept {return reader_->is_eof();}
    std::size_t      current()   const noexcept {return reader_->current();}
    std::string_view file_name() const noexcept {return reader_->file_name();}

  private:
    std::optional<value_type> current_;
    DeferedReaderBase*        reader_;
};

inline bool operator==(const ReaderIterator& lhs, const ReaderIterator& rhs)
{
    return std::make_tuple(lhs.is_eof(), lhs.current(), lhs.file_name()) ==
           std::make_tuple(rhs.is_eof(), rhs.current(), rhs.file_name());
}
inline bool operator!=(const ReaderIterator& lhs, const ReaderIterator& rhs)
{
    return !(lhs == rhs);
}

class ReaderIteratorSentinel
{
  public:
    using iterator_category = std::input_iterator_tag;
    using difference_type   = std::ptrdiff_t;
    using value_type        = Snapshot;
    using reference         = value_type&;
    using pointer           = value_type*;

  public:
    explicit ReaderIteratorSentinel(DeferedReaderBase& reader)
        : file_name_(reader.file_name())
    {}

    bool             is_eof()    const noexcept {return true;}
    std::string_view file_name() const noexcept {return file_name_;}

  private:
    std::string_view file_name_;
};

inline bool operator==(
        const ReaderIteratorSentinel& lhs, const ReaderIteratorSentinel& rhs)
{
    return lhs.file_name() == rhs.file_name();
}
inline bool operator!=(
        const ReaderIteratorSentinel& lhs, const ReaderIteratorSentinel& rhs)
{
    return !(lhs == rhs);
}

inline bool operator==(const ReaderIterator& lhs, const ReaderIteratorSentinel& rhs)
{
    return (not lhs.has_value()) && (lhs.file_name() == rhs.file_name());
}
inline bool operator==(const ReaderIteratorSentinel& lhs, const ReaderIterator& rhs)
{
    return (not rhs.has_value()) && (lhs.file_name() == rhs.file_name());
}
inline bool operator!=(const ReaderIterator& lhs, const ReaderIteratorSentinel& rhs)
{
    return !(lhs == rhs);
}
inline bool operator!=(const ReaderIteratorSentinel& lhs, const ReaderIterator& rhs)
{
    return !(lhs == rhs);
}

inline ReaderIterator DeferedReaderBase::begin()
{
    return ReaderIterator(*this);
}
inline ReaderIteratorSentinel DeferedReaderBase::end()
{
    return ReaderIteratorSentinel(*this);
}

} // mill
#endif// COFFEE_MILL_COMMON_DEFERED_READER_HPP
<commit_msg>:bug: fix iterator increment method<commit_after>#ifndef COFFEE_MILL_COMMON_DEFERED_READER_HPP
#define COFFEE_MILL_COMMON_DEFERED_READER_HPP
#include <mill/common/Trajectory.hpp>
#include <string_view>
#include <optional>
#include <iterator>
#include <cstddef>

namespace mill
{

class ReaderIterator;
class ReaderIteratorSentinel;

class DeferedReaderBase
{
  public:
    using trajectory_type = Trajectory;
    using snapshot_type   = Snapshot;
    using particle_type   = Particle;
    using vector_type     = Particle::vector_type;
    using attribute_container_type = trajectory_type::attribute_container_type;

  public:

    virtual ~DeferedReaderBase() = default;

    virtual attribute_container_type read_header()                         = 0;
    virtual trajectory_type          read()                                = 0;
    virtual std::optional<snapshot_type> read_frame()                      = 0;
    virtual std::optional<snapshot_type> read_frame(const std::size_t idx) = 0;

    ReaderIterator         begin();
    ReaderIteratorSentinel end();

    virtual void             rewind()    = 0;
    virtual bool             is_eof()    const noexcept = 0;
    virtual std::size_t      current()   const noexcept = 0;
    virtual std::string_view file_name() const noexcept = 0;
};

class ReaderIterator
{
  public:
    using iterator_category = std::input_iterator_tag;
    using difference_type   = std::ptrdiff_t;
    using value_type        = Snapshot;
    using reference         = value_type&;
    using pointer           = value_type*;

  public:
    explicit ReaderIterator(DeferedReaderBase& reader)
        : current_(reader.read_frame()), reader_(std::addressof(reader))
    {}
    ~ReaderIterator() = default;

    ReaderIterator(const ReaderIterator&) = default;
    ReaderIterator(ReaderIterator&&)      = default;
    ReaderIterator& operator=(const ReaderIterator&) = default;
    ReaderIterator& operator=(ReaderIterator&&)      = default;

    value_type& operator*()
    {
        if(!current_)
        {
            using namespace std::literals::string_literals;
            throw std::out_of_range("ReaderIterator: "s +
                    std::string(reader_->file_name()) +
                    " does not have any more snapshot"s);
        }
        return *current_;
    }
    value_type* operator->()
    {
        if(!current_)
        {
            using namespace std::literals::string_literals;
            throw std::out_of_range("ReaderIterator: "s +
                    std::string(reader_->file_name()) +
                    " does not have any more snapshot"s);
        }
        return std::addressof(*current_);
    }

    ReaderIterator& operator++()
    {
        current_ = reader_->read_frame();
        return *this;
    }
    ReaderIterator  operator++(int)
    {
        const auto self(*this);
        current_ = reader_->read_frame();
        return self;
    }

    bool             has_value() const noexcept {return current_.has_value();}
    bool             is_eof()    const noexcept {return reader_->is_eof();}
    std::size_t      current()   const noexcept {return reader_->current();}
    std::string_view file_name() const noexcept {return reader_->file_name();}

  private:
    std::optional<value_type> current_;
    DeferedReaderBase*        reader_;
};

inline bool operator==(const ReaderIterator& lhs, const ReaderIterator& rhs)
{
    return std::make_tuple(lhs.is_eof(), lhs.current(), lhs.file_name()) ==
           std::make_tuple(rhs.is_eof(), rhs.current(), rhs.file_name());
}
inline bool operator!=(const ReaderIterator& lhs, const ReaderIterator& rhs)
{
    return !(lhs == rhs);
}

class ReaderIteratorSentinel
{
  public:
    using iterator_category = std::input_iterator_tag;
    using difference_type   = std::ptrdiff_t;
    using value_type        = Snapshot;
    using reference         = value_type&;
    using pointer           = value_type*;

  public:
    explicit ReaderIteratorSentinel(DeferedReaderBase& reader)
        : file_name_(reader.file_name())
    {}

    bool             is_eof()    const noexcept {return true;}
    std::string_view file_name() const noexcept {return file_name_;}

  private:
    std::string_view file_name_;
};

inline bool operator==(
        const ReaderIteratorSentinel& lhs, const ReaderIteratorSentinel& rhs)
{
    return lhs.file_name() == rhs.file_name();
}
inline bool operator!=(
        const ReaderIteratorSentinel& lhs, const ReaderIteratorSentinel& rhs)
{
    return !(lhs == rhs);
}

inline bool operator==(const ReaderIterator& lhs, const ReaderIteratorSentinel& rhs)
{
    return (not lhs.has_value()) && (lhs.file_name() == rhs.file_name());
}
inline bool operator==(const ReaderIteratorSentinel& lhs, const ReaderIterator& rhs)
{
    return (not rhs.has_value()) && (lhs.file_name() == rhs.file_name());
}
inline bool operator!=(const ReaderIterator& lhs, const ReaderIteratorSentinel& rhs)
{
    return !(lhs == rhs);
}
inline bool operator!=(const ReaderIteratorSentinel& lhs, const ReaderIterator& rhs)
{
    return !(lhs == rhs);
}

inline ReaderIterator DeferedReaderBase::begin()
{
    return ReaderIterator(*this);
}
inline ReaderIteratorSentinel DeferedReaderBase::end()
{
    return ReaderIteratorSentinel(*this);
}

} // mill
#endif// COFFEE_MILL_COMMON_DEFERED_READER_HPP
<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  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.txt
 *
 *  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.
 *
 *=========================================================================*/
#ifndef itkEuler3DTransform_hxx
#define itkEuler3DTransform_hxx

#include "itkEuler3DTransform.h"

namespace itk
{
// Constructor with default arguments
template<typename TParametersValueType>
Euler3DTransform<TParametersValueType>
::Euler3DTransform() :
  Superclass(ParametersDimension)
{
  m_ComputeZYX = false;
  m_AngleX = m_AngleY = m_AngleZ = NumericTraits<ScalarType>::ZeroValue();
  this->m_FixedParameters.SetSize(SpaceDimension+1);
  this->m_FixedParameters.Fill(0.0);
}

// Constructor with default arguments
template<typename TParametersValueType>
Euler3DTransform<TParametersValueType>
::Euler3DTransform(const MatrixType & matrix, const OutputPointType & offset)
{
  m_ComputeZYX = false;
  this->SetMatrix(matrix);

  OffsetType off;
  off[0] = offset[0];
  off[1] = offset[1];
  off[2] = offset[2];
  this->SetOffset(off);
  this->m_FixedParameters.SetSize(SpaceDimension+1);
  this->m_FixedParameters.Fill(0.0);
}

// Constructor with arguments
template<typename TParametersValueType>
Euler3DTransform<TParametersValueType>
::Euler3DTransform(unsigned int parametersDimension) :
  Superclass(parametersDimension)
{
  m_ComputeZYX = false;
  m_AngleX = m_AngleY = m_AngleZ = NumericTraits<ScalarType>::ZeroValue();
  this->m_FixedParameters.SetSize(SpaceDimension+1);
  this->m_FixedParameters.Fill(0.0);
}

// Set Angles
template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::SetVarRotation(ScalarType angleX, ScalarType angleY, ScalarType angleZ)
{
  this->m_AngleX = angleX;
  this->m_AngleY = angleY;
  this->m_AngleZ = angleZ;
}

// Set Parameters
template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::SetParameters(const ParametersType & parameters)
{
  itkDebugMacro(<< "Setting parameters " << parameters);

  // Save parameters. Needed for proper operation of TransformUpdateParameters.
  if( &parameters != &(this->m_Parameters) )
    {
    this->m_Parameters = parameters;
    }

  // Set angles with parameters
  m_AngleX = parameters[0];
  m_AngleY = parameters[1];
  m_AngleZ = parameters[2];
  this->ComputeMatrix();

  // Transfer the translation part
  OutputVectorType newTranslation;
  newTranslation[0] = parameters[3];
  newTranslation[1] = parameters[4];
  newTranslation[2] = parameters[5];
  this->SetVarTranslation(newTranslation);
  this->ComputeOffset();

  // Modified is always called since we just have a pointer to the
  // parameters and cannot know if the parameters have changed.
  this->Modified();

  itkDebugMacro(<< "After setting parameters ");
}

// Get Parameters
template<typename TParametersValueType>
const typename Euler3DTransform<TParametersValueType>::ParametersType
& Euler3DTransform<TParametersValueType>
::GetParameters(void) const
  {
  this->m_Parameters[0] = m_AngleX;
  this->m_Parameters[1] = m_AngleY;
  this->m_Parameters[2] = m_AngleZ;
  this->m_Parameters[3] = this->GetTranslation()[0];
  this->m_Parameters[4] = this->GetTranslation()[1];
  this->m_Parameters[5] = this->GetTranslation()[2];

  return this->m_Parameters;
  }

template<typename TParametersValueType>
const typename Euler3DTransform<TParametersValueType>::FixedParametersType &
Euler3DTransform<TParametersValueType>
::GetFixedParameters() const
{
  // Call the superclass GetFixedParameters so that it fills the
  // array, we ignore the returned data and add the additional
  // information to the updated array.
  Superclass::GetFixedParameters();
  this->m_FixedParameters[3] = this-> m_ComputeZYX;
  return this->m_FixedParameters;
}

template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::SetFixedParameters(const FixedParametersType & parameters)
{
  InputPointType c;
  for( unsigned int i = 0; i < InputSpaceDimension; i++ )
    {
    c[i] = this->m_FixedParameters[i] = parameters[i];
    }
  this->SetCenter(c);
  //conditional is here for backwards compatibility: the
  //m_ComputeZYX flag was not serialized so it may or may
  //not be included as part of the fixed parameters
  if( parameters.Size() == 4 )
    {
    this->m_FixedParameters[3] = parameters[3];
    this->SetComputeZYX(this->m_FixedParameters[3]);
    }
}

// Set Rotational Part
template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::SetRotation(ScalarType angleX, ScalarType angleY, ScalarType angleZ)
{
  m_AngleX = angleX;
  m_AngleY = angleY;
  m_AngleZ = angleZ;
  this->ComputeMatrix();
  this->ComputeOffset();
}

// Compose
template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::SetIdentity(void)
{
  Superclass::SetIdentity();
  m_AngleX = 0;
  m_AngleY = 0;
  m_AngleZ = 0;
}

// Compute angles from the rotation matrix
template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::ComputeMatrixParameters(void)
{
  if( m_ComputeZYX )
    {
    m_AngleY = -std::asin(this->GetMatrix()[2][0]);
    double C = std::cos(m_AngleY);
    if( std::fabs(C) > 0.00005 )
      {
      double x = this->GetMatrix()[2][2] / C;
      double y = this->GetMatrix()[2][1] / C;
      m_AngleX = std::atan2(y, x);
      x = this->GetMatrix()[0][0] / C;
      y = this->GetMatrix()[1][0] / C;
      m_AngleZ = std::atan2(y, x);
      }
    else
      {
      m_AngleX = NumericTraits<ScalarType>::ZeroValue();
      double x = this->GetMatrix()[1][1];
      double y = -this->GetMatrix()[0][1];
      m_AngleZ = std::atan2(y, x);
      }
    }
  else
    {
    m_AngleX = std::asin(this->GetMatrix()[2][1]);
    double A = std::cos(m_AngleX);
    if( std::fabs(A) > 0.00005 )
      {
      double x = this->GetMatrix()[2][2] / A;
      double y = -this->GetMatrix()[2][0] / A;
      m_AngleY = std::atan2(y, x);

      x = this->GetMatrix()[1][1] / A;
      y = -this->GetMatrix()[0][1] / A;
      m_AngleZ = std::atan2(y, x);
      }
    else
      {
      m_AngleZ = NumericTraits<ScalarType>::ZeroValue();
      double x = this->GetMatrix()[0][0];
      double y = this->GetMatrix()[1][0];
      m_AngleY = std::atan2(y, x);
      }
    }
  this->ComputeMatrix();
}

// Compute the matrix
template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::ComputeMatrix(void)
{
  // need to check if angles are in the right order
  const ScalarType cx = std::cos(m_AngleX);
  const ScalarType sx = std::sin(m_AngleX);
  const ScalarType cy = std::cos(m_AngleY);
  const ScalarType sy = std::sin(m_AngleY);
  const ScalarType cz = std::cos(m_AngleZ);
  const ScalarType sz = std::sin(m_AngleZ);
  const ScalarType one = NumericTraits<ScalarType>::OneValue();
  const ScalarType zero = NumericTraits<ScalarType>::ZeroValue();

  Matrix<TParametersValueType, 3, 3> RotationX;
  RotationX[0][0] = one;  RotationX[0][1] = zero; RotationX[0][2] = zero;
  RotationX[1][0] = zero; RotationX[1][1] = cx;   RotationX[1][2] = -sx;
  RotationX[2][0] = zero; RotationX[2][1] = sx;   RotationX[2][2] = cx;

  Matrix<TParametersValueType, 3, 3> RotationY;
  RotationY[0][0] = cy;   RotationY[0][1] = zero; RotationY[0][2] = sy;
  RotationY[1][0] = zero; RotationY[1][1] = one;  RotationY[1][2] = zero;
  RotationY[2][0] = -sy;  RotationY[2][1] = zero; RotationY[2][2] = cy;

  Matrix<TParametersValueType, 3, 3> RotationZ;
  RotationZ[0][0] = cz;   RotationZ[0][1] = -sz;  RotationZ[0][2] = zero;
  RotationZ[1][0] = sz;   RotationZ[1][1] = cz;   RotationZ[1][2] = zero;
  RotationZ[2][0] = zero; RotationZ[2][1] = zero; RotationZ[2][2] = one;

  /** Aply the rotation first around Y then X then Z */
  if( m_ComputeZYX )
    {
    this->SetVarMatrix(RotationZ * RotationY * RotationX);
    }
  else
    {
    // Like VTK transformation order
    this->SetVarMatrix(RotationZ * RotationX * RotationY);
    }
}

template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::ComputeJacobianWithRespectToParameters(const InputPointType & p, JacobianType & jacobian) const
{
  // need to check if angles are in the right order
  const double cx = std::cos(m_AngleX);
  const double sx = std::sin(m_AngleX);
  const double cy = std::cos(m_AngleY);
  const double sy = std::sin(m_AngleY);
  const double cz = std::cos(m_AngleZ);
  const double sz = std::sin(m_AngleZ);

  jacobian.SetSize( 3, this->GetNumberOfLocalParameters() );
  jacobian.Fill(0.0);

  const double px = p[0] - this->GetCenter()[0];
  const double py = p[1] - this->GetCenter()[1];
  const double pz = p[2] - this->GetCenter()[2];

  if( m_ComputeZYX )
    {
    jacobian[0][0] = ( cz * sy * cx + sz * sx ) * py + ( -cz * sy * sx + sz * cx ) * pz;
    jacobian[1][0] = ( sz * sy * cx - cz * sx ) * py + ( -sz * sy * sx - cz * cx ) * pz;
    jacobian[2][0] = ( cy * cx ) * py + ( -cy * sx ) * pz;

    jacobian[0][1] = ( -cz * sy ) * px + ( cz * cy * sx ) * py + ( cz * cy * cx ) * pz;
    jacobian[1][1] = ( -sz * sy ) * px + ( sz * cy * sx ) * py + ( sz * cy * cx ) * pz;
    jacobian[2][1] = ( -cy ) * px + ( -sy * sx ) * py + ( -sy * cx ) * pz;

    jacobian[0][2] = ( -sz * cy ) * px + ( -sz * sy * sx - cz * cx ) * py
      + ( -sz * sy * cx + cz * sx ) * pz;
    jacobian[1][2] = ( cz * cy ) * px + ( cz * sy * sx - sz * cx ) * py + ( cz * sy * cx + sz * sx ) * pz;
    jacobian[2][2] = 0;
    }
  else
    {
    jacobian[0][0] = ( -sz * cx * sy ) * px + ( sz * sx ) * py + ( sz * cx * cy ) * pz;
    jacobian[1][0] = ( cz * cx * sy ) * px + ( -cz * sx ) * py + ( -cz * cx * cy ) * pz;
    jacobian[2][0] = ( sx * sy ) * px + ( cx ) * py + ( -sx * cy ) * pz;

    jacobian[0][1] = ( -cz * sy - sz * sx * cy ) * px + ( cz * cy - sz * sx * sy ) * pz;
    jacobian[1][1] = ( -sz * sy + cz * sx * cy ) * px + ( sz * cy + cz * sx * sy ) * pz;
    jacobian[2][1] = ( -cx * cy ) * px + ( -cx * sy ) * pz;

    jacobian[0][2] = ( -sz * cy - cz * sx * sy ) * px + ( -cz * cx ) * py
      + ( -sz * sy + cz * sx * cy ) * pz;
    jacobian[1][2] = ( cz * cy - sz * sx * sy ) * px + ( -sz * cx ) * py
      + ( cz * sy + sz * sx * cy ) * pz;
    jacobian[2][2] = 0;
    }

  // compute derivatives for the translation part
  unsigned int blockOffset = 3;
  for( unsigned int dim = 0; dim < SpaceDimension; dim++ )
    {
    jacobian[dim][blockOffset + dim] = 1.0;
    }
}

template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::SetComputeZYX (const bool flag)
{
  if( this->m_ComputeZYX != flag )
    {
    this->m_ComputeZYX = flag;
    this->ComputeMatrix();
    // The meaning of the parameters has changed so the transform
    // has been modified even if the parameter values have not.
    this->Modified();
    }
}

// Print self
template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  os << indent << "Euler's angles: AngleX=" << m_AngleX
     << " AngleY=" << m_AngleY
     << " AngleZ=" << m_AngleZ
     << std::endl;
  os << indent << "m_ComputeZYX = " << m_ComputeZYX << std::endl;
}

} // namespace

#endif
<commit_msg>COMP: fix warning about implicit double to bool conversion<commit_after>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  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.txt
 *
 *  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.
 *
 *=========================================================================*/
#ifndef itkEuler3DTransform_hxx
#define itkEuler3DTransform_hxx

#include "itkEuler3DTransform.h"

namespace itk
{
// Constructor with default arguments
template<typename TParametersValueType>
Euler3DTransform<TParametersValueType>
::Euler3DTransform() :
  Superclass(ParametersDimension)
{
  m_ComputeZYX = false;
  m_AngleX = m_AngleY = m_AngleZ = NumericTraits<ScalarType>::ZeroValue();
  this->m_FixedParameters.SetSize(SpaceDimension+1);
  this->m_FixedParameters.Fill(0.0);
}

// Constructor with default arguments
template<typename TParametersValueType>
Euler3DTransform<TParametersValueType>
::Euler3DTransform(const MatrixType & matrix, const OutputPointType & offset)
{
  m_ComputeZYX = false;
  this->SetMatrix(matrix);

  OffsetType off;
  off[0] = offset[0];
  off[1] = offset[1];
  off[2] = offset[2];
  this->SetOffset(off);
  this->m_FixedParameters.SetSize(SpaceDimension+1);
  this->m_FixedParameters.Fill(0.0);
}

// Constructor with arguments
template<typename TParametersValueType>
Euler3DTransform<TParametersValueType>
::Euler3DTransform(unsigned int parametersDimension) :
  Superclass(parametersDimension)
{
  m_ComputeZYX = false;
  m_AngleX = m_AngleY = m_AngleZ = NumericTraits<ScalarType>::ZeroValue();
  this->m_FixedParameters.SetSize(SpaceDimension+1);
  this->m_FixedParameters.Fill(0.0);
}

// Set Angles
template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::SetVarRotation(ScalarType angleX, ScalarType angleY, ScalarType angleZ)
{
  this->m_AngleX = angleX;
  this->m_AngleY = angleY;
  this->m_AngleZ = angleZ;
}

// Set Parameters
template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::SetParameters(const ParametersType & parameters)
{
  itkDebugMacro(<< "Setting parameters " << parameters);

  // Save parameters. Needed for proper operation of TransformUpdateParameters.
  if( &parameters != &(this->m_Parameters) )
    {
    this->m_Parameters = parameters;
    }

  // Set angles with parameters
  m_AngleX = parameters[0];
  m_AngleY = parameters[1];
  m_AngleZ = parameters[2];
  this->ComputeMatrix();

  // Transfer the translation part
  OutputVectorType newTranslation;
  newTranslation[0] = parameters[3];
  newTranslation[1] = parameters[4];
  newTranslation[2] = parameters[5];
  this->SetVarTranslation(newTranslation);
  this->ComputeOffset();

  // Modified is always called since we just have a pointer to the
  // parameters and cannot know if the parameters have changed.
  this->Modified();

  itkDebugMacro(<< "After setting parameters ");
}

// Get Parameters
template<typename TParametersValueType>
const typename Euler3DTransform<TParametersValueType>::ParametersType
& Euler3DTransform<TParametersValueType>
::GetParameters(void) const
  {
  this->m_Parameters[0] = m_AngleX;
  this->m_Parameters[1] = m_AngleY;
  this->m_Parameters[2] = m_AngleZ;
  this->m_Parameters[3] = this->GetTranslation()[0];
  this->m_Parameters[4] = this->GetTranslation()[1];
  this->m_Parameters[5] = this->GetTranslation()[2];

  return this->m_Parameters;
  }

template<typename TParametersValueType>
const typename Euler3DTransform<TParametersValueType>::FixedParametersType &
Euler3DTransform<TParametersValueType>
::GetFixedParameters() const
{
  // Call the superclass GetFixedParameters so that it fills the
  // array, we ignore the returned data and add the additional
  // information to the updated array.
  Superclass::GetFixedParameters();
  this->m_FixedParameters[3] = this->m_ComputeZYX ? 1.0 : 0.0;
  return this->m_FixedParameters;
}

template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::SetFixedParameters(const FixedParametersType & parameters)
{
  InputPointType c;
  for( unsigned int i = 0; i < InputSpaceDimension; i++ )
    {
    c[i] = this->m_FixedParameters[i] = parameters[i];
    }
  this->SetCenter(c);
  //conditional is here for backwards compatibility: the
  //m_ComputeZYX flag was not serialized so it may or may
  //not be included as part of the fixed parameters
  if( parameters.Size() == 4 )
    {
    this->m_FixedParameters[3] = parameters[3];
    this->SetComputeZYX(this->m_FixedParameters[3] != 0.0);
    }
}

// Set Rotational Part
template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::SetRotation(ScalarType angleX, ScalarType angleY, ScalarType angleZ)
{
  m_AngleX = angleX;
  m_AngleY = angleY;
  m_AngleZ = angleZ;
  this->ComputeMatrix();
  this->ComputeOffset();
}

// Compose
template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::SetIdentity(void)
{
  Superclass::SetIdentity();
  m_AngleX = 0;
  m_AngleY = 0;
  m_AngleZ = 0;
}

// Compute angles from the rotation matrix
template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::ComputeMatrixParameters(void)
{
  if( m_ComputeZYX )
    {
    m_AngleY = -std::asin(this->GetMatrix()[2][0]);
    double C = std::cos(m_AngleY);
    if( std::fabs(C) > 0.00005 )
      {
      double x = this->GetMatrix()[2][2] / C;
      double y = this->GetMatrix()[2][1] / C;
      m_AngleX = std::atan2(y, x);
      x = this->GetMatrix()[0][0] / C;
      y = this->GetMatrix()[1][0] / C;
      m_AngleZ = std::atan2(y, x);
      }
    else
      {
      m_AngleX = NumericTraits<ScalarType>::ZeroValue();
      double x = this->GetMatrix()[1][1];
      double y = -this->GetMatrix()[0][1];
      m_AngleZ = std::atan2(y, x);
      }
    }
  else
    {
    m_AngleX = std::asin(this->GetMatrix()[2][1]);
    double A = std::cos(m_AngleX);
    if( std::fabs(A) > 0.00005 )
      {
      double x = this->GetMatrix()[2][2] / A;
      double y = -this->GetMatrix()[2][0] / A;
      m_AngleY = std::atan2(y, x);

      x = this->GetMatrix()[1][1] / A;
      y = -this->GetMatrix()[0][1] / A;
      m_AngleZ = std::atan2(y, x);
      }
    else
      {
      m_AngleZ = NumericTraits<ScalarType>::ZeroValue();
      double x = this->GetMatrix()[0][0];
      double y = this->GetMatrix()[1][0];
      m_AngleY = std::atan2(y, x);
      }
    }
  this->ComputeMatrix();
}

// Compute the matrix
template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::ComputeMatrix(void)
{
  // need to check if angles are in the right order
  const ScalarType cx = std::cos(m_AngleX);
  const ScalarType sx = std::sin(m_AngleX);
  const ScalarType cy = std::cos(m_AngleY);
  const ScalarType sy = std::sin(m_AngleY);
  const ScalarType cz = std::cos(m_AngleZ);
  const ScalarType sz = std::sin(m_AngleZ);
  const ScalarType one = NumericTraits<ScalarType>::OneValue();
  const ScalarType zero = NumericTraits<ScalarType>::ZeroValue();

  Matrix<TParametersValueType, 3, 3> RotationX;
  RotationX[0][0] = one;  RotationX[0][1] = zero; RotationX[0][2] = zero;
  RotationX[1][0] = zero; RotationX[1][1] = cx;   RotationX[1][2] = -sx;
  RotationX[2][0] = zero; RotationX[2][1] = sx;   RotationX[2][2] = cx;

  Matrix<TParametersValueType, 3, 3> RotationY;
  RotationY[0][0] = cy;   RotationY[0][1] = zero; RotationY[0][2] = sy;
  RotationY[1][0] = zero; RotationY[1][1] = one;  RotationY[1][2] = zero;
  RotationY[2][0] = -sy;  RotationY[2][1] = zero; RotationY[2][2] = cy;

  Matrix<TParametersValueType, 3, 3> RotationZ;
  RotationZ[0][0] = cz;   RotationZ[0][1] = -sz;  RotationZ[0][2] = zero;
  RotationZ[1][0] = sz;   RotationZ[1][1] = cz;   RotationZ[1][2] = zero;
  RotationZ[2][0] = zero; RotationZ[2][1] = zero; RotationZ[2][2] = one;

  /** Aply the rotation first around Y then X then Z */
  if( m_ComputeZYX )
    {
    this->SetVarMatrix(RotationZ * RotationY * RotationX);
    }
  else
    {
    // Like VTK transformation order
    this->SetVarMatrix(RotationZ * RotationX * RotationY);
    }
}

template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::ComputeJacobianWithRespectToParameters(const InputPointType & p, JacobianType & jacobian) const
{
  // need to check if angles are in the right order
  const double cx = std::cos(m_AngleX);
  const double sx = std::sin(m_AngleX);
  const double cy = std::cos(m_AngleY);
  const double sy = std::sin(m_AngleY);
  const double cz = std::cos(m_AngleZ);
  const double sz = std::sin(m_AngleZ);

  jacobian.SetSize( 3, this->GetNumberOfLocalParameters() );
  jacobian.Fill(0.0);

  const double px = p[0] - this->GetCenter()[0];
  const double py = p[1] - this->GetCenter()[1];
  const double pz = p[2] - this->GetCenter()[2];

  if( m_ComputeZYX )
    {
    jacobian[0][0] = ( cz * sy * cx + sz * sx ) * py + ( -cz * sy * sx + sz * cx ) * pz;
    jacobian[1][0] = ( sz * sy * cx - cz * sx ) * py + ( -sz * sy * sx - cz * cx ) * pz;
    jacobian[2][0] = ( cy * cx ) * py + ( -cy * sx ) * pz;

    jacobian[0][1] = ( -cz * sy ) * px + ( cz * cy * sx ) * py + ( cz * cy * cx ) * pz;
    jacobian[1][1] = ( -sz * sy ) * px + ( sz * cy * sx ) * py + ( sz * cy * cx ) * pz;
    jacobian[2][1] = ( -cy ) * px + ( -sy * sx ) * py + ( -sy * cx ) * pz;

    jacobian[0][2] = ( -sz * cy ) * px + ( -sz * sy * sx - cz * cx ) * py
      + ( -sz * sy * cx + cz * sx ) * pz;
    jacobian[1][2] = ( cz * cy ) * px + ( cz * sy * sx - sz * cx ) * py + ( cz * sy * cx + sz * sx ) * pz;
    jacobian[2][2] = 0;
    }
  else
    {
    jacobian[0][0] = ( -sz * cx * sy ) * px + ( sz * sx ) * py + ( sz * cx * cy ) * pz;
    jacobian[1][0] = ( cz * cx * sy ) * px + ( -cz * sx ) * py + ( -cz * cx * cy ) * pz;
    jacobian[2][0] = ( sx * sy ) * px + ( cx ) * py + ( -sx * cy ) * pz;

    jacobian[0][1] = ( -cz * sy - sz * sx * cy ) * px + ( cz * cy - sz * sx * sy ) * pz;
    jacobian[1][1] = ( -sz * sy + cz * sx * cy ) * px + ( sz * cy + cz * sx * sy ) * pz;
    jacobian[2][1] = ( -cx * cy ) * px + ( -cx * sy ) * pz;

    jacobian[0][2] = ( -sz * cy - cz * sx * sy ) * px + ( -cz * cx ) * py
      + ( -sz * sy + cz * sx * cy ) * pz;
    jacobian[1][2] = ( cz * cy - sz * sx * sy ) * px + ( -sz * cx ) * py
      + ( cz * sy + sz * sx * cy ) * pz;
    jacobian[2][2] = 0;
    }

  // compute derivatives for the translation part
  unsigned int blockOffset = 3;
  for( unsigned int dim = 0; dim < SpaceDimension; dim++ )
    {
    jacobian[dim][blockOffset + dim] = 1.0;
    }
}

template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>
::SetComputeZYX (const bool flag)
{
  if( this->m_ComputeZYX != flag )
    {
    this->m_ComputeZYX = flag;
    this->ComputeMatrix();
    // The meaning of the parameters has changed so the transform
    // has been modified even if the parameter values have not.
    this->Modified();
    }
}

// Print self
template<typename TParametersValueType>
void
Euler3DTransform<TParametersValueType>::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  os << indent << "Euler's angles: AngleX=" << m_AngleX
     << " AngleY=" << m_AngleY
     << " AngleZ=" << m_AngleZ
     << std::endl;
  os << indent << "m_ComputeZYX = " << m_ComputeZYX << std::endl;
}

} // namespace

#endif
<|endoftext|>


<commit_before>/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.

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

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

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

#include "tensorflow/compiler/jit/tests/auto_clustering_test_helper.h"

#include "absl/strings/numbers.h"
#include "tensorflow/compiler/jit/mark_for_compilation_pass.h"
#include "tensorflow/compiler/jit/xla_cluster_util.h"
#include "tensorflow/compiler/xla/status_macros.h"
#include "tensorflow/compiler/xla/statusor.h"
#include "tensorflow/core/common_runtime/graph_constructor.h"
#include "tensorflow/core/lib/core/status_test_util.h"
#include "tensorflow/core/lib/io/random_inputstream.h"
#include "tensorflow/core/lib/io/zlib_compression_options.h"
#include "tensorflow/core/lib/io/zlib_inputstream.h"
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/platform/test.h"
#include "tensorflow/core/platform/test_benchmark.h"
#include "tensorflow/core/util/port.h"
#include "tensorflow/tools/optimization/optimization_pass_runner.h"

namespace tensorflow {
namespace {
StatusOr<string> SummarizeClustering(const GraphDef& auto_clustered_graph_def) {
  testing::ResetClusterSequenceNumber();
  Graph graph(OpRegistry::Global());
  GraphConstructorOptions graph_opts;
  graph_opts.expect_device_spec = true;
  graph_opts.allow_internal_ops = true;
  TF_RETURN_IF_ERROR(
      ConvertGraphDefToGraph(graph_opts, auto_clustered_graph_def, &graph));

  // cluster_id -> (operation name -> # of operations)
  const int kNoCluster = -1;
  std::map<int, std::map<string, int>> clusters;
  std::map<int, int> cluster_size;
  int clustered_nodes = 0;
  for (Node* n : graph.op_nodes()) {
    int cluster = kNoCluster;
    if (absl::optional<absl::string_view> maybe_cluster =
            GetXlaClusterForNode(*n)) {
      maybe_cluster->remove_prefix(absl::string_view("cluster_").size());
      TF_RET_CHECK(absl::SimpleAtoi(*maybe_cluster, &cluster));
      clustered_nodes++;
    }
    clusters[cluster][n->type_string()]++;
    cluster_size[cluster]++;
  }

  string result =
      absl::StrCat("Clustered nodes: ", clustered_nodes,
                   "\nUnclustered nodes: ", cluster_size[kNoCluster],
                   "\nNumber of clusters: ", clusters.size() - 1, "\n\n");
  for (const auto& pair : clusters) {
    if (pair.first == kNoCluster) {
      absl::StrAppend(&result, "unclustered");
    } else {
      absl::StrAppend(&result, "cluster ", pair.first);
    }

    absl::StrAppend(&result, " size ", cluster_size[pair.first], "\n");

    for (const auto& ops_and_counts : pair.second) {
      absl::StrAppend(&result, " ", ops_and_counts.first, " ",
                      ops_and_counts.second, "\n");
    }
  }

  return result;
}

Status AssertGraphDefIsUnclustered(const GraphDef& graphdef) {
  const char* kXlaClusterAttr = "_XlaCluster";
  const char* kXlaAlreadyClusteredAttr = "_XlaAlreadyClustered";

  for (const NodeDef& node : graphdef.node()) {
    if (node.attr().count(kXlaClusterAttr) ||
        node.attr().count(kXlaAlreadyClusteredAttr)) {
      return errors::InvalidArgument(
          "Input files are already clustered, you probably copied in "
          "mark_for_compilation_<n>.pbtxt when you should have copied in "
          "before_mark_for_compilation_<n>.pbtxt");
    }
  }

  return Status::OK();
}

Status ReadTextProtoFromString(Env* env, const string& data,
                               ::tensorflow::protobuf::Message* proto) {
  if (!::tensorflow::protobuf::TextFormat::ParseFromString(data, proto)) {
    return errors::DataLoss("Can't parse input data as text proto");
  }
  return Status::OK();
}
}  // namespace

Status AutoClusteringTest::RunAutoClusteringTestImpl(
    GraphDef graphdef, absl::string_view golden_summary_file_path) {
  if (!IsGoogleCudaEnabled()) {
    // There is some slight change in the clustering decisions under
    // --config=cuda.  I have not looked closely at why that is happening, but
    // most likely some of the partial declustering passes behave differently
    // with --config=cuda because of different HostMemory.  So for now only test
    // the non-CUDA config, under the assumption that regressions with
    // --config=cuda would also be detected as regressions without
    // --config=cuda.

    LOG(INFO) << "Not running "
              << ::testing::UnitTest::GetInstance()->current_test_info()->name()
              << " since test was not built with --config=cuda";
    return Status::OK();
  }

  TF_RETURN_IF_ERROR(AssertGraphDefIsUnclustered(graphdef));

  OptimizationPassRunner runner;
  TF_RETURN_IF_ERROR(runner.SetJitLevel(tensorflow::OptimizerOptions::ON_2));
  TF_RETURN_IF_ERROR(runner.AddCpus(32));
  TF_RETURN_IF_ERROR(runner.AddGpus(8));

  for (absl::string_view auto_clustering_pass :
       {"CloneConstantsForBetterClusteringPass", "MarkForCompilationPass",
        "IncreaseDynamismForAutoJitPass", "PartiallyDeclusterPass"}) {
    GraphDef next;
    TF_RETURN_IF_ERROR(
        runner.Run(auto_clustering_pass, std::move(graphdef), &next));
    graphdef = std::move(next);
  }

  TF_ASSIGN_OR_RETURN(string clustering_summary, SummarizeClustering(graphdef));

  // To update golden files flip this to true and run
  //
  // bazel test --test_strategy=local \
  //   tensorflow/compiler/jit/tests:auto_clustering_test
  bool update_golden = false;
  if (update_golden) {
    TF_RETURN_IF_ERROR(WriteStringToFile(
        Env::Default(), string(golden_summary_file_path), clustering_summary));
  }

  string golden_file_contents;
  TF_RETURN_IF_ERROR(ReadFileToString(
      Env::Default(), string(golden_summary_file_path), &golden_file_contents));

  EXPECT_EQ(golden_file_contents, clustering_summary);

  return Status::OK();
}

Status AutoClusteringTest::RunAutoClusteringTestWithPbtxt(
    absl::string_view pbtxt_file_path,
    absl::string_view golden_summary_file_path) {
  GraphDef graphdef;
  TF_RETURN_IF_ERROR(
      ReadTextProto(Env::Default(), string(pbtxt_file_path), &graphdef));
  return RunAutoClusteringTestImpl(std::move(graphdef),
                                   golden_summary_file_path);
}

Status AutoClusteringTest::RunAutoClusteringTestWithGzippedPbtxt(
    absl::string_view gzipped_pbtxt_file_path,
    absl::string_view golden_summary_file_path) {
  Env* env = Env::Default();
  std::unique_ptr<RandomAccessFile> file_reader;
  TF_RETURN_IF_ERROR(
      env->NewRandomAccessFile(string(gzipped_pbtxt_file_path), &file_reader));
  std::unique_ptr<io::RandomAccessInputStream> input_stream(
      new io::RandomAccessInputStream(file_reader.get()));
  constexpr int k_buffer_size = 256 << 10;  // 256kb
  io::ZlibInputStream in(input_stream.get(),
                         /*input_buffer_bytes=*/k_buffer_size,
                         /*output_buffer_bytes=*/k_buffer_size,
                         io::ZlibCompressionOptions::GZIP());
  tstring decompressed_pbtxt_string;
  Status s = in.ReadNBytes(INT_MAX, &decompressed_pbtxt_string);
  if (!s.ok() && !errors::IsOutOfRange(s)) {
    // OutOfRange is fine since we set the number of read bytes to INT_MAX.
    // Only return other kinds of errors.
    return s;
  }

  GraphDef graphdef;
  TF_RETURN_IF_ERROR(ReadTextProtoFromString(
      Env::Default(), decompressed_pbtxt_string, &graphdef));
  return RunAutoClusteringTestImpl(std::move(graphdef),
                                   golden_summary_file_path);
}

#if defined(PLATFORM_GOOGLE)
Status BenchmarkMarkForCompilation(absl::string_view graph_def_path,
                                   benchmark::State& state) {
  GraphDef graph_def;
  TF_RETURN_IF_ERROR(
      ReadTextProto(Env::Default(), string(graph_def_path), &graph_def));

  OptimizationPassRunner runner;
  TF_RETURN_IF_ERROR(runner.SetJitLevel(tensorflow::OptimizerOptions::ON_2));
  TF_RETURN_IF_ERROR(runner.AddCpus(32));
  TF_RETURN_IF_ERROR(runner.AddGpus(8));

  for (auto _ : state) {
    StopBenchmarkTiming();
    GraphDef result;
    GraphDef graph_def_copy = graph_def;
    StartBenchmarkTiming();
    TF_RETURN_IF_ERROR(runner.Run("MarkForCompilationPass",
                                  std::move(graph_def_copy), &result));
  }

  return Status::OK();
}
#endif  // PLATFORM_GOOGLE

}  // namespace tensorflow
<commit_msg>Remove calls to deprecated Stop/Start timer functions in benchmarks.<commit_after>/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.

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

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

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

#include "tensorflow/compiler/jit/tests/auto_clustering_test_helper.h"

#include "absl/strings/numbers.h"
#include "tensorflow/compiler/jit/mark_for_compilation_pass.h"
#include "tensorflow/compiler/jit/xla_cluster_util.h"
#include "tensorflow/compiler/xla/status_macros.h"
#include "tensorflow/compiler/xla/statusor.h"
#include "tensorflow/core/common_runtime/graph_constructor.h"
#include "tensorflow/core/lib/core/status_test_util.h"
#include "tensorflow/core/lib/io/random_inputstream.h"
#include "tensorflow/core/lib/io/zlib_compression_options.h"
#include "tensorflow/core/lib/io/zlib_inputstream.h"
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/platform/test.h"
#include "tensorflow/core/platform/test_benchmark.h"
#include "tensorflow/core/util/port.h"
#include "tensorflow/tools/optimization/optimization_pass_runner.h"

namespace tensorflow {
namespace {
StatusOr<string> SummarizeClustering(const GraphDef& auto_clustered_graph_def) {
  testing::ResetClusterSequenceNumber();
  Graph graph(OpRegistry::Global());
  GraphConstructorOptions graph_opts;
  graph_opts.expect_device_spec = true;
  graph_opts.allow_internal_ops = true;
  TF_RETURN_IF_ERROR(
      ConvertGraphDefToGraph(graph_opts, auto_clustered_graph_def, &graph));

  // cluster_id -> (operation name -> # of operations)
  const int kNoCluster = -1;
  std::map<int, std::map<string, int>> clusters;
  std::map<int, int> cluster_size;
  int clustered_nodes = 0;
  for (Node* n : graph.op_nodes()) {
    int cluster = kNoCluster;
    if (absl::optional<absl::string_view> maybe_cluster =
            GetXlaClusterForNode(*n)) {
      maybe_cluster->remove_prefix(absl::string_view("cluster_").size());
      TF_RET_CHECK(absl::SimpleAtoi(*maybe_cluster, &cluster));
      clustered_nodes++;
    }
    clusters[cluster][n->type_string()]++;
    cluster_size[cluster]++;
  }

  string result =
      absl::StrCat("Clustered nodes: ", clustered_nodes,
                   "\nUnclustered nodes: ", cluster_size[kNoCluster],
                   "\nNumber of clusters: ", clusters.size() - 1, "\n\n");
  for (const auto& pair : clusters) {
    if (pair.first == kNoCluster) {
      absl::StrAppend(&result, "unclustered");
    } else {
      absl::StrAppend(&result, "cluster ", pair.first);
    }

    absl::StrAppend(&result, " size ", cluster_size[pair.first], "\n");

    for (const auto& ops_and_counts : pair.second) {
      absl::StrAppend(&result, " ", ops_and_counts.first, " ",
                      ops_and_counts.second, "\n");
    }
  }

  return result;
}

Status AssertGraphDefIsUnclustered(const GraphDef& graphdef) {
  const char* kXlaClusterAttr = "_XlaCluster";
  const char* kXlaAlreadyClusteredAttr = "_XlaAlreadyClustered";

  for (const NodeDef& node : graphdef.node()) {
    if (node.attr().count(kXlaClusterAttr) ||
        node.attr().count(kXlaAlreadyClusteredAttr)) {
      return errors::InvalidArgument(
          "Input files are already clustered, you probably copied in "
          "mark_for_compilation_<n>.pbtxt when you should have copied in "
          "before_mark_for_compilation_<n>.pbtxt");
    }
  }

  return Status::OK();
}

Status ReadTextProtoFromString(Env* env, const string& data,
                               ::tensorflow::protobuf::Message* proto) {
  if (!::tensorflow::protobuf::TextFormat::ParseFromString(data, proto)) {
    return errors::DataLoss("Can't parse input data as text proto");
  }
  return Status::OK();
}
}  // namespace

Status AutoClusteringTest::RunAutoClusteringTestImpl(
    GraphDef graphdef, absl::string_view golden_summary_file_path) {
  if (!IsGoogleCudaEnabled()) {
    // There is some slight change in the clustering decisions under
    // --config=cuda.  I have not looked closely at why that is happening, but
    // most likely some of the partial declustering passes behave differently
    // with --config=cuda because of different HostMemory.  So for now only test
    // the non-CUDA config, under the assumption that regressions with
    // --config=cuda would also be detected as regressions without
    // --config=cuda.

    LOG(INFO) << "Not running "
              << ::testing::UnitTest::GetInstance()->current_test_info()->name()
              << " since test was not built with --config=cuda";
    return Status::OK();
  }

  TF_RETURN_IF_ERROR(AssertGraphDefIsUnclustered(graphdef));

  OptimizationPassRunner runner;
  TF_RETURN_IF_ERROR(runner.SetJitLevel(tensorflow::OptimizerOptions::ON_2));
  TF_RETURN_IF_ERROR(runner.AddCpus(32));
  TF_RETURN_IF_ERROR(runner.AddGpus(8));

  for (absl::string_view auto_clustering_pass :
       {"CloneConstantsForBetterClusteringPass", "MarkForCompilationPass",
        "IncreaseDynamismForAutoJitPass", "PartiallyDeclusterPass"}) {
    GraphDef next;
    TF_RETURN_IF_ERROR(
        runner.Run(auto_clustering_pass, std::move(graphdef), &next));
    graphdef = std::move(next);
  }

  TF_ASSIGN_OR_RETURN(string clustering_summary, SummarizeClustering(graphdef));

  // To update golden files flip this to true and run
  //
  // bazel test --test_strategy=local \
  //   tensorflow/compiler/jit/tests:auto_clustering_test
  bool update_golden = false;
  if (update_golden) {
    TF_RETURN_IF_ERROR(WriteStringToFile(
        Env::Default(), string(golden_summary_file_path), clustering_summary));
  }

  string golden_file_contents;
  TF_RETURN_IF_ERROR(ReadFileToString(
      Env::Default(), string(golden_summary_file_path), &golden_file_contents));

  EXPECT_EQ(golden_file_contents, clustering_summary);

  return Status::OK();
}

Status AutoClusteringTest::RunAutoClusteringTestWithPbtxt(
    absl::string_view pbtxt_file_path,
    absl::string_view golden_summary_file_path) {
  GraphDef graphdef;
  TF_RETURN_IF_ERROR(
      ReadTextProto(Env::Default(), string(pbtxt_file_path), &graphdef));
  return RunAutoClusteringTestImpl(std::move(graphdef),
                                   golden_summary_file_path);
}

Status AutoClusteringTest::RunAutoClusteringTestWithGzippedPbtxt(
    absl::string_view gzipped_pbtxt_file_path,
    absl::string_view golden_summary_file_path) {
  Env* env = Env::Default();
  std::unique_ptr<RandomAccessFile> file_reader;
  TF_RETURN_IF_ERROR(
      env->NewRandomAccessFile(string(gzipped_pbtxt_file_path), &file_reader));
  std::unique_ptr<io::RandomAccessInputStream> input_stream(
      new io::RandomAccessInputStream(file_reader.get()));
  constexpr int k_buffer_size = 256 << 10;  // 256kb
  io::ZlibInputStream in(input_stream.get(),
                         /*input_buffer_bytes=*/k_buffer_size,
                         /*output_buffer_bytes=*/k_buffer_size,
                         io::ZlibCompressionOptions::GZIP());
  tstring decompressed_pbtxt_string;
  Status s = in.ReadNBytes(INT_MAX, &decompressed_pbtxt_string);
  if (!s.ok() && !errors::IsOutOfRange(s)) {
    // OutOfRange is fine since we set the number of read bytes to INT_MAX.
    // Only return other kinds of errors.
    return s;
  }

  GraphDef graphdef;
  TF_RETURN_IF_ERROR(ReadTextProtoFromString(
      Env::Default(), decompressed_pbtxt_string, &graphdef));
  return RunAutoClusteringTestImpl(std::move(graphdef),
                                   golden_summary_file_path);
}

#if defined(PLATFORM_GOOGLE)
Status BenchmarkMarkForCompilation(absl::string_view graph_def_path,
                                   benchmark::State& state) {
  GraphDef graph_def;
  TF_RETURN_IF_ERROR(
      ReadTextProto(Env::Default(), string(graph_def_path), &graph_def));

  OptimizationPassRunner runner;
  TF_RETURN_IF_ERROR(runner.SetJitLevel(tensorflow::OptimizerOptions::ON_2));
  TF_RETURN_IF_ERROR(runner.AddCpus(32));
  TF_RETURN_IF_ERROR(runner.AddGpus(8));

  for (auto _ : state) {
    state.PauseTiming();
    GraphDef result;
    GraphDef graph_def_copy = graph_def;
    state.ResumeTiming();
    TF_RETURN_IF_ERROR(runner.Run("MarkForCompilationPass",
                                  std::move(graph_def_copy), &result));
  }

  return Status::OK();
}
#endif  // PLATFORM_GOOGLE

}  // namespace tensorflow
<|endoftext|>