/*
 * Copyright (c) 2019 Lawrence Livermore National Laboratory
 *
 * SPDX-License-Identifier: GPL-2.0-only
 *
 * Author: Mathew Bielejeski <bielejeski1@llnl.gov>
 */

#ifndef NS3_LENGTH_H_
#define NS3_LENGTH_H_

#include "attribute-helper.h"
#include "attribute.h"

#ifdef HAVE_BOOST
#include <boost/units/quantity.hpp>
#include <boost/units/systems/si.hpp>
#endif

#include <istream>
#include <limits>
#include <optional>
#include <ostream>
#include <string>

/**
 * @file
 * @ingroup length
 * Declaration of ns3::Length class
 */

/**
 * ns3 namespace
 */
namespace ns3
{

/**
 * @ingroup core
 * @defgroup length Length
 *
 * Management of lengths in real world units.
 *
 */

/**
 * @ingroup length
 *
 * Represents a length in meters
 *
 * The purpose of this class is to replace the use of raw numbers (ints, doubles)
 * that have implicit lengths with a class that represents lengths with an explicit
 * unit.  Using raw values with implicit units can lead to bugs when a value is
 * assumed to represent one unit but actually represents another.  For example,
 * assuming a value represents a length in meters when it in fact represents a
 * length in kilometers.
 *
 * The Length class prevents this confusion by storing values internally as
 * doubles representing lengths in Meters and providing conversion functions
 * to convert to/from other length units (kilometers, miles, etc.).
 *
 * ## Supported Units ##
 *
 * Conversion to and from meters is supported for the following units:
 *
 * ### Metric ###
 * - nanometer
 * - micrometer
 * - millimeter
 * - centimeter
 * - meter
 * - kilometer
 * - nautical mile
 *
 * ### US Customary ###
 * - inch
 * - foot
 * - yard
 * - mile
 *
 * ## Construction ##
 *
 * Length objects can be constructed in a number of different ways
 *
 * ### String Constructor ###
 *
 * The string constructor parses strings with the format \<number\>\<unit\> or
 * \<number\> \<unit\> and creates the equivalent Length value in meters.
 * \<unit\> can be the full name of the unit (nanometer, kilometer, foot, etc.) or the
 * abbreviated name (nm, km, ft, etc.).
 *
 * @code
 *   //construct lengths from strings
 *   Length foot ("1foot");
 *   Length cm ("1cm");
 *   Length mile ("1 mile");
 *   Length km ("1 km");
 *
 *   //nautical mile is special because it is two words
 *   Length nautmile ("1 nautical mile");
 *   Length nmi ("1 nmi");
 * @endcode
 *
 * ### Quantity Constructor ###
 *
 * Quantity is a class that describes a value and a unit type. For example, meter
 * is a unit while 5 meters is a quantity.
 * The Length constructor takes the quantity instance and converts it to the
 * equivalent value in meters.
 *
 * @code
 *   //construct a Length representing 2 kilometers
 *   Length::Quantity q (2, Length::Unit::Kilometer);
 *   Length km(q);
 * @endcode
 *
 * ### Value/Unit Constructor
 *
 * Two constructors are provided which take a value and a unit as separate
 * parameters.  The difference between the two constructors is that one takes
 * the unit as a string and the other takes the unit as a Length::Unit value.
 * An assertion is triggered if the string does not map to a valid
 * Length::Unit
 *
 * @code
 *   //These two constructors are equivalent
 *   Length l1 (1, "cm");
 *   Length l2 (1, Length::Unit::Centimeter);
 * @endcode
 *
 * ### boost::units
 *
 * If the boost::units library is discovered during ns-3 configuration an
 * additional constructor is enabled which allows constructing Length objects
 * from boost::unit quantities.
 *
 * @code
 *   //construct length from a boost::units quantity
 *   boost::units::quantity<boost::units::si::length> q
 *       = 5 * boost::units::si::meter;
 *   Length meters (q);
 * @endcode
 *
 * ## Arithmetic Operations ##
 *
 * The following arithmetic operations are supported:
 *
 * ### Addition ###
 *
 *   Addition is between two Length instances
 *
 * @code
 *   std::cout << Length(1, Length::Unit::Meter) + Length (2, Length::Unit::Meter);
 *   // output: "3 m"
 * @endcode
 *
 * ### Subtraction ###
 *
 *   Subtraction is between two Length instances
 *
 * @code
 *   std::cout << Length(3, Length::Unit::Meter) - Length (2, Length::Unit::Meter);
 *   // output: "1 m"
 * @endcode
 *
 * ### Multiplication ###
 *
 *   Multiplication is only supported between a Length and a unitless scalar value
 *
 * @code
 *   std::cout << Length(1, Length::Unit::Meter) * 5;  // output: "5 m"
 *
 *   std::cout << 5 * Length (1, Length::Unit::Meter);  // output: "5 m"
 * @endcode
 *
 *
 * ### Division ###
 *
 *   Division can be between a Length and a scalar or two Length objects.
 *
 *   Division between a Length and a scalar returns a new Length.
 *
 *   Division between two Length objects returns a unitless value.
 *
 * @code
 *   std::cout << Length(5, Length::Unit::Meter) / 5;  // output: "1 m"
 *
 *   std::cout << Length (5, Length::Unit::Meter) / Length (5, Length::Unit::Meter);
 *   // output: 1
 * @endcode
 *
 * ## Comparison Operations ##
 *
 * All the usual arithmetic comparison operations (=, !=, <, <=, >, >=) are supported.
 * There are two forms of comparison operators: free function and member function.
 * The free functions define =, !=, <, <=, >, >= and perform exact comparisons of
 * the underlying double values.  The Length class provides comparison
 * operator functions (IsEqual, IsLess, IsGreater, etc.) which accept an
 * additional tolerance value to control how much the underlying double values
 * must match when performing the comparison.
 *
 * @code
 *   //check for exact match
 *   Length l (5, Length::Unit::Meter);
 *
 *   bool match = (l == l);  // match is true
 *
 *   Length v1 (0.02, Length::Unit::Meter);
 *   Length v2 (0.022, Length::Unit::Meter);
 *
 *   match = (v1 == v2);  // match is false
 *
 *   double tolerance = 0.01;
 *   bool mostly_match = v1.IsEqual (v2, tolerance);  // mostly_match is true
 * @endcode
 *
 * ## Serialization ##
 *
 * The Length class supports serialization using the << and >> operators.
 * By default the output serialization is in meters. Use Length::As to output
 * the Length value in a different unit.
 *
 * @code
 *   Length m(5, Length::Unit::Meter);
 *
 *   std::cout << m << ", "
 *             << m.As(Length::Unit::Kilometer) << ", "
 *             << m.As(Length::Unit::Foot);
 *   //output: 5 m, 0.005 km, 16.4042 ft
 * @endcode
 */
class Length
{
  public:
    /**
     * Units of length in various measurement systems that are supported by the
     * Length class
     */
    enum Unit : uint16_t
    {
        // Metric Units
        Nanometer = 1, //!< 1e<sup>-9</sup> meters
        Micrometer,    //!< 1e<sup>-6</sup> meters
        Millimeter,    //!< 1e<sup>-3</sup> meters
        Centimeter,    //!< 1e<sup>-2</sup> meters
        Meter,         //!< Base length unit in metric system
        Kilometer,     //!< 1e<sup>3</sup> meters
        NauticalMile,  //!< 1,852 meters

        // US Customary Units
        Inch, //!< 1/12 of a foot
        Foot, //!< Base length unit in US customary system
        Yard, //!< 3 feet
        Mile  //!< 5,280 feet
    };

    /**
     * An immutable class which represents a value in a specific length unit
     */
    class Quantity
    {
      public:
        /**
         * Constructor
         *
         * @param value Length value
         * @param unit Length unit of the value
         */
        Quantity(double value, Length::Unit unit)
            : m_value(value),
              m_unit(unit)
        {
        }

        /**
         * Copy Constructor
         */
        Quantity(const Quantity&) = default;

        /**
         * Move Constructor
         */
        Quantity(Quantity&&) = default;

        /**
         * Destructor
         */
        ~Quantity() = default;

        /**
         * Copy Assignment Operator
         * @param [in] other The source to copy from.
         * @returns this.
         */
        Quantity& operator=(const Quantity& other) = default;

        /**
         * Move Assignment Operator
         * @param [in] other The source to move from.
         * @returns this.
         */
        Quantity& operator=(Quantity&& other) = default;

        /**
         * The value of the quantity
         *
         * @return The value of this quantity
         */
        double Value() const
        {
            return m_value;
        }

        /**
         * The unit of the quantity
         *
         * @return The unit of this quantity
         */
        Length::Unit Unit() const
        {
            return m_unit;
        }

      private:
        double m_value;      //!< Value of the length
        Length::Unit m_unit; //!< unit of length of the value
    };

    /**
     * Default tolerance value used for the member comparison functions (IsEqual,
     * IsLess, etc.)
     *
     * The default tolerance is set to epsilon which is defined as the difference
     * between 1.0 and the next value that can be represented by a double.
     */
    static constexpr double DEFAULT_TOLERANCE = std::numeric_limits<double>::epsilon();

    /**
     * Attempt to construct a Length object from a value and a unit string
     *
     * \p unit can either be the full name of the unit (meter, kilometer, mile, etc.)
     * or the symbol of the unit (m, km, mi, etc.)
     *
     * This function will return false if \p unit does not map to a known type.
     *
     * @param value Numeric value of the new length
     * @param unit Unit that the value represents
     *
     * @return A std::optional object containing the Length object constructed from
     * the given value and unit, if the attempt to construct the Length object was
     * successful.
     */
    static std::optional<Length> TryParse(double value, const std::string& unit);

    /**
     * Default Constructor
     *
     * Initialize with a value of 0 meters.
     */
    Length();

    /**
     * String Constructor
     *
     * Parses \p text and initializes the value with the parsed result.
     *
     * The expected format of \p text is \<number\> \<unit\> or \<number\>\<unit\>
     *
     * @param text Serialized length value
     */
    Length(const std::string& text);

    /**
     * Construct a Length object from a value and a unit string
     *
     * \p unit can either be the full name of the unit (meter, kilometer, mile, etc.)
     * or the symbol of the unit (m, km, mi, etc.)
     *
     * @warning NS_FATAL_ERROR is called if \p unit is not a valid unit string.
     * @warning Use Length::TryParse to parse potentially bad values without terminating.
     *
     * @param value Numeric value of the new length
     * @param unit Unit that the value represents
     */
    Length(double value, const std::string& unit);

    /**
     * Construct a Length object from a value and a unit
     *
     * @warning NS_FATAL_ERROR is called if \p unit is not valid.
     * @warning Use Length::TryParse to parse potentially bad values without terminating.
     *
     * @param value Numeric value of the new length
     * @param unit Length unit of the value
     */
    Length(double value, Length::Unit unit);

    /**
     * Construct a Length object from a Quantity
     *
     * @param quantity Quantity representing a length value and unit
     */
    Length(Quantity quantity);

#ifdef HAVE_BOOST_UNITS
    /**
     * Construct a Length object from a boost::units::quantity
     *
     * @note The boost::units:quantity must contain a unit that derives from
     * the length dimension.  Passing a quantity with a Unit that is not a length
     * unit will result in a compile time error
     *
     * @tparam U A boost::units length unit
     * @tparam T Numeric data type of the quantity value
     *
     * @param quantity A boost::units length quantity
     */
    template <class U, class T>
    explicit Length(boost::units::quantity<U, T> quantity);
#endif

    /**
     * Copy Constructor
     *
     * Initialize an object with the value from \p other.
     *
     * @param other Length object to copy
     */
    Length(const Length& other) = default;

    /**
     * Move Constructor
     *
     * Initialize an object with the value from \p other.
     *
     * After the move completes, \p other is left in an undefined but
     * usable state.
     *
     * @param other Length object to move
     */
    Length(Length&& other) = default;

    /**
     * Destructor
     */
    ~Length() = default;

    /**
     * Copy Assignment operator
     *
     * Replace the current value with the value from \p other
     *
     * @param other Length object to copy
     *
     * @return Reference to the updated object
     */
    Length& operator=(const Length& other) = default;

    /**
     * Move Assignment operator
     *
     * Replace the current value with the value from \p other
     * After the move, \p other is left in an undefined but valid state
     *
     * @param other Length object to move
     *
     * @return Reference to the updated object
     */
    Length& operator=(Length&& other) = default;

    /**
     * Assignment operator
     *
     * Replace the current value with the value from \p q
     *
     * @param q Quantity holding the value to assign
     *
     * @return Reference to the updated object
     */
    Length& operator=(const Length::Quantity& q);

    /**
     * Check if \p other is equal in value to this instance.
     *
     * @param other Value to compare against
     * @param tolerance Smallest difference allowed between the two
     * values to still be considered equal
     *
     * @return true if the absolute difference between lengths
     * is less than or equal to \p tolerance
     */
    bool IsEqual(const Length& other, double tolerance = DEFAULT_TOLERANCE) const;

