/** @file

    $Id$

    $Date$
    $Author$

    @copy &copy; Marc W&auml;ckerlin
    @license LGPL, see file <a href="license.html">COPYING</a>

*/

#ifndef __MRW__STDEXT_HPP__
#define __MRW__STDEXT_HPP__

#include <string>
#include <iostream>
#include <algorithm>
#include <mrw/checkcxx11.hxx>

namespace mrw {

  /** @addtogroup StdExt
   */
  //@{

  /** @defgroup stdextFunction Useful Global Functions

       - @ref getline read exactly one line from a stream
       - @ref ifelse implements a dual <code>?:</code> operator, like:
              <code>a&nbsp;?&nbsp;a&nbsp;:&nbsp;b</code>      

      @section stdextGetline Read Line
      
      The global functions mrw::getline read exactly one line from a
      stream, without the need of a buffer. It is therefore guaranteed
      that a whole line is read, regardless of the length of the
      line. The result is returned as std::string.

      @code
      // first syntax returns a string:
      std::string line(mrw::getline(std::cin));
      // second syntax returns the stream:
      while (mrw::getline(std::cin, line))
        std::cout<<"Read: "<<line<<std::endl;
      @endcode
  */
  //@{

  /** @defgroup getmax Get the Maximum of Values */
  //@{
  //! Get the maximum out of two values
  template<typename T> T& max(T& t1, T& t2) {
    return t2<t1?t1:t2;
  }
  //! Get the maximum out of two values
  template<typename T> const T& max(const T& t1, const T& t2) {
    return t2<t1?t1:t2;
  }
#ifdef MRW__OLD_PRE11_COMPILER
  //! Get the maximum out of three values
  template<typename T> T& max(T& t1, T& t2, T& t3) {
    return max(t1, max(t2, t3));
  }
  //! Get the maximum out of four values
  template<typename T> T& max(T& t1, T& t2, T& t3, T& t4) {
    return max(max(t1, t2), max(t3, t4));
  }
  //! Get the maximum out of five values
  template<typename T> T& max(T& t1, T& t2, T& t3, T& t4, T& t5) {
    return max(max(t1, t2), max(t3, t4, t5));
  }
  //! Get the maximum out of six values
  template<typename T> T& max(T& t1, T& t2, T& t3, T& t4, T& t5, T& t6) {
    return max(max(t1, t2, t3), max(t4, t5, t6));
  }
  //! Get the maximum out of three values
  template<typename T> const T& max(const T& t1, const T& t2, const T& t3) {
    return max(t1, max(t2, t3));
  }
  //! Get the maximum out of four values
  template<typename T> const T& max(const T& t1, const T& t2, const T& t3,
                                    const T& t4) {
    return max(max(t1, t2), max(t3, t4));
  }
  //! Get the maximum out of five values
  template<typename T> const T& max(const T& t1, const T& t2, const T& t3,
                                    const T& t4, const T& t5) {
    return max(max(t1, t2), max(t3, t4, t5));
  }
  //! Get the maximum out of six values
  template<typename T> const T& max(const T& t1, const T& t2, const T& t3,
                                    const T& t4, const T& t5, const T& t6) {
    return max(max(t1, t2, t3), max(t4, t5, t6));
  }
#else
  //! Get the maximum out of any number of arguments
  template<typename T, typename... T2> T& max(T& t1, T& t2, T2&... args) {
    return max(t1, max(t2, args...));
  }
  //! Get the maximum out of any number of arguments
  template<typename T, typename... T2> const T& max(const T& t1, const T& t2,
                                                    const T2&... args) {
    return max(t1, max(t2, args...));
  }
#endif
  //@}

