| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * \file linear_variable.h | ||
| 3 | * \brief storage for variable points of the form p_i = B_i x + c_i | ||
| 4 | * \author Steve T. | ||
| 5 | * \version 0.1 | ||
| 6 | * \date 07/02/2019 | ||
| 7 | */ | ||
| 8 | |||
| 9 | #ifndef _CLASS_LINEAR_VARIABLE | ||
| 10 | #define _CLASS_LINEAR_VARIABLE | ||
| 11 | |||
| 12 | #include <math.h> | ||
| 13 | |||
| 14 | #include <Eigen/Core> | ||
| 15 | #include <stdexcept> | ||
| 16 | #include <vector> | ||
| 17 | |||
| 18 | #include "MathDefs.h" | ||
| 19 | #include "bezier_curve.h" | ||
| 20 | #include "curve_abc.h" | ||
| 21 | #include "serialization/archive.hpp" | ||
| 22 | #include "serialization/eigen-matrix.hpp" | ||
| 23 | |||
| 24 | namespace ndcurves { | ||
| 25 | template <typename Numeric = double, bool Safe = true> | ||
| 26 | struct linear_variable : public serialization::Serializable { | ||
| 27 | typedef Eigen::Matrix<Numeric, Eigen::Dynamic, 1> vector_x_t; | ||
| 28 | typedef Eigen::Matrix<Numeric, Eigen::Dynamic, Eigen::Dynamic> matrix_x_t; | ||
| 29 | typedef Eigen::Matrix<Numeric, 3, 1> vector_3_t; | ||
| 30 | typedef Eigen::Matrix<Numeric, 3, 3> matrix_3_t; | ||
| 31 | typedef linear_variable<Numeric> linear_variable_t; | ||
| 32 | |||
| 33 | 155 | linear_variable() | |
| 34 |
2/4✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
|
155 | : B_(matrix_x_t::Identity(0, 0)), |
| 35 |
2/4✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
|
155 | c_(vector_x_t::Zero(0)), |
| 36 | 155 | zero(true) {} // variable | |
| 37 | 55 | linear_variable(const vector_x_t& c) | |
| 38 |
2/4✓ Branch 3 taken 55 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 55 times.
✗ Branch 7 not taken.
|
55 | : B_(matrix_x_t::Zero(c.size(), c.size())), |
| 39 |
1/2✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
|
55 | c_(c), |
| 40 | 55 | zero(false) {} // constant | |
| 41 | 3277 | linear_variable(const matrix_x_t& B, const vector_x_t& c) | |
| 42 |
1/2✓ Branch 2 taken 3277 times.
✗ Branch 3 not taken.
|
3277 | : B_(B), c_(c), zero(false) {} // mixed |
| 43 | 5231 | linear_variable(const linear_variable_t& other) | |
| 44 | 5231 | : B_(other.B()), | |
| 45 |
1/2✓ Branch 2 taken 5231 times.
✗ Branch 3 not taken.
|
5231 | c_(other.c()), |
| 46 | 5231 | zero(other.isZero()) {} // copy constructor | |
| 47 | |||
| 48 | 8718 | ~linear_variable() {} | |
| 49 | |||
| 50 | /// \brief Linear evaluation for vector x. | ||
| 51 | /// \param val : vector to evaluate the linear variable. | ||
| 52 | /// \return Evaluation of linear variable for vector x. | ||
| 53 | /// | ||
| 54 | 170 | vector_x_t operator()(const Eigen::Ref<const vector_x_t>& val) const { | |
| 55 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 170 times.
|
170 | if (isZero()) return c(); |
| 56 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 170 times.
|
170 | if (Safe && B().cols() != val.rows()) |
| 57 | ✗ | throw std::length_error( | |
| 58 | "Cannot evaluate linear variable, variable value does not have the " | ||
| 59 | "correct dimension"); | ||
| 60 |
3/6✓ Branch 3 taken 170 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 170 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 170 times.
✗ Branch 10 not taken.
|
170 | return B() * val + c(); |
| 61 | } | ||
| 62 | |||
| 63 | /// \brief Add another linear variable. | ||
| 64 | /// \param w1 : linear variable to add. | ||
| 65 | /// \return Linear variable after operation. | ||
| 66 | /// | ||
| 67 | 934 | linear_variable_t& operator+=(const linear_variable_t& w1) { | |
| 68 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 934 times.
|
934 | if (w1.isZero()) return *this; |
| 69 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 934 times.
|
934 | if (isZero()) { |
| 70 | ✗ | this->B_ = w1.B_; | |
| 71 | ✗ | this->c_ = w1.c_; | |
| 72 | ✗ | zero = w1.isZero(); | |
| 73 | } else { | ||
| 74 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 934 times.
|
934 | if (Safe && B().rows() != w1.B().rows()) |
| 75 | ✗ | throw std::length_error( | |
| 76 | "Cannot add linear variables, variables do not have the same " | ||
| 77 | "dimension"); | ||
| 78 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 934 times.
|
1868 | else if (B().cols() > |
| 79 | 934 | w1.B().cols()) { // new variables added left for primitive | |
| 80 | ✗ | B_.block(0, B().cols() - w1.B().cols(), B().rows(), w1.B().cols()) += | |
| 81 | ✗ | w1.B(); | |
| 82 | ✗ | c_.tail(w1.c().rows()) += w1.c(); | |
| 83 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 934 times.
|
1868 | } else if (B().cols() < |
| 84 | 934 | w1.B().cols()) { // new variables added left for primitive | |
| 85 | ✗ | linear_variable_t opp = w1 + (*this); | |
| 86 | ✗ | this->B_ = opp.B_; | |
| 87 | ✗ | this->c_ = opp.c_; | |
| 88 | ✗ | } else { | |
| 89 | 934 | this->B_ += w1.B_; | |
| 90 | 934 | this->c_ += w1.c_; | |
| 91 | } | ||
| 92 | } | ||
| 93 | 934 | return *this; | |
| 94 | } | ||
| 95 | |||
| 96 | /// \brief Substract another linear variable. | ||
| 97 | /// \param w1 : linear variable to substract. | ||
| 98 | /// \return Linear variable after operation. | ||
| 99 | /// | ||
| 100 | 16 | linear_variable_t& operator-=(const linear_variable_t& w1) { | |
| 101 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
|
16 | if (w1.isZero()) return *this; |
| 102 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
|
16 | if (isZero()) { |
| 103 | ✗ | this->B_ = -w1.B_; | |
| 104 | ✗ | this->c_ = -w1.c_; | |
| 105 | ✗ | zero = w1.isZero(); | |
| 106 | } else { | ||
| 107 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 16 times.
|
16 | if (Safe && B().rows() != w1.B().rows()) |
| 108 | ✗ | throw std::length_error( | |
| 109 | "Cannot add linear variables, variables do not have the same " | ||
| 110 | "dimension"); | ||
| 111 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
|
32 | else if (B().cols() > |
| 112 | 16 | w1.B().cols()) { // new variables added left for primitive | |
| 113 | ✗ | B_.block(0, B().cols() - w1.B().cols(), B().rows(), w1.B().cols()) -= | |
| 114 | ✗ | w1.B(); | |
| 115 | ✗ | c_.tail(w1.c().rows()) -= w1.c(); | |
| 116 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
|
32 | } else if (B().cols() < |
| 117 | 16 | w1.B().cols()) { // new variables added left for primitive | |
| 118 | ✗ | linear_variable_t opp = -w1 + (*this); | |
| 119 | ✗ | this->B_ = opp.B_; | |
| 120 | ✗ | this->c_ = opp.c_; | |
| 121 | ✗ | } else { | |
| 122 | 16 | this->B_ -= w1.B_; | |
| 123 | 16 | this->c_ -= w1.c_; | |
| 124 | } | ||
| 125 | } | ||
| 126 | 16 | return *this; | |
| 127 | } | ||
| 128 | |||
| 129 | /// \brief Divide by a constant : p_i / d = B_i*x/d + c_i/d. | ||
| 130 | /// \param d : constant. | ||
| 131 | /// \return Linear variable after operation. | ||
| 132 | /// | ||
| 133 | ✗ | linear_variable_t& operator/=(const double d) { | |
| 134 | ✗ | B_ /= d; | |
| 135 | ✗ | c_ /= d; | |
| 136 | ✗ | return *this; | |
| 137 | } | ||
| 138 | |||
| 139 | /// \brief Multiply by a constant : p_i / d = B_i*x*d + c_i*d. | ||
| 140 | /// \param d : constant. | ||
| 141 | /// \return Linear variable after operation. | ||
| 142 | /// | ||
| 143 | 1915 | linear_variable_t& operator*=(const double d) { | |
| 144 | 1915 | B_ *= d; | |
| 145 | 1915 | c_ *= d; | |
| 146 | 1915 | return *this; | |
| 147 | } | ||
| 148 | |||
| 149 | /// \brief Compute the cross product of the current linear_variable and the | ||
| 150 | /// other. | ||
| 151 | /// This method of course only makes sense for dimension 3 curves and | ||
| 152 | /// dimension 3 unknown, since otherwise the result is non-linear. It assumes | ||
| 153 | /// that a method point_t cross(const point_t&, const point_t&) has been | ||
| 154 | /// defined | ||
| 155 | /// \param pOther other polynomial to compute the cross product with. | ||
| 156 | /// \return a new polynomial defining the cross product between this and | ||
| 157 | /// other | ||
| 158 | 33 | linear_variable_t cross(const linear_variable_t& other) const { | |
| 159 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
|
33 | if (B().rows() != 3) |
| 160 | ✗ | throw std::invalid_argument( | |
| 161 | "Can't perform cross product on linear variables with dimensions != " | ||
| 162 | "3 "); | ||
| 163 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
|
33 | if (B().cols() != 3) |
| 164 | ✗ | throw std::invalid_argument( | |
| 165 | "Can't perform cross product on linear variables more than one " | ||
| 166 | "unknown "); | ||
| 167 |
6/8✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 18 times.
✓ Branch 6 taken 15 times.
✓ Branch 7 taken 18 times.
✓ Branch 9 taken 15 times.
✗ Branch 10 not taken.
|
33 | if (isZero() || other.isZero()) return linear_variable_t::Zero(3); |
| 168 |
5/8✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 18 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 17 times.
✓ Branch 12 taken 1 times.
|
35 | if ((B().squaredNorm() - B().diagonal().squaredNorm() > MARGIN) || |
| 169 |
7/10✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 17 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 17 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 16 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 16 times.
|
35 | (other.B().squaredNorm() - other.B().diagonal().squaredNorm() > MARGIN)) |
| 170 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | throw std::invalid_argument( |
| 171 | "Can't perform cross product on linear variables if B is not " | ||
| 172 | "diagonal "); | ||
| 173 | // (B1 x + c1) X (B2 x + c2) = (-c2X B1) x + (bX B2) x + b1Xb2 | ||
| 174 |
2/4✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
|
16 | typename linear_variable_t::matrix_3_t newB = |
| 175 | skew<typename linear_variable_t::matrix_3_t, | ||
| 176 |
4/8✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 16 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 16 times.
✗ Branch 12 not taken.
|
16 | typename linear_variable_t::vector_3_t>(-other.c()) * |
| 177 | 16 | B() + | |
| 178 | skew<typename linear_variable_t::matrix_3_t, | ||
| 179 |
3/6✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 16 times.
✗ Branch 9 not taken.
|
16 | typename linear_variable_t::vector_3_t>(c()) * |
| 180 | 16 | other.B(); | |
| 181 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | typename linear_variable_t::vector_3_t newC = |
| 182 | 16 | ndcurves::cross(c(), other.c()); | |
| 183 |
3/6✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 16 times.
✗ Branch 8 not taken.
|
16 | return linear_variable_t(newB, newC); |
| 184 | } | ||
| 185 | |||
| 186 | /// \brief Get a linear variable equal to zero. | ||
| 187 | /// \param dim : Dimension of linear variable. | ||
| 188 | /// \return Linear variable equal to zero. | ||
| 189 | /// | ||
| 190 | 30 | static linear_variable_t Zero(size_t dim = 0) { | |
| 191 |
5/10✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 30 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 30 times.
✗ Branch 14 not taken.
|
30 | return linear_variable_t(matrix_x_t::Zero(dim, dim), vector_x_t::Zero(dim)); |
| 192 | } | ||
| 193 | |||
| 194 | /// \brief Get a linear variable equal to the variable | ||
| 195 | /// \param dim : Dimension of linear variable. | ||
| 196 | /// \return Linear variable equal to the variable. | ||
| 197 | /// | ||
| 198 | 168 | static linear_variable_t X(size_t dim = 0) { | |
| 199 | return linear_variable_t(matrix_x_t::Identity(dim, dim), | ||
| 200 |
5/10✓ Branch 1 taken 168 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 168 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 168 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 168 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 168 times.
✗ Branch 14 not taken.
|
168 | vector_x_t::Zero(dim)); |
| 201 | } | ||
| 202 | |||
| 203 | /// \brief Get dimension of linear variable. | ||
| 204 | /// \return Dimension of linear variable. | ||
| 205 | /// | ||
| 206 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 701 times.
|
705 | std::size_t size() const { return zero ? 0 : std::max(B_.rows(), c_.size()); } |
| 207 | |||
| 208 | /// \brief Get norm of linear variable (Norm of B plus norm of C). | ||
| 209 | /// \return Norm of linear variable. | ||
| 210 | ✗ | Numeric norm() const { return isZero() ? 0 : (B_.norm() + c_.norm()); } | |
| 211 | |||
| 212 | /// \brief Check if actual linear variable and other are approximately equal | ||
| 213 | /// given a precision threshold. Only two curves of the same class can be | ||
| 214 | /// approximately equal, \param prec : the precision threshold, default | ||
| 215 | /// Eigen::NumTraits<Numeric>::dummy_precision() \return true if the two | ||
| 216 | /// linear variables are approximately equal. | ||
| 217 | ✗ | bool isApprox( | |
| 218 | const linear_variable_t& other, | ||
| 219 | const double prec = Eigen::NumTraits<Numeric>::dummy_precision()) const { | ||
| 220 | ✗ | return (*this - other).norm() < prec; | |
| 221 | } | ||
| 222 | |||
| 223 | 14763 | const matrix_x_t& B() const { return B_; } | |
| 224 | 8874 | const vector_x_t& c() const { return c_; } | |
| 225 | 7484 | bool isZero() const { return zero; } | |
| 226 | |||
| 227 | // Serialization of the class | ||
| 228 | friend class boost::serialization::access; | ||
| 229 | |||
| 230 | template <class Archive> | ||
| 231 | ✗ | void serialize(Archive& ar, const unsigned int version) { | |
| 232 | if (version) { | ||
| 233 | // Do something depending on version ? | ||
| 234 | } | ||
| 235 | ✗ | ar& boost::serialization::make_nvp("B_", B_); | |
| 236 | ✗ | ar& boost::serialization::make_nvp("c_", c_); | |
| 237 | ✗ | ar& boost::serialization::make_nvp("zero", zero); | |
| 238 | ✗ | } | |
| 239 | |||
| 240 | 905 | linear_variable& operator=(const linear_variable& other) { | |
| 241 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 905 times.
|
905 | if (this == &other) { |
| 242 | ✗ | return *this; | |
| 243 | } | ||
| 244 | // Perform a deep copy here to copy all necessary data. | ||
| 245 | // Make sure to handle memory allocation properly. | ||
| 246 | // You may need to copy the data contained within the linear_variable. | ||
| 247 | 905 | this->B_ = other.B_; | |
| 248 | 905 | this->c_ = other.c_; | |
| 249 | 905 | this->zero = other.zero; | |
| 250 | 905 | return *this; | |
| 251 | } | ||
| 252 | |||
| 253 | private: | ||
| 254 | matrix_x_t B_; | ||
| 255 | vector_x_t c_; | ||
| 256 | bool zero; | ||
| 257 | }; | ||
| 258 | |||
| 259 | template <typename N, bool S> | ||
| 260 | 897 | inline linear_variable<N, S> operator+(const linear_variable<N, S>& w1, | |
| 261 | const linear_variable<N, S>& w2) { | ||
| 262 |
1/2✓ Branch 3 taken 897 times.
✗ Branch 4 not taken.
|
897 | linear_variable<N, S> res(w1.B(), w1.c()); |
| 263 |
2/4✓ Branch 1 taken 897 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 897 times.
✗ Branch 5 not taken.
|
1794 | return res += w2; |
| 264 | 897 | } | |
| 265 | |||
| 266 | template <typename N, bool S> | ||
| 267 | 16 | linear_variable<N, S> operator-(const linear_variable<N, S>& w1, | |
| 268 | const linear_variable<N, S>& w2) { | ||
| 269 |
1/2✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
|
16 | linear_variable<N, S> res(w1.B(), w1.c()); |
| 270 |
2/4✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
|
32 | return res -= w2; |
| 271 | 16 | } | |
| 272 | |||
| 273 | template <typename N, bool S> | ||
| 274 | ✗ | linear_variable<N, S> operator-(const linear_variable<N, S>& w1) { | |
| 275 | ✗ | return linear_variable<N, S>(-w1.B(), -w1.c()); | |
| 276 | } | ||
| 277 | |||
| 278 | template <typename N, bool S> | ||
| 279 | 955 | linear_variable<N, S> operator*(const double k, | |
| 280 | const linear_variable<N, S>& w) { | ||
| 281 |
1/2✓ Branch 3 taken 955 times.
✗ Branch 4 not taken.
|
955 | linear_variable<N, S> res(w.B(), w.c()); |
| 282 |
2/4✓ Branch 1 taken 955 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 955 times.
✗ Branch 5 not taken.
|
1910 | return res *= k; |
| 283 | 955 | } | |
| 284 | |||
| 285 | template <typename N, bool S> | ||
| 286 | 960 | linear_variable<N, S> operator*(const linear_variable<N, S>& w, | |
| 287 | const double k) { | ||
| 288 |
1/2✓ Branch 3 taken 960 times.
✗ Branch 4 not taken.
|
960 | linear_variable<N, S> res(w.B(), w.c()); |
| 289 |
2/4✓ Branch 1 taken 960 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 960 times.
✗ Branch 5 not taken.
|
1920 | return res *= k; |
| 290 | 960 | } | |
| 291 | |||
| 292 | template <typename N, bool S> | ||
| 293 | ✗ | linear_variable<N, S> operator/(const linear_variable<N, S>& w, | |
| 294 | const double k) { | ||
| 295 | ✗ | linear_variable<N, S> res(w.B(), w.c()); | |
| 296 | ✗ | return res /= k; | |
| 297 | ✗ | } | |
| 298 | |||
| 299 | template <typename BezierFixed, typename BezierLinear, typename X> | ||
| 300 | 16 | BezierFixed evaluateLinear(const BezierLinear& bIn, const X x) { | |
| 301 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | typename BezierFixed::t_point_t fixed_wps; |
| 302 | 16 | for (typename BezierLinear::cit_point_t cit = bIn.waypoints().begin(); | |
| 303 |
2/2✓ Branch 3 taken 140 times.
✓ Branch 4 taken 16 times.
|
156 | cit != bIn.waypoints().end(); ++cit) |
| 304 |
3/6✓ Branch 2 taken 140 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 140 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 140 times.
✗ Branch 9 not taken.
|
140 | fixed_wps.push_back(cit->operator()(x)); |
| 305 | 16 | return BezierFixed(fixed_wps.begin(), fixed_wps.end(), bIn.T_min_, | |
| 306 |
1/2✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
|
32 | bIn.T_max_); |
| 307 | 16 | } | |
| 308 | |||
| 309 | template <typename N, bool S> | ||
| 310 | std::ostream& operator<<(std::ostream& os, const linear_variable<N, S>& l) { | ||
| 311 | return os << "linear_variable: \n \t B:\n" | ||
| 312 | << l.B() << "\t c: \n" | ||
| 313 | << l.c().transpose(); | ||
| 314 | } | ||
| 315 | |||
| 316 | } // namespace ndcurves | ||
| 317 | |||
| 318 | DEFINE_CLASS_TEMPLATE_VERSION( | ||
| 319 | SINGLE_ARG(typename Numeric, bool Safe), | ||
| 320 | SINGLE_ARG(ndcurves::linear_variable<Numeric, Safe>)) | ||
| 321 | #endif //_CLASS_LINEAR_VARIABLE | ||
| 322 |