    /**
     * Check if \p other is not equal in value to this instance.
     *
     * @param other Value to compare against
     * @param tolerance Smallest difference allowed between the two
     * values to still be considered equal
     *
     * @return true if the absolute difference between lengths
     * is greater than \p tolerance
     */
    bool IsNotEqual(const Length& other, double tolerance = DEFAULT_TOLERANCE) const;

    /**
     * Check if \p other is greater in value than this instance.
     *
     * @param other Value to compare against
     * @param tolerance Smallest difference allowed between the two
     * values to still be considered equal
     *
     * @return true if the values are not equal and \p other is
     * greater in value
     */
    bool IsLess(const Length& other, double tolerance = DEFAULT_TOLERANCE) const;

    /**
     * Check if \p other is greater or equal in value than this instance.
     *
     * @param other Value to compare against
     * @param tolerance Smallest difference allowed between the two
     * values to still be considered equal
     *
     * Equivalent to:
     * @code
     *   IsEqual(other, tolerance) || IsLess(other, tolerance)
     * @endcode
     *
     * @return true if the values are equal or \p other is
     * greater in value
     */
    bool IsLessOrEqual(const Length& other, double tolerance = DEFAULT_TOLERANCE) const;

    /**
     * Check if \p other is less in value than this instance.
     *
     * @param other Value to compare against
     * @param tolerance Smallest difference allowed between the two
     * values to still be considered equal
     *
     * Equivalent to:
     * @code
     *   !(IsLessOrEqual(other, tolerance))
     * @endcode
     *
     * @return true if the values are not equal and \p other is
     * less in value
     */
    bool IsGreater(const Length& other, double tolerance = DEFAULT_TOLERANCE) const;

    /**
     * Check if \p other is equal or less in value than this instance.
     *
     * @param other Value to compare against
     * @param tolerance Smallest difference allowed between the two
     * values to still be considered equal
     *
     * Equivalent to:
     * @code
     *   !IsLess(other, tolerance)
     * @endcode
     *
     * @return true if the values are equal or \p other is
     * less in value
     */
    bool IsGreaterOrEqual(const Length& other, double tolerance = DEFAULT_TOLERANCE) const;

    /**
     * Swap values with another object
     *
     * Swap the current value with the value in \p other.
     *
     * Equivalent to:
     * @code
     *   Length temp(*this);
     *   *this = other;
     *   other = temp;
     * @endcode
     *
     * @param other Length object to swap
     */
    void swap(Length& other);

    /**
     * Current length value
     *
     * Equivalent to:
     * @code
     *   As (Length::Unit::Meter).Value ()
     * @endcode
     * @return The current value, in meters
     */
    double GetDouble() const;

    /**
     * Create a Quantity in a specific unit from a Length
     *
     * Converts the current length value to the equivalent value specified by
     * \p unit and returns a Quantity object with the converted value and unit
     *
     * @param unit The desired unit of the returned Quantity
     *
     * @return A quantity representing the length in the requested unit
     */
    Quantity As(Unit unit) const;

  private:
    double m_value; //!< Length in meters