  /** @defgroup getmin Get the Minimum of Values */
  //@{
  //! Get the minimum out of two values
  template<typename T> T& min(T& t1, T& t2) {
    return t1<t2?t1:t2;
  }
  //! Get the minimum out of two values
  template<typename T> const T& min(const T& t1, const T& t2) {
    return t1<t2?t1:t2;
  }
#ifdef MRW__OLD_PRE11_COMPILER
  //! Get the minimum out of three values
  template<typename T> T& min(T& t1, T& t2, T& t3) {
    return min(t1, min(t2, t3));
  }
  //! Get the minimum out of four values
  template<typename T> T& min(T& t1, T& t2, T& t3, T& t4) {
    return min(min(t1, t2), min(t3, t4));
  }
  //! Get the minimum out of five values
  template<typename T> T& min(T& t1, T& t2, T& t3, T& t4, T& t5) {
    return min(min(t1, t2), min(t3, t4, t5));
  }
  //! Get the minimum out of six values
  template<typename T> T& min(T& t1, T& t2, T& t3, T& t4, T& t5, T& t6) {
    return min(min(t1, t2, t3), min(t4, t5, t6));
  }
  //! Get the minimum out of three values
  template<typename T> const T& min(const T& t1, const T& t2, const T& t3) {
    return min(t1, min(t2, t3));
  }
  //! Get the minimum out of four values
  template<typename T> const T& min(const T& t1, const T& t2, const T& t3,
                                    const T& t4) {
    return min(min(t1, t2), min(t3, t4));
  }
  //! Get the minimum out of five values
  template<typename T> const T& min(const T& t1, const T& t2, const T& t3,
                                    const T& t4, const T& t5) {
    return min(min(t1, t2), min(t3, t4, t5));
  }
  //! Get the minimum out of six values
  template<typename T> const T& min(const T& t1, const T& t2, const T& t3,
                                    const T& t4, const T& t5, const T& t6) {
    return min(min(t1, t2, t3), min(t4, t5, t6));
  }
#else
  //! Get the minimum out of any number of arguments
  template<typename T, typename... T2> T& min(T& t1, T& t2, T2&... args) {
    return min(t1, min(t2, args...));
  }
  //! Get the minimum out of any number of arguments
  template<typename T, typename... T2> const T& min(const T& t1, const T& t2,
                                                    const T2&... args) {
    return min(t1, min(t2, args...));
  }
#endif
  //@}

  /** @brief Dual <code>?:</code> operation:
             <code>a&nbsp;?&nbsp;a&nbsp;:&nbsp;b</code>

      If the first parameter is not @c 0, then it is returned,
      otherwise the second parameter is returned. It is equivalent to
      the construct: <code>a&nbsp;?&nbsp;a&nbsp;:&nbsp;b</code>, but without
      the side effect of duplicated evaluation of parameter
      <code>a</code>.

      Older gcc compiler implemented the non standard extension of
      <code>a?:b</code>, which was exactly the same.

      @param a criteria that is checked and returned if not @c 0
      @param b alternative value to be returned if @c a is @c 0
      @return <code>a&nbsp;?&nbsp;a&nbsp;:&nbsp;b</code>
      @pre \#include <mrw/stdext.hxx>
      @pre @c T must be convertible to @c bool
  */
  template<typename T> const T& ifelse(const T& a, const T& b) {
    return a?a:b;
  }

  /** @brief Dual <code>?:</code> operation:
             <code>a&nbsp;?&nbsp;a&nbsp;:&nbsp;b</code>

      @copydoc ifelse(const T& a, const T& b) */
  template<typename T> const T* ifelse(const T* a, const T* b) {
    return a?a:b;
  }

  /** @brief read one line from a stream

      Reads one line from a stream, up to delimiter @c d.
      The delimiter is not appended to the string.

      @param is the stream to read from
      @param d the end of line delimiter
      @return the line read from the stream
      @pre \#include <mrw/stdext.hxx>
  */
  std::string getline(std::istream& is, char d = '\n');

  /** @brief read one line from a stream

      Reads one line from a stream, up to delimiter @c d.
      The delimiter is not appended to the string.

      @param is the stream to read from
      @param s the string to place the line in
      @param d the end of line delimiter
      @return @c s: the line read from the stream
      @return the stream after extraction of line
      @pre \#include <mrw/stdext.hxx>
  */
  std::istream& getline(std::istream& is, std::string& s, char d = '\n');

  //@}
  //@}
}

#endif
