Sparkle 0.0.1
Loading...
Searching...
No Matches
spk_vector4.hpp
1#pragma once
2
3#include <algorithm>
4#include <cmath>
5#include <cstdint>
6#include <initializer_list>
7#include <iostream>
8#include <sstream>
9#include <stdexcept>
10#include <string>
11#include <type_traits>
12
13#include "structure/math/spk_safe_comparand.hpp"
14
15#include "structure/math/spk_vector3.hpp"
16
17namespace spk
18{
32 template <typename TType>
33 struct IVector4
34 {
38 using value_type = TType;
39
43 TType x{};
47 TType y{};
51 TType z{};
55 TType w{};
56
60 constexpr IVector4() = default;
61
69 template <typename TX, typename TY, typename TZ, typename TW>
70 requires(std::is_constructible_v<TType, TX> && std::is_constructible_v<TType, TY> &&
71 std::is_constructible_v<TType, TZ> && std::is_constructible_v<TType, TW>)
72 constexpr IVector4(const TX &p_x, const TY &p_y, const TZ &p_z, const TW &p_w) :
73 x(static_cast<TType>(p_x)),
74 y(static_cast<TType>(p_y)),
75 z(static_cast<TType>(p_z)),
76 w(static_cast<TType>(p_w))
77 {
78 }
79
89 template <typename TOtherVector, typename TZ, typename TW>
90 requires(std::is_constructible_v<TType, TOtherVector> &&
91 std::is_constructible_v<TType, TZ> && std::is_constructible_v<TType, TW>)
92 constexpr IVector4(const IVector2<TOtherVector> &p_other, const TZ &p_z, const TW &p_w) :
93 x(static_cast<TType>(p_other.x)),
94 y(static_cast<TType>(p_other.y)),
95 z(static_cast<TType>(p_z)),
96 w(static_cast<TType>(p_w))
97 {
98 }
99
107 template <typename TOtherVector, typename TW>
108 requires(std::is_constructible_v<TType, TOtherVector> && std::is_constructible_v<TType, TW>)
109 constexpr IVector4(const IVector3<TOtherVector> &p_other, const TW &p_w) :
110 x(static_cast<TType>(p_other.x)),
111 y(static_cast<TType>(p_other.y)),
112 z(static_cast<TType>(p_other.z)),
113 w(static_cast<TType>(p_w))
114 {
115 }
116
122 template <typename TOther>
123 requires std::is_constructible_v<TType, TOther>
124 explicit constexpr IVector4(const IVector4<TOther> &p_other) :
125 x(static_cast<TType>(p_other.x)),
126 y(static_cast<TType>(p_other.y)),
127 z(static_cast<TType>(p_other.z)),
128 w(static_cast<TType>(p_other.w))
129 {
130 }
131
136 template <typename TOther>
137 requires std::is_constructible_v<TType, TOther>
138 explicit constexpr IVector4(const TOther &p_value) :
139 x(static_cast<TType>(p_value)),
140 y(static_cast<TType>(p_value)),
141 z(static_cast<TType>(p_value)),
142 w(static_cast<TType>(p_value))
143 {
144 }
145
149 static const IVector4 Zero;
153 static const IVector4 One;
154
159 std::string toString() const
160 {
161 std::ostringstream stream;
162 stream << *this;
163 return stream.str();
164 }
165
170 std::wstring toWstring() const
171 {
172 std::wostringstream stream;
173 stream << *this;
174 return stream.str();
175 }
176
183 friend std::ostream &operator<<(std::ostream &p_stream, const IVector4 &p_value)
184 {
185 p_stream << '(' << p_value.x << ", " << p_value.y << ", " << p_value.z << ", " << p_value.w << ')';
186 return p_stream;
187 }
188
195 friend std::wostream &operator<<(std::wostream &p_stream, const IVector4 &p_value)
196 {
197 p_stream << L'(' << p_value.x << L", " << p_value.y << L", " << p_value.z << L", " << p_value.w << L')';
198 return p_stream;
199 }
200
206 bool operator==(const IVector4 &p_other) const
207 {
208 return spk::SafeComparand(x) == p_other.x &&
209 spk::SafeComparand(y) == p_other.y &&
210 spk::SafeComparand(z) == p_other.z &&
211 spk::SafeComparand(w) == p_other.w;
212 }
213
220 bool operator!=(const IVector4 &p_other) const
221 {
222 return !(*this == p_other);
223 }
224
230 IVector4 operator+(const IVector4 &p_other) const
231 {
232 return IVector4(x + p_other.x, y + p_other.y, z + p_other.z, w + p_other.w);
233 }
234
240 IVector4 operator-(const IVector4 &p_other) const
241 {
242 return IVector4(x - p_other.x, y - p_other.y, z - p_other.z, w - p_other.w);
243 }
244
250 IVector4 operator*(const IVector4 &p_other) const
251 {
252 return IVector4(x * p_other.x, y * p_other.y, z * p_other.z, w * p_other.w);
253 }
254
261 IVector4 operator/(const IVector4 &p_other) const
262 {
263 if (spk::SafeComparand(p_other.x) == 0 ||
264 spk::SafeComparand(p_other.y) == 0 ||
265 spk::SafeComparand(p_other.z) == 0 ||
266 spk::SafeComparand(p_other.w) == 0)
267 {
268 throw std::invalid_argument("Can't divide by 0");
269 }
270 return IVector4(x / p_other.x, y / p_other.y, z / p_other.z, w / p_other.w);
271 }
272
278 IVector4 operator+(const TType &p_scalar) const
279 {
280 return IVector4(x + p_scalar, y + p_scalar, z + p_scalar, w + p_scalar);
281 }
282
288 IVector4 operator-(const TType &p_scalar) const
289 {
290 return IVector4(x - p_scalar, y - p_scalar, z - p_scalar, w - p_scalar);
291 }
292
298 IVector4 operator*(const TType &p_scalar) const
299 {
300 return IVector4(x * p_scalar, y * p_scalar, z * p_scalar, w * p_scalar);
301 }
302
309 IVector4 operator/(const TType &p_scalar) const
310 {
311 if (spk::SafeComparand(p_scalar) == 0)
312 {
313 throw std::invalid_argument("Can't divide by 0");
314 }
315 return IVector4(x / p_scalar, y / p_scalar, z / p_scalar, w / p_scalar);
316 }
317
325 friend IVector4 operator+(const TType &p_scalar, const IVector4 &p_vector)
326 {
327 return p_vector + p_scalar;
328 }
329
336 friend IVector4 operator-(const TType &p_scalar, const IVector4 &p_vector)
337 {
338 return IVector4(p_scalar - p_vector.x, p_scalar - p_vector.y, p_scalar - p_vector.z, p_scalar - p_vector.w);
339 }
340
348 friend IVector4 operator*(const TType &p_scalar, const IVector4 &p_vector)
349 {
350 return p_vector * p_scalar;
351 }
352
360 friend IVector4 operator/(const TType &p_scalar, const IVector4 &p_vector)
361 {
362 if (spk::SafeComparand(p_vector.x) == 0 ||
363 spk::SafeComparand(p_vector.y) == 0 ||
364 spk::SafeComparand(p_vector.z) == 0 ||
365 spk::SafeComparand(p_vector.w) == 0)
366 {
367 throw std::invalid_argument("Can't divide by 0");
368 }
369 return IVector4(p_scalar / p_vector.x, p_scalar / p_vector.y, p_scalar / p_vector.z, p_scalar / p_vector.w);
370 }
371
377 {
378 return IVector4(-x, -y, -z, -w);
379 }
380
386 {
387 return inverse();
388 }
389
396 IVector4 &operator+=(const IVector4 &p_other)
397 {
398 x += p_other.x;
399 y += p_other.y;
400 z += p_other.z;
401 w += p_other.w;
402 return *this;
403 }
404
411 IVector4 &operator-=(const IVector4 &p_other)
412 {
413 x -= p_other.x;
414 y -= p_other.y;
415 z -= p_other.z;
416 w -= p_other.w;
417 return *this;
418 }
419
426 IVector4 &operator*=(const IVector4 &p_other)
427 {
428 x *= p_other.x;
429 y *= p_other.y;
430 z *= p_other.z;
431 w *= p_other.w;
432 return *this;
433 }
434
442 IVector4 &operator/=(const IVector4 &p_other)
443 {
444 if (spk::SafeComparand(p_other.x) == 0 ||
445 spk::SafeComparand(p_other.y) == 0 ||
446 spk::SafeComparand(p_other.z) == 0 ||
447 spk::SafeComparand(p_other.w) == 0)
448 {
449 throw std::invalid_argument("Can't divide by 0");
450 }
451 x /= p_other.x;
452 y /= p_other.y;
453 z /= p_other.z;
454 w /= p_other.w;
455 return *this;
456 }
457
464 IVector4 &operator+=(const TType &p_scalar)
465 {
466 x += p_scalar;
467 y += p_scalar;
468 z += p_scalar;
469 w += p_scalar;
470 return *this;
471 }
472
479 IVector4 &operator-=(const TType &p_scalar)
480 {
481 x -= p_scalar;
482 y -= p_scalar;
483 z -= p_scalar;
484 w -= p_scalar;
485 return *this;
486 }
487
494 IVector4 &operator*=(const TType &p_scalar)
495 {
496 x *= p_scalar;
497 y *= p_scalar;
498 z *= p_scalar;
499 w *= p_scalar;
500 return *this;
501 }
502
510 IVector4 &operator/=(const TType &p_scalar)
511 {
512 if (spk::SafeComparand(p_scalar) == 0)
513 {
514 throw std::invalid_argument("Can't divide by 0");
515 }
516 x /= p_scalar;
517 y /= p_scalar;
518 z /= p_scalar;
519 w /= p_scalar;
520 return *this;
521 }
522
527 bool isZero() const
528 {
529 return spk::SafeComparand(x) == 0 &&
530 spk::SafeComparand(y) == 0 &&
531 spk::SafeComparand(z) == 0 &&
532 spk::SafeComparand(w) == 0;
533 }
534
540 float squaredLength() const
541 {
542 const auto px = static_cast<float>(x);
543 const auto py = static_cast<float>(y);
544 const auto pz = static_cast<float>(z);
545 const auto pw = static_cast<float>(w);
546 return (px * px) + (py * py) + (pz * pz) + (pw * pw);
547 }
548
555 {
556 float baseSquareLenght = squaredLength();
557
558 if (spk::SafeComparand(baseSquareLenght) == 0)
559 {
560 throw std::runtime_error("Can't normilize a vector of norm == 0");
561 }
562 return (this->operator/(std::sqrt(baseSquareLenght)));
563 }
564
570 float length() const
571 {
572 return static_cast<float>(std::sqrt(static_cast<double>(squaredLength())));
573 }
574
581 float squaredDistance(const IVector4 &p_other) const
582 {
583 return (*this - p_other).squaredLength();
584 }
585
592 float distance(const IVector4 &p_other) const
593 {
594 return (*this - p_other).length();
595 }
596
602 float dot(const IVector4 &p_other) const
603 {
604 const auto px = static_cast<float>(x);
605 const auto py = static_cast<float>(y);
606 const auto pz = static_cast<float>(z);
607 const auto pw = static_cast<float>(w);
608 return (px * static_cast<float>(p_other.x)) +
609 (py * static_cast<float>(p_other.y)) +
610 (pz * static_cast<float>(p_other.z)) +
611 (pw * static_cast<float>(p_other.w));
612 }
613
619 {
620 return IVector3<TType>(x, y, z);
621 }
622
637 template <typename TAlpha>
638 requires std::is_arithmetic_v<TAlpha>
639 static IVector4 lerp(const IVector4 &p_from, const IVector4 &p_to, const TAlpha &p_alpha)
640 {
641 const auto alpha = static_cast<float>(p_alpha);
642 const auto startX = static_cast<float>(p_from.x);
643 const auto startY = static_cast<float>(p_from.y);
644 const auto startZ = static_cast<float>(p_from.z);
645 const auto startW = static_cast<float>(p_from.w);
646 const auto deltaX = static_cast<float>(p_to.x) - startX;
647 const auto deltaY = static_cast<float>(p_to.y) - startY;
648 const auto deltaZ = static_cast<float>(p_to.z) - startZ;
649 const auto deltaW = static_cast<float>(p_to.w) - startW;
650 return IVector4(
651 static_cast<TType>(startX + (deltaX * alpha)),
652 static_cast<TType>(startY + (deltaY * alpha)),
653 static_cast<TType>(startZ + (deltaZ * alpha)),
654 static_cast<TType>(startW + (deltaW * alpha)));
655 }
656
664 static IVector4 min(const IVector4 &p_a, const IVector4 &p_b)
665 {
666 return IVector4(
667 std::min(p_a.x, p_b.x),
668 std::min(p_a.y, p_b.y),
669 std::min(p_a.z, p_b.z),
670 std::min(p_a.w, p_b.w));
671 }
672
682 template <typename... TOthers>
683 requires(sizeof...(TOthers) > 0 && (std::is_same_v<TOthers, IVector4> && ...))
684 static IVector4 min(const IVector4 &p_a, const IVector4 &p_b, const TOthers &...p_rest)
685 {
686 return min(min(p_a, p_b), p_rest...);
687 }
688
695 static IVector4 min(std::initializer_list<IVector4> p_values)
696 {
697 if (p_values.size() == 0)
698 {
699 throw std::invalid_argument("IVector4::min requires at least one operand");
700 }
701
702 auto result = *p_values.begin();
703 for (auto it = std::next(p_values.begin()); it != p_values.end(); ++it)
704 {
705 result = min(result, *it);
706 }
707 return result;
708 }
709
717 static IVector4 max(const IVector4 &p_a, const IVector4 &p_b)
718 {
719 return IVector4(
720 std::max(p_a.x, p_b.x),
721 std::max(p_a.y, p_b.y),
722 std::max(p_a.z, p_b.z),
723 std::max(p_a.w, p_b.w));
724 }
725
734 template <typename... TOthers>
735 requires(sizeof...(TOthers) > 0 && (std::is_same_v<TOthers, IVector4> && ...))
736 static IVector4 max(const IVector4 &p_a, const IVector4 &p_b, const TOthers &...p_rest)
737 {
738 return max(max(p_a, p_b), p_rest...);
739 }
740
747 static IVector4 max(std::initializer_list<IVector4> p_values)
748 {
749 if (p_values.size() == 0)
750 {
751 throw std::invalid_argument("IVector4::max requires at least one operand");
752 }
753
754 auto result = *p_values.begin();
755 for (auto it = std::next(p_values.begin()); it != p_values.end(); ++it)
756 {
757 result = max(result, *it);
758 }
759 return result;
760 }
761
770 static IVector4 clamp(const IVector4 &p_value, const IVector4 &p_boundA, const IVector4 &p_boundB)
771 {
772 const IVector4 low(
773 std::min(p_boundA.x, p_boundB.x),
774 std::min(p_boundA.y, p_boundB.y),
775 std::min(p_boundA.z, p_boundB.z),
776 std::min(p_boundA.w, p_boundB.w));
777 const IVector4 high(
778 std::max(p_boundA.x, p_boundB.x),
779 std::max(p_boundA.y, p_boundB.y),
780 std::max(p_boundA.z, p_boundB.z),
781 std::max(p_boundA.w, p_boundB.w));
782
783 return IVector4(
784 std::clamp(p_value.x, low.x, high.x),
785 std::clamp(p_value.y, low.y, high.y),
786 std::clamp(p_value.z, low.z, high.z),
787 std::clamp(p_value.w, low.w, high.w));
788 }
789
797 IVector4 clamped(const IVector4 &p_boundA, const IVector4 &p_boundB) const
798 {
799 return clamp(*this, p_boundA, p_boundB);
800 }
801
810 static bool isBetween(const IVector4 &p_value, const IVector4 &p_boundA, const IVector4 &p_boundB)
811 {
812 const IVector4 low(
813 std::min(p_boundA.x, p_boundB.x),
814 std::min(p_boundA.y, p_boundB.y),
815 std::min(p_boundA.z, p_boundB.z),
816 std::min(p_boundA.w, p_boundB.w));
817 const IVector4 high(
818 std::max(p_boundA.x, p_boundB.x),
819 std::max(p_boundA.y, p_boundB.y),
820 std::max(p_boundA.z, p_boundB.z),
821 std::max(p_boundA.w, p_boundB.w));
822
823 return (p_value.x >= low.x && p_value.x <= high.x) &&
824 (p_value.y >= low.y && p_value.y <= high.y) &&
825 (p_value.z >= low.z && p_value.z <= high.z) &&
826 (p_value.w >= low.w && p_value.w <= high.w);
827 }
828
834 template <typename TOther>
835 requires std::is_constructible_v<TOther, TType>
836 explicit constexpr operator IVector4<TOther>() const
837 {
838 return IVector4<TOther>(
839 static_cast<TOther>(x),
840 static_cast<TOther>(y),
841 static_cast<TOther>(z),
842 static_cast<TOther>(w));
843 }
844 };
845
846 template <typename TType>
847 inline const IVector4<TType> IVector4<TType>::Zero{};
848
849 template <typename TType>
851 static_cast<TType>(1),
852 static_cast<TType>(1),
853 static_cast<TType>(1),
854 static_cast<TType>(1)};
855
856 using Vector4 = IVector4<float_t>;
857 using Vector4Int = IVector4<int32_t>;
858 using Vector4UInt = IVector4<uint32_t>;
859}
2D value type holding two components with basic arithmetic helpers.
Definition spk_vector2.hpp:35
TType x
X component.
Definition spk_vector2.hpp:44
TType y
Y component.
Definition spk_vector2.hpp:48
3D value type with common arithmetic helpers.
Definition spk_vector3.hpp:34
TType x
X component.
Definition spk_vector3.hpp:43
TType z
Z component.
Definition spk_vector3.hpp:51
TType y
Y component.
Definition spk_vector3.hpp:47
4D value type with arithmetic helpers and swizzles.
Definition spk_vector4.hpp:34
float_t z
Definition spk_vector4.hpp:51
static bool isBetween(const IVector4 &p_value, const IVector4 &p_boundA, const IVector4 &p_boundB)
Checks whether a vector lies within inclusive bounds.
Definition spk_vector4.hpp:810
IVector4 operator-(const TType &p_scalar) const
Subtracts a scalar from each component.
Definition spk_vector4.hpp:288
IVector4 & operator*=(const IVector4 &p_other)
In-place component-wise multiplication.
Definition spk_vector4.hpp:426
constexpr IVector4(const IVector3< TOtherVector > &p_other, const TW &p_w)
Builds a vector from a 3D vector and W component.
Definition spk_vector4.hpp:109
float distance(const IVector4 &p_other) const
Computes Euclidean distance to another vector.
Definition spk_vector4.hpp:592
std::wstring toWstring() const
Converts the vector to a wide string.
Definition spk_vector4.hpp:170
IVector3< TType > xyz() const
Projects this vector into 3D using x, y, z components.
Definition spk_vector4.hpp:618
float_t w
Definition spk_vector4.hpp:55
constexpr IVector4(const IVector4< TOther > &p_other)
Converting constructor from another component type.
Definition spk_vector4.hpp:124
IVector4 operator-(const IVector4 &p_other) const
Component-wise subtraction.
Definition spk_vector4.hpp:240
float squaredDistance(const IVector4 &p_other) const
Computes squared distance to another vector.
Definition spk_vector4.hpp:581
std::string toString() const
Converts the vector to a string.
Definition spk_vector4.hpp:159
IVector4 & operator+=(const TType &p_scalar)
In-place scalar addition.
Definition spk_vector4.hpp:464
IVector4 & operator+=(const IVector4 &p_other)
In-place component-wise addition.
Definition spk_vector4.hpp:396
IVector4 & operator-=(const TType &p_scalar)
In-place scalar subtraction.
Definition spk_vector4.hpp:479
IVector4 & operator/=(const IVector4 &p_other)
In-place component-wise division.
Definition spk_vector4.hpp:442
IVector4 operator*(const TType &p_scalar) const
Multiplies each component by a scalar.
Definition spk_vector4.hpp:298
IVector4 & operator*=(const TType &p_scalar)
In-place scalar multiplication.
Definition spk_vector4.hpp:494
constexpr IVector4(const IVector2< TOtherVector > &p_other, const TZ &p_z, const TW &p_w)
Builds a vector from a 2D vector plus Z and W components.
Definition spk_vector4.hpp:92
float dot(const IVector4 &p_other) const
Computes dot product with another vector.
Definition spk_vector4.hpp:602
float length() const
Computes Euclidean length.
Definition spk_vector4.hpp:570
constexpr IVector4()=default
Builds a zero vector.
IVector4 inverse() const
Return the inverse vector.
Definition spk_vector4.hpp:376
static IVector4 max(const IVector4 &p_a, const IVector4 &p_b)
Component-wise maximum of two vectors.
Definition spk_vector4.hpp:717
static IVector4 min(const IVector4 &p_a, const IVector4 &p_b)
Component-wise minimum of two vectors.
Definition spk_vector4.hpp:664
IVector3< float > normalize() const
Return the vector once normalized by its length.
Definition spk_vector4.hpp:554
IVector4 & operator-=(const IVector4 &p_other)
In-place component-wise subtraction.
Definition spk_vector4.hpp:411
float squaredLength() const
Computes squared vector length.
Definition spk_vector4.hpp:540
static IVector4 min(std::initializer_list< IVector4 > p_values)
Component-wise minimum across an initializer list.
Definition spk_vector4.hpp:695
friend IVector4 operator-(const TType &p_scalar, const IVector4 &p_vector)
Subtracts vector components from a scalar (scalar on the left side).
Definition spk_vector4.hpp:336
static IVector4 max(std::initializer_list< IVector4 > p_values)
Component-wise maximum across an initializer list.
Definition spk_vector4.hpp:747
static IVector4 clamp(const IVector4 &p_value, const IVector4 &p_boundA, const IVector4 &p_boundB)
Clamps each component of a vector between two bounds.
Definition spk_vector4.hpp:770
static IVector4 lerp(const IVector4 &p_from, const IVector4 &p_to, const TAlpha &p_alpha)
Linearly interpolates between two vectors.
Definition spk_vector4.hpp:639
IVector4 operator+(const IVector4 &p_other) const
Component-wise addition.
Definition spk_vector4.hpp:230
IVector4 operator-() const
Unary minus.
Definition spk_vector4.hpp:385
float_t y
Definition spk_vector4.hpp:47
IVector4 operator*(const IVector4 &p_other) const
Component-wise multiplication.
Definition spk_vector4.hpp:250
friend IVector4 operator*(const TType &p_scalar, const IVector4 &p_vector)
Multiplies a scalar by a vector (scalar on the left side).
Definition spk_vector4.hpp:348
IVector4 clamped(const IVector4 &p_boundA, const IVector4 &p_boundB) const
Returns a clamped copy of this vector.
Definition spk_vector4.hpp:797
friend IVector4 operator+(const TType &p_scalar, const IVector4 &p_vector)
Adds a scalar to a vector (scalar on the left side).
Definition spk_vector4.hpp:325
TType value_type
Component type stored by the vector.
Definition spk_vector4.hpp:38
static const IVector4 Zero
Definition spk_vector4.hpp:149
IVector4 operator/(const TType &p_scalar) const
Divides each component by a scalar.
Definition spk_vector4.hpp:309
bool operator!=(const IVector4 &p_other) const
Negation of equality.
Definition spk_vector4.hpp:220
IVector4 operator/(const IVector4 &p_other) const
Component-wise division.
Definition spk_vector4.hpp:261
bool operator==(const IVector4 &p_other) const
Checks component-wise equality using SafeComparand.
Definition spk_vector4.hpp:206
constexpr IVector4(const TX &p_x, const TY &p_y, const TZ &p_z, const TW &p_w)
Builds a vector from four components.
Definition spk_vector4.hpp:72
friend std::ostream & operator<<(std::ostream &p_stream, const IVector4 &p_value)
Streams the vector to an output stream.
Definition spk_vector4.hpp:183
static const IVector4 One
Definition spk_vector4.hpp:153
friend IVector4 operator/(const TType &p_scalar, const IVector4 &p_vector)
Divides a scalar by each vector component (scalar on the left side).
Definition spk_vector4.hpp:360
bool isZero() const
Checks if all components are zero.
Definition spk_vector4.hpp:527
IVector4 operator+(const TType &p_scalar) const
Adds a scalar to each component.
Definition spk_vector4.hpp:278
constexpr IVector4(const TOther &p_value)
Fills all components with the same value.
Definition spk_vector4.hpp:138
IVector4 & operator/=(const TType &p_scalar)
In-place scalar division.
Definition spk_vector4.hpp:510
friend std::wostream & operator<<(std::wostream &p_stream, const IVector4 &p_value)
Streams the vector to a wide output stream.
Definition spk_vector4.hpp:195
float_t x
Definition spk_vector4.hpp:43
Wraps arithmetic values to compare with tolerance for floating-point inputs.
Definition spk_safe_comparand.hpp:17