    // end of class Length
};

ATTRIBUTE_HELPER_HEADER(Length);

/**
 * @ingroup length
 * @brief Return the symbol of the supplied unit
 *
 * The symbol of the unit is the shortened form of the unit name and is usually
 * two or three characters long
 *
 * @param unit The unit to symbolize
 *
 * @return String containing the symbol of \p unit
 */
std::string ToSymbol(Length::Unit unit);

/**
 * @ingroup length
 * @brief Return the name of the supplied unit
 *
 * The value returned by this function is the common name of \p unit. The output
 * is always lowercase.
 *
 * If \p plural is true, then the plural form of the common name is returned
 * instead.
 *
 * @param unit The unit to name
 * @param plural Boolean indicating if the returned string should contain the
 * plural form of the name
 *
 * @return String containing the full name of \p unit
 */
std::string ToName(Length::Unit unit, bool plural = false);

/**
 * @ingroup length
 * @brief Find the equivalent Length::Unit for a unit string
 *
 * The string value can be a symbol or name (plural or singular).
 *
 * The string comparison ignores case so strings like "NanoMeter", "centiMeter",
 * "METER" will all match the correct unit.
 *
 * Leading and trailing whitespace are trimmed from the string before searching
 * for a match.
 *
 * @param unitString String containing the symbol or name of a length unit
 *
 * @return A std::optional object containing a Length::Unit if a match for the
 * string could be found
 */
std::optional<Length::Unit> FromString(std::string unitString);

/**
 * @ingroup length
 * @brief Write a length value to an output stream.
 *
 * The output of the length is in meters.
 *
 * Equivalent to:
 * @code
 *   stream << l.As (Meter);
 * @endcode
 *
 * @param stream Output stream
 * @param l Length value to write to the stream
 *
 * @return Reference to the output stream
 */
std::ostream& operator<<(std::ostream& stream, const Length& l);

/**
 * @ingroup length
 * @brief Write a Quantity to an output stream.
 *
 * The data written to the output stream will have the format \<value\> \<symbol\>
 *
 * Equivalent to:
 * @code
 *   stream << q.Value () << ' ' << ToSymbol (q.Unit());
 * @endcode
 *
 * @param stream Output stream
 * @param q Quantity to write to the output stream
 *
 * @return Reference to the output stream
 */
std::ostream& operator<<(std::ostream& stream, const Length::Quantity& q);

/**
 * @ingroup length
 * @brief Write a Length::Unit to an output stream.
 *
 * Writes the name of \p unit to the output stream
 *
 * Equivalent to:
 * @code
 *   stream << ToName (unit);
 * @endcode
 *
 * @param stream Output stream
 * @param unit Length unit to output
 *
 * @return Reference to the output stream
 */
std::ostream& operator<<(std::ostream& stream, Length::Unit unit);

/**
 * @ingroup length
 * @brief Read a length value from an input stream.
 *
 * The expected format of the input is \<number\> \<unit\>
 * or \<number\>\<unit\>
 * This function calls NS_ABORT if the input stream does not contain
 * a valid length string
 *
 * @param stream Input stream
 * @param l Object where the deserialized value will be stored
 *
 * @return Reference to the input stream
 */
std::istream& operator>>(std::istream& stream, Length& l);

/**
 * @ingroup length
 * @brief Compare two length objects for equality.
 *
 * Equivalent to:
 * @code
 *   left.IsEqual(right, 0);
 * @endcode
 *
 * @param left Left length object
 * @param right Right length object
 *
 * @return true if \p left and \p right have the same value
 */
bool operator==(const Length& left, const Length& right);

/**
 * @ingroup length
 * @brief Compare two length objects for inequality.
 *
 * Equivalent to:
 * @code
 *   left.IsNotEqual(right, 0);
 * @endcode
 *
 * @param left Left length object
 * @param right Right length object
 *
 * @return true if \p left and \p right do not have the same value
 */
bool operator!=(const Length& left, const Length& right);

/**
 * @ingroup length
 * @brief Check if \p left has a value less than \p right
 *
 * Equivalent to:
 * @code
 *   left.IsLess(right, 0);
 * @endcode
 *
 * @param left Left length object
 * @param right Right length object
 *
 * @return true if \p left is less than \p right
 */
bool operator<(const Length& left, const Length& right);

/**
 * @ingroup length
 * @brief Check if \p left has a value less than or equal to \p right
 *
 * Equivalent to:
 * @code
 *   left.IsLessOrEqual(right, 0);
 * @endcode
 *
 * @param left Left length object
 * @param right Right length object
 *
 * @return true if \p left is less than or equal to \p right
 */
bool operator<=(const Length& left, const Length& right);

/**
 * @ingroup length
 * @brief Check if \p left has a value greater than \p right
 *
 * Equivalent to:
 * @code
 *   left.IsGreater(right, 0);
 * @endcode
 *
 * @param left Left length object
 * @param right Right length object
 *
 * @return true if \p left is greater than \p right
 */
bool operator>(const Length& left, const Length& right);

/**
 * @ingroup length
 * @brief Check if \p left has a value greater than or equal to \p right
 *
 * Equivalent to:
 * @code
 *   left.IsGreaterOrEqual(right, 0);
 * @endcode
 *
 * @param left Left length object
 * @param right Right length object
 *
 * @return true if \p left is greater than or equal to \p right
 */
bool operator>=(const Length& left, const Length& right);

/**
 * @ingroup length
 * @brief Add two length values together.
 *
 * Adds the values of \p left to \p right and returns a new
 * Length object containing the result.
 *
 * @param left A Length object
 * @param right A Length object
 *
 * @return A newly constructed Length object containing the
 * result of `left + right`.
 */
Length operator+(const Length& left, const Length& right);

/**
 * @ingroup length
 * @brief Subtract two length values.
 *
 * Subtracts the value of \p right from \p left and returns a
 * new Length object containing the result.
 *
 * @param left A Length object
 * @param right A Length object
 *
 * @return A newly constructed Length object containing the
 * result of `left - right`.
 */
Length operator-(const Length& left, const Length& right);

/**
 * @ingroup length
 * @brief Multiply a length value by a scalar
 *
 * Multiplies the value \p l by \p scalar and returns a new
 * Length object containing the result.
 *
 * @param l The Length object
 * @param scalar Multiplication factor
 *
 * @return A newly constructed Length object containing the result
 * of `l * scalar`.
 */
Length operator*(double scalar, const Length& l);
/**
 * @ingroup length
 * @brief Multiply a length value by a scalar
 *
 * Multiplies the value \p l by \p scalar and returns a new
 * Length object containing the result.
 *
 * @param l The Length object
 * @param scalar Multiplication factor
 *
 * @return A newly constructed Length object containing the result
 * of `l * scalar`.
 */
Length operator*(const Length& l, double scalar);

/**
 * @ingroup length
 * @brief Divide a length value by a scalar
 *
 * Divides the value \p left by \p scalar and returns a new
 * Length object containing the result.
 *
 * \p scalar must contain a non zero value.
 * NS_FATAL_ERROR is called if \p scalar is zero
 *
 * @param left Length value
 * @param scalar Multiplication factor
 *
 * @return A newly constructed Length object containing the result
 * of `left / scalar`.
 */
Length operator/(const Length& left, double scalar);

/**
 * @ingroup length
 * @brief Divide a length value by another length value
 *
 * Divides the value \p numerator by the value \p denominator and
 * returns a scalar value containing the result.
 *
 * The return value will be NaN if \p denominator is 0.
 *
 * @param numerator The top value of the division
 * @param denominator The bottom value of the division
 *
 * @return A scalar value that is the result of `numerator / denominator` or
 * NaN if \p denominator is 0.
 */
double operator/(const Length& numerator, const Length& denominator);

/**
 * @ingroup length
 * @brief Calculate how many times \p numerator can be split into \p denominator
 * sized pieces.
 *
 * If the result of `numerator / denominator` is not a whole number, the
 * result is rounded toward zero to the nearest whole number.
 * The amount remaining  after the division can be retrieved by passing a pointer
 * to a Length in \p remainder.  The remainder will be less than \p denominator
 * and have the same sign as \p numerator.
 *
 * NS_FATAL_ERROR is called if \p denominator is 0.
 *
 * @param numerator The value to split
 * @param denominator The length of each split
 * @param remainder Location to store the remainder
 *
 * @return The number of times \p numerator can be split into \p denominator
 * sized pieces, rounded down to the nearest whole number
 */
int64_t Div(const Length& numerator, const Length& denominator, Length* remainder = nullptr);

/**
 * @ingroup length
 * @brief Calculate the amount remaining after dividing two lengths
 *
 * The returned value will be less than \p denominator and have the same sign as
 * \p numerator.
 *
 * NS_FATAL_ERROR is called if \p denominator is 0.
 *
 * @param numerator The value to split
 * @param denominator The length of each split
 *
 * @return The amount remaining after splitting \p numerator into \p denominator
 * sized pieces.
 */
Length Mod(const Length& numerator, const Length& denominator);

/**
 * Construct a length from a value in the indicated unit.
 * @{
 * @ingroup length
 * @param value The numerical value.
 * @returns Length object.
 */
Length NanoMeters(double value);
Length MicroMeters(double value);
Length MilliMeters(double value);
Length CentiMeters(double value);
Length Meters(double value);
Length KiloMeters(double value);
Length NauticalMiles(double value);
Length Inches(double value);
Length Feet(double value);
Length Yards(double value);
Length Miles(double value);
/**@}*/

#ifdef HAVE_BOOST_UNITS
template <class U, class T>
Length::Length(boost::units::quantity<U, T> quantity)
    : m_value(0)
{
    namespace bu = boost::units;
    using BoostMeters = bu::quantity<bu::si::length, double>;

    // convert value to meters
    m_value = static_cast<BoostMeters>(quantity).value();
}
#endif

} // namespace ns3

#endif /* NS3_LENGTH_H_ */
