[ignition-math4] 01/04: Imported Upstream version 4.0.0~pre2
Jose Luis Rivero
jrivero-guest at moszumanska.debian.org
Mon Nov 20 19:51:21 UTC 2017
This is an automated email from the git hooks/post-receive script.
jrivero-guest pushed a commit to branch master
in repository ignition-math4.
commit 5b753089b763f77f3ea440b2e5de13df6a387d49
Author: Jose Luis Rivero <jrivero at osrfoundation.org>
Date: Mon Nov 20 15:55:53 2017 +0000
Imported Upstream version 4.0.0~pre2
---
.hg_archival.txt | 6 +
AUTHORS | 1 +
CMakeLists.txt | 37 +
COPYING | 178 +
Changelog.md | 185 +
LICENSE | 15 +
Migration.md | 139 +
NEWS | 1 +
README.md | 50 +
bitbucket-pipelines.yml | 24 +
configure.bat | 23 +
doc/CMakeLists.txt | 14 +
doc/footer.html | 1 +
doc/header.html | 67 +
doc/ignition.in | 2306 ++++++++
doc/ignition_logo.svg | 249 +
doc/mainpage.html | 17 +
doc/style.css | 107 +
examples/CMakeLists.txt | 13 +
examples/graph_example.cc | 90 +
examples/triangle_example.cc | 80 +
examples/vector2_example.cc | 66 +
include/CMakeLists.txt | 1 +
include/ignition/CMakeLists.txt | 1 +
include/ignition/math/Angle.hh | 203 +
include/ignition/math/Box.hh | 236 +
include/ignition/math/CMakeLists.txt | 2 +
include/ignition/math/Color.hh | 318 ++
include/ignition/math/Filter.hh | 247 +
include/ignition/math/Frustum.hh | 181 +
include/ignition/math/Helpers.hh | 770 +++
include/ignition/math/Inertial.hh | 244 +
include/ignition/math/Kmeans.hh | 85 +
include/ignition/math/Line2.hh | 315 ++
include/ignition/math/Line3.hh | 388 ++
include/ignition/math/MassMatrix3.hh | 906 ++++
include/ignition/math/MassMatrix3.ipynb | 134 +
include/ignition/math/Matrix3.hh | 544 ++
include/ignition/math/Matrix4.hh | 890 ++++
include/ignition/math/OrientedBox.hh | 187 +
include/ignition/math/PID.hh | 228 +
include/ignition/math/Plane.hh | 231 +
include/ignition/math/Pose3.hh | 411 ++
include/ignition/math/Quaternion.hh | 1083 ++++
include/ignition/math/Rand.hh | 86 +
include/ignition/math/RotationSpline.hh | 121 +
include/ignition/math/SemanticVersion.hh | 159 +
include/ignition/math/SignalStats.hh | 258 +
include/ignition/math/SphericalCoordinates.hh | 232 +
include/ignition/math/Spline.hh | 261 +
include/ignition/math/Temperature.hh | 375 ++
include/ignition/math/Triangle.hh | 242 +
include/ignition/math/Triangle3.hh | 278 +
include/ignition/math/Vector2.hh | 478 ++
include/ignition/math/Vector3.hh | 757 +++
include/ignition/math/Vector3Stats.hh | 107 +
include/ignition/math/Vector4.hh | 589 ++
include/ignition/math/config.hh.in | 15 +
include/ignition/math/graph/Edge.hh | 337 ++
include/ignition/math/graph/Graph.hh | 763 +++
include/ignition/math/graph/GraphAlgorithms.hh | 323 ++
include/ignition/math/graph/Vertex.hh | 140 +
include/ignition/math/math.hh.in | 3 +
src/Angle.cc | 173 +
src/Angle_TEST.cc | 119 +
src/Box.cc | 296 ++
src/BoxPrivate.hh | 38 +
src/Box_TEST.cc | 567 ++
src/CMakeLists.txt | 20 +
src/Color.cc | 583 ++
src/Color_TEST.cc | 379 ++
src/Filter_TEST.cc | 105 +
src/Frustum.cc | 254 +
src/FrustumPrivate.hh | 78 +
src/Frustum_TEST.cc | 472 ++
src/Helpers.cc | 47 +
src/Helpers_TEST.cc | 517 ++
src/Inertial_TEST.cc | 527 ++
src/Kmeans.cc | 172 +
src/KmeansPrivate.hh | 51 +
src/Kmeans_TEST.cc | 142 +
src/Line2_TEST.cc | 268 +
src/Line3_TEST.cc | 293 +
src/MassMatrix3_TEST.cc | 858 +++
src/Matrix3_TEST.cc | 400 ++
src/Matrix4_TEST.cc | 656 +++
src/OrientedBox_TEST.cc | 335 ++
src/PID.cc | 243 +
src/PID_TEST.cc | 402 ++
src/Plane_TEST.cc | 147 +
src/Pose_TEST.cc | 157 +
src/Quaternion_TEST.cc | 568 ++
src/Rand.cc | 92 +
src/Rand_TEST.cc | 79 +
src/RotationSpline.cc | 211 +
src/RotationSplinePrivate.cc | 26 +
src/RotationSplinePrivate.hh | 46 +
src/RotationSpline_TEST.cc | 100 +
src/SemanticVersion.cc | 294 +
src/SemanticVersion_TEST.cc | 217 +
src/SignalStats.cc | 359 ++
src/SignalStatsPrivate.hh | 77 +
src/SignalStats_TEST.cc | 641 +++
src/SphericalCoordinates.cc | 557 ++
src/SphericalCoordinates_TEST.cc | 385 ++
src/Spline.cc | 385 ++
src/SplinePrivate.cc | 156 +
src/SplinePrivate.hh | 207 +
src/Spline_TEST.cc | 179 +
src/Temperature.cc | 325 ++
src/Temperature_TEST.cc | 178 +
src/Triangle3_TEST.cc | 261 +
src/Triangle_TEST.cc | 194 +
src/Vector2_TEST.cc | 304 ++
src/Vector3Stats.cc | 121 +
src/Vector3StatsPrivate.hh | 44 +
src/Vector3Stats_TEST.cc | 189 +
src/Vector3_TEST.cc | 418 ++
src/Vector4_TEST.cc | 314 ++
src/graph/CMakeLists.txt | 7 +
src/graph/Edge_TEST.cc | 225 +
src/graph/GraphAlgorithms_TEST.cc | 312 ++
src/graph/GraphDirected_TEST.cc | 500 ++
src/graph/GraphUndirected_TEST.cc | 534 ++
src/graph/Graph_TEST.cc | 435 ++
src/graph/Vertex_TEST.cc | 69 +
test/CMakeLists.txt | 22 +
test/gtest/cmake/internal_utils.cmake | 227 +
test/gtest/gtest-1.7.0.diff | 44 +
test/gtest/include/gtest/gtest-death-test.h | 294 +
test/gtest/include/gtest/gtest-message.h | 250 +
test/gtest/include/gtest/gtest-param-test.h | 1421 +++++
test/gtest/include/gtest/gtest-param-test.h.pump | 487 ++
test/gtest/include/gtest/gtest-printers.h | 858 +++
test/gtest/include/gtest/gtest-spi.h | 232 +
test/gtest/include/gtest/gtest-test-part.h | 179 +
test/gtest/include/gtest/gtest-typed-test.h | 263 +
test/gtest/include/gtest/gtest.h | 2291 ++++++++
test/gtest/include/gtest/gtest_pred_impl.h | 358 ++
test/gtest/include/gtest/gtest_prod.h | 58 +
.../gtest/internal/gtest-death-test-internal.h | 319 ++
test/gtest/include/gtest/internal/gtest-filepath.h | 206 +
test/gtest/include/gtest/internal/gtest-internal.h | 1158 ++++
.../include/gtest/internal/gtest-linked_ptr.h | 233 +
.../gtest/internal/gtest-param-util-generated.h | 5143 ++++++++++++++++++
.../internal/gtest-param-util-generated.h.pump | 301 ++
.../include/gtest/internal/gtest-param-util.h | 619 +++
test/gtest/include/gtest/internal/gtest-port.h | 1950 +++++++
test/gtest/include/gtest/internal/gtest-string.h | 167 +
test/gtest/include/gtest/internal/gtest-tuple.h | 1012 ++++
.../include/gtest/internal/gtest-tuple.h.pump | 339 ++
.../gtest/include/gtest/internal/gtest-type-util.h | 3331 ++++++++++++
.../include/gtest/internal/gtest-type-util.h.pump | 297 ++
test/gtest/src/gtest-all.cc | 48 +
test/gtest/src/gtest-death-test.cc | 1344 +++++
test/gtest/src/gtest-filepath.cc | 382 ++
test/gtest/src/gtest-internal-inl.h | 1218 +++++
test/gtest/src/gtest-port.cc | 805 +++
test/gtest/src/gtest-printers.cc | 363 ++
test/gtest/src/gtest-test-part.cc | 110 +
test/gtest/src/gtest-typed-test.cc | 110 +
test/gtest/src/gtest.cc | 5019 +++++++++++++++++
test/gtest/src/gtest_main.cc | 38 +
test/integration/CMakeLists.txt | 8 +
test/performance/CMakeLists.txt | 8 +
test/regression/CMakeLists.txt | 8 +
test/test_config.h.in | 1 +
tools/check_test_ran.py | 79 +
tools/code_check.sh | 150 +
tools/cppcheck_rules/header_guard.rule | 10 +
tools/cppcheck_rules/namespace_AZ.rule | 9 +
tools/cpplint.py | 5622 ++++++++++++++++++++
172 files changed, 71497 insertions(+)
diff --git a/.hg_archival.txt b/.hg_archival.txt
new file mode 100644
index 0000000..77fd3c2
--- /dev/null
+++ b/.hg_archival.txt
@@ -0,0 +1,6 @@
+repo: e97318167882c5edd21067dd86576efadebd68ed
+node: 3e9ea893e1e4b477e8eb77e40d7a020f3f1b5527
+branch: ign-math4
+latesttag: ignition-math4_4.0.0~pre1
+latesttagdistance: 3
+changessincelatesttag: 59
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..65032b7
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+OSRFoundation
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..bb99ea8
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,37 @@
+cmake_minimum_required(VERSION 3.5.1 FATAL_ERROR)
+
+#============================================================================
+# Find ignition-cmake
+#============================================================================
+# If you get an error at this line, you need to install ignition-cmake
+find_package(ignition-cmake0 REQUIRED)
+
+#============================================================================
+# Set up the project
+#============================================================================
+ign_configure_project(math 4.0.0 VERSION_SUFFIX pre2)
+
+
+#============================================================================
+# Set project-specific options
+#============================================================================
+
+# ignition-math currently has no options that are unique to it
+
+
+#============================================================================
+# Search for project-specific dependencies
+#============================================================================
+
+# ignition-math currently has no dependencies that are unique to it
+
+#============================================================================
+# Configure the build
+#============================================================================
+ign_configure_build(QUIT_IF_BUILD_ERRORS)
+
+
+#============================================================================
+# Create package information
+#============================================================================
+ign_create_packages()
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..4909afd
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,178 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+
diff --git a/Changelog.md b/Changelog.md
new file mode 100644
index 0000000..f75a6b3
--- /dev/null
+++ b/Changelog.md
@@ -0,0 +1,185 @@
+## Ignition Math 4.x
+
+### Ignition Math 4.x.x
+
+1. Use ignition-cmake to simplify build scripts
+ * [Pull request 200](https://bitbucket.org/ignitionrobotics/ign-math/pull-requests/200)
+
+1. Make constructor SemanticVersion(string) explicit
+ * [Pull request 203](https://bitbucket.org/ignitionrobotics/ign-math/pull-requests/203)
+
+1. Switch to C++14
+ * [Pull request 180](https://bitbucket.org/ignitionrobotics/ign-math/pull-requests/180)
+
+1. Improved the performance of `graph::InDegree()` and `graph::IncidentsTo()`.
+ * [Pull request 188](https://bitbucket.org/ignitionrobotics/ign-math/pull-requests/188)
+ * [Issue 79](https://bitbucket.org/ignitionrobotics/ign-math/issues/79)
+
+1. Removed the box 'extent' field. The default constructor now sets a box's
+ corners to extrema in order to indicate an uninitialized box.
+ * [Pull request 172](https://bitbucket.org/ignitionrobotics/ign-math/pull-requests/172)
+ * [Issue 72](https://bitbucket.org/ignitionrobotics/ign-math/issues/72)
+ * [Issue 53](https://bitbucket.org/ignitionrobotics/ign-math/issues/53)
+
+1. Added graph utilites:
+ 1. Added a Vertex class:
+ * [Pull request 170](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/170)
+ 1. Added an Edge class:
+ * [Pull request 174](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/174)
+ 1. Added a Graph class:
+ * [Pull request 175](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/175)
+ 1. Added a GraphAlgorithms class:
+ * [Pull request 177](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/177)
+ 1. Added a function to calculate connected components in undirected
+ graphs:
+ * [Pull request 190](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/190)
+
+## Ignition Math 3.x
+
+### Ignition Math 3.x.x
+
+1. Create consistent bracket operators across all Vector# types
+ * [Pull request 181](https://bitbucket.org/ignitionrobotics/ign-math/pull-requests/181)
+
+### Ignition Math 3.2.0 (2017-05-15)
+
+1. Construct on first use in Rand class
+ * [Pull request 165](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/165)
+
+1. Extended Spline API: derivative interpolation, arc length calculation
+ and tangent forcing.
+ * [Pull request 162](https://bitbucket.org/ignitionrobotics/ign-math/pull-requests/162)
+
+### Ignition Math 3.1.0 (2017-04-11)
+
+1. Added signum functions to Helpers.hh.
+ * Contribution from Martin Pecka
+ * [Pull request 153](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/153)
+
+### Ignition Math 3.0.0 (2017-01-05)
+
+1. Deprecate many IGN_* macros in favor of static const variables in Helpers.hh
+ * [Pull request 138](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/138)
+ * [Pull request 137](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/137)
+
+1. Removed exceptions. Return values should be evaluated to determine if
+ errors have occured.
+ * [Pull request 132](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/132)
+
+1. Added `operator=(const Quaternion<T> &_q)` to `Matrix3`.
+ * [Pull request 111](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/111)
+
+1. Fix xenial cppcheck
+ * [Pull request xxx](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/xxx)
+
+1. Require cmake 2.8.12
+ * [Pull request 76](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/76)
+
+1. Migrate to relocatable CMake package.
+ Contribution from Silvio Traversaro.
+ * [Pull request 67](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/67)
+
+1. Fix logic of installation of CMake configuration files in Windows.
+ Contribution from Silvio Traversaro.
+ * [Pull request 63](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/63)
+
+## Ignition Math 2.x
+
+### Ignition Math 2.8
+
+### Ignition Math 2.8.0
+
+1. Added Color
+ * [Pull request 150](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/150)
+
+1. Added OrientedBox
+ * [Pull request 146](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/146)
+
+1. Added an assignment operator to the Frustum class.
+ * [Pull request 144](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/144)
+
+### Ignition Math 2.7
+
+### Ignition Math 2.7.0
+
+1. Add static const variables as alternative to macros in Helpers.hh
+ * [Pull request 137](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/137)
+
+1. Add new methods for floating numbers: lessOrEqual and greaterOrEqual
+ * [Pull request 134](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/134)
+
+### Ignition Math 2.6
+
+### Ignition Math 2.6.0
+
+1. Added copy constructor, equality operators and assignment operators to
+ SphericalCoordinates class.
+ * [Pull request 131](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/131)
+
+1. Fix Euler angle conversion of quaternions near singularities
+ * [Pull request 129](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/129)
+
+1. Backport triangle3, helper functions, equality helper to work with 387 fp unit
+ (Contribution from Rich Mattes).
+ * [Pull request 125](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/125)
+ * [Pull request 58](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/58)
+ * [Pull request 56](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/56)
+
+1. Added Matrix4<T>::LookAt
+ * [Pull request 124](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/124)
+
+1. Set Inertial Rotations
+ * [Pull request 121](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/121)
+
+1. Added SemanticVersion class
+ * [Pull request 120](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/120)
+
+### Ignition Math 2.5
+
+### Ignition Math 2.5.0
+
+1. Added PID class
+ * [Pull request 117](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/117)
+
+1. Added SphericalCoordinate class
+ * [Pull request 108](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/108)
+
+### Ignition Math 2.4
+
+#### Ignition Math 2.4.1
+
+1. Combine inertial properties of different objects, returning the equivalent
+ inertial properties as if the objects were welded together.
+ * [Pull request 115](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/115)
+
+#### Ignition Math 2.4.0
+
+1. New MassMatrix3 class
+ * [Pull request 112](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/112)
+1. MassMatrix3 helper functions
+ * [Pull request 110](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/110)
+1. Added Temperature class
+ * A contribution from Shintaro Noda
+ * [Pull request 113](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/113)
+
+### Ignition Math 2.3.0
+
+1. Added simple volumes formulas
+ * [Pull request 84](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/84)
+1. Add Length and SquaredLength for Vector2 with test
+ * [Pull request 73](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/73)
+1. Add Equal function with numerical tolerance argument
+ * [Pull request 75](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/75)
+1. First part of MassMatrix3 class, mostly accessors and modifiers
+ * [Pull request 77](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/77)
+1. Add Transpose methods for Matrix3,4 with test
+ * [Pull request 74](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/74)
+1. Multiplication improvements for Vector/Matrix classes
+ * [Pull request 69](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/69)
+1. Scalar +,- operators for Vector[234]
+ * [Pull request 71](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/71)
+1. Add Determinant method for Matrix[34]
+ * [Pull request 72](https://bitbucket.org/ignitionrobotics/ign-math/pull-requests/72)
+1. Fixes for compiling and running tests on Windows 7/Visual Studio 2013
+ Contribution from Silvio Traversaro.
+ * [Pull request 62](https://bitbucket.org/ignitionrobotics/ign-math/pull-request/62)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..bd33c43
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+Software License Agreement (Apache License)
+
+Copyright 2012 Open Source Robotics Foundation
+
+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.
diff --git a/Migration.md b/Migration.md
new file mode 100644
index 0000000..080663f
--- /dev/null
+++ b/Migration.md
@@ -0,0 +1,139 @@
+# Note on deprecations
+A tick-tock release cycle allows easy migration to new software versions.
+Obsolete code is marked as deprecated for one major release.
+Deprecated code produces compile-time warnings. These warning serve as
+notification to users that their code should be upgraded. The next major
+release will remove the deprecated code.
+
+## Ignition Math 3.X to 4.X
+
+### Added dependencies
+
+1. **ignition-cmake**
+ + Ignition-math now has a build dependency on ignition-cmake, which
+ allows cmake scripts to be shared across all the ignition packages.
+
+### Modifications
+
+1. **Box.hh**
+ + Boxes generated with the default constructor do not intersect any other
+ boxes or contain any points (previously they contained the origin).
+
+1. **SemanticVersion.hh**
+ + The SemanticVersion(const std::string &) constructor is now explicit.
+
+## Ignition Math 2.X to 3.X
+
+### Modifications
+
+1. **RotationSpline.hh**
+ + The `UpdatePoint` function now returns a boolean value.
+
+1. **Spline.hh**
+ + The `UpdatePoint` function now returns a boolean value.
+
+### Deprecations
+
+1. **Matrix4.hh**
+ + ***Deprecation:*** public: Vector3<T> TransformAffine(const Vector3<T>
+ &_v) const
+ + ***Replacement:*** public: bool TransformAffine(const Vector3<T>
+ &_v,Vector3<T> &_result) const
+
+1. **Helpers.hh**
+ + ***Deprecation:*** IGN_DBL_MAX
+ + ***Replacement:*** ignition::math::MAX_D
+
+ + ***Deprecation:*** IGN_DBL_MIN
+ + ***Replacement:*** ignition::math::MIN_D
+
+ + ***Deprecation:*** IGN_DBL_LOW
+ + ***Replacement:*** ignition::math::LOW_D
+
+ + ***Deprecation:*** IGN_DBL_INF
+ + ***Replacement:*** ignition::math::INF_D
+
+ + ***Deprecation:*** IGN_FLT_MAX
+ + ***Replacement:*** ignition::math::MAX_F
+
+ + ***Deprecation:*** IGN_FLT_MIN
+ + ***Replacement:*** ignition::math::MIN_F
+
+ + ***Deprecation:*** IGN_FLT_LOW
+ + ***Replacement:*** ignition::math::LOW_F
+
+ + ***Deprecation:*** IGN_FLT_INF
+ + ***Replacement:*** ignition::math::INF_F
+
+ + ***Deprecation:*** IGN_UI16_MAX
+ + ***Replacement:*** ignition::math::MAX_UI16
+
+ + ***Deprecation:*** IGN_UI16_MIN
+ + ***Replacement:*** ignition::math::MIN_UI16
+
+ + ***Deprecation:*** IGN_UI16_LOW
+ + ***Replacement:*** ignition::math::LOW_UI16
+
+ + ***Deprecation:*** IGN_UI16_INF
+ + ***Replacement:*** ignition::math::INF_UI16
+
+ + ***Deprecation:*** IGN_I16_MAX
+ + ***Replacement:*** ignition::math::MAX_I16
+
+ + ***Deprecation:*** IGN_I16_MIN
+ + ***Replacement:*** ignition::math::MIN_I16
+
+ + ***Deprecation:*** IGN_I16_LOW
+ + ***Replacement:*** ignition::math::LOW_I16
+
+ + ***Deprecation:*** IGN_I16_INF
+ + ***Replacement:*** ignition::math::INF_I16
+
+ + ***Deprecation:*** IGN_UI32_MAX
+ + ***Replacement:*** ignition::math::MAX_UI32
+
+ + ***Deprecation:*** IGN_UI32_MIN
+ + ***Replacement:*** ignition::math::MIN_UI32
+
+ + ***Deprecation:*** IGN_UI32_LOW
+ + ***Replacement:*** ignition::math::LOW_UI32
+
+ + ***Deprecation:*** IGN_UI32_INF
+ + ***Replacement:*** ignition::math::INF_UI32
+
+ + ***Deprecation:*** IGN_I32_MAX
+ + ***Replacement:*** ignition::math::MAX_I32
+
+ + ***Deprecation:*** IGN_I32_MIN
+ + ***Replacement:*** ignition::math::MIN_I32
+
+ + ***Deprecation:*** IGN_I32_LOW
+ + ***Replacement:*** ignition::math::LOW_I32
+
+ + ***Deprecation:*** IGN_I32_INF
+ + ***Replacement:*** ignition::math::INF_I32
+
+ + ***Deprecation:*** IGN_UI64_MAX
+ + ***Replacement:*** ignition::math::MAX_UI64
+
+ + ***Deprecation:*** IGN_UI64_MIN
+ + ***Replacement:*** ignition::math::MIN_UI64
+
+ + ***Deprecation:*** IGN_UI64_LOW
+ + ***Replacement:*** ignition::math::LOW_UI64
+
+ + ***Deprecation:*** IGN_UI64_INF
+ + ***Replacement:*** ignition::math::INF_UI64
+
+ + ***Deprecation:*** IGN_I64_MAX
+ + ***Replacement:*** ignition::math::MAX_I64
+
+ + ***Deprecation:*** IGN_I64_MIN
+ + ***Replacement:*** ignition::math::MIN_I64
+
+ + ***Deprecation:*** IGN_I64_LOW
+ + ***Replacement:*** ignition::math::LOW_I64
+
+ + ***Deprecation:*** IGN_I64_INF
+ + ***Replacement:*** ignition::math::INF_I64
+
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..6b7be6e
--- /dev/null
+++ b/NEWS
@@ -0,0 +1 @@
+http://ignition_robotics.org
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a4d7415
--- /dev/null
+++ b/README.md
@@ -0,0 +1,50 @@
+# Ignition Math
+
+
+**Math classes and functions for robot applications.**
+
+Ignition Math is a component in the Ignition framework, a set of libraries
+designed to rapidly develop robot applications. The library defines math
+classes and functions used in other Ignition libraries and programs.
+
+ [http://ignitionrobotics.org](http://ignitionrobotics.org)
+
+## Continuous integration
+
+This project uses [appveyor](https://ci.appveyor.com/project/scpeters/ign-math/history)
+for testing on Windows.
+It has the following build status:
+
+
+This project also uses [bitbucket pipelines](https://bitbucket.org/ignitionrobotics/ign-math/addon/pipelines/home#!/)
+for testing with Linux.
+
+Test coverage reports are available at Codecov:
+
+[](https://codecov.io/bb/ignitionrobotics/ign-math)
+
+## Required Dependencies
+
+ brew install ignition-cmake
+
+## Optional Dependencies
+
+ sudo apt-get install doxygen
+ brew install doxygen
+
+## Installation
+
+Standard installation can be performed in UNIX systems using the following
+steps:
+
+ - mkdir build/
+ - cd build/
+ - cmake ..
+ - sudo make install
+
+## Uninstallation
+
+To uninstall the software installed with the previous steps:
+
+ - cd build/
+ - sudo make uninstall
diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml
new file mode 100644
index 0000000..0979a22
--- /dev/null
+++ b/bitbucket-pipelines.yml
@@ -0,0 +1,24 @@
+image: ubuntu:xenial
+
+pipelines:
+ default:
+ - step:
+ script:
+ - apt-get update
+ - apt-get -y install cmake build-essential lcov curl mercurial
+ - gcc -v
+ - g++ -v
+ # Ignition cmake
+ - echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-prerelease xenial main" > /etc/apt/sources.list.d/gazebo-prerelease.list
+ - apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys D2486D2DD83DB69272AFE98867170598AF249743
+ - apt-get update
+ - apt-get -y install
+ libignition-cmake-dev
+ # Ignition math
+ - mkdir build
+ - cd build
+ - cmake .. -DCMAKE_BUILD_TYPE=coverage
+ - make
+ - make test
+ - make coverage
+ - bash <(curl -s https://codecov.io/bash)
diff --git a/configure.bat b/configure.bat
new file mode 100644
index 0000000..625cc06
--- /dev/null
+++ b/configure.bat
@@ -0,0 +1,23 @@
+
+:: NOTE: This script is only meant to be used as part of the ignition developers' CI system
+:: Users and developers should build and install this library using cmake and Visual Studio
+
+:: Install dependencies
+call %win_lib% :install_ign_project ign-cmake default
+
+:: Set configuration variables
+ at set build_type=Release
+ at if not "%1"=="" set build_type=%1
+ at echo Configuring for build type %build_type%
+
+:: Go to the directory that this configure.bat file exists in
+cd /d %~dp0
+
+:: Create a build directory and configure
+md build
+cd build
+cmake .. -G "NMake Makefiles" -DCMAKE_INSTALL_PREFIX="%WORKSPACE_INSTALL_DIR%" -DCMAKE_BUILD_TYPE="%build_type%" -DBUILD_TESTING:BOOL=False
+:: Note: We disable testing by default. If the intention is for the CI to build and test
+:: this project, then the CI script will turn it back on.
+
+:: If the caller wants to build and/or install, they should do so after calling this script
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
new file mode 100644
index 0000000..36e4496
--- /dev/null
+++ b/doc/CMakeLists.txt
@@ -0,0 +1,14 @@
+find_package(Doxygen)
+
+if (DOXYGEN_FOUND)
+ configure_file(${CMAKE_SOURCE_DIR}/doc/ignition.in
+ ${CMAKE_BINARY_DIR}/ignition.dox @ONLY)
+
+ add_custom_target(doc
+ # Generate the API documentation
+ ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/ignition.dox
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ COMMAND cp ${CMAKE_SOURCE_DIR}/doc/ignition_logo.svg
+ ${CMAKE_BINARY_DIR}/doxygen/html
+ COMMENT "Generating API documentation with Doxygen" VERBATIM)
+endif()
diff --git a/doc/footer.html b/doc/footer.html
new file mode 100644
index 0000000..71d71c4
--- /dev/null
+++ b/doc/footer.html
@@ -0,0 +1 @@
+<!--</td></tr></table>-->
diff --git a/doc/header.html b/doc/header.html
new file mode 100644
index 0000000..55ace60
--- /dev/null
+++ b/doc/header.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=9"/>
+ <meta name="keywords" content="Ingition: Math">
+ <title>Ignition: $title</title>
+ <link href="tabs.css" rel="stylesheet" type="text/css">
+ <script type="text/javascript" src="jquery.js"></script>
+ <script type="text/javascript" src="dynsections.js"></script>
+ <link href="search/search.css" rel="stylesheet" type="text/css">
+ <script type="text/javascript" src="search/search.js"></script>
+ <script type="text/javascript">
+ $(document).ready(function() { searchBox.OnSelectItem(0); });
+ </script>
+
+ <link href="doxygen.css" rel="stylesheet" type="text/css">
+ <!--<link href="tabs.css" rel="stylesheet" type="text/css">-->
+ <link href="style.css" rel="stylesheet" type="text/css">
+ </head>
+
+<body>
+ <div class="leftbar">
+ <h2 style="text-align:center;">
+ <a href="index.html"><img src="ignition_logo.svg" width="180px"/></a>
+ </h2>
+
+ <div class="menu">
+ <dl>
+ <dt>API</dt>
+ <dd><a href="classes.html">Class List</a></dd>
+ <dd><a href="hierarchy.html">Class Hierarchy</a></dd>
+ <dd><a href="globals.html">Globals</a></dd>
+ <dd><a href="namespacemembers.html">Namespace Members</a></dd>
+ <dd><a href="files.html">Files</a></dd>
+ </dl>
+ </div>
+ <div class="menu">
+ <dl>
+ <dt>Links</dt>
+ <dd><a href="http://ignitionrobotics.org">Ignition Website</a></dd>
+ <dd><a href="https://bitbucket.org/ignitionrobotics/ign-math/issues/new">Report Documentation Issues</a></dd>
+ </dl>
+ </div>
+
+ <div id="MSearchBox" class="MSearchBoxInactive">
+ <span>
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span>
+ </div> <!-- End MSearchBox -->
+
+ <div id="MSearchResultsWindow" style="position: static; display: block; border: none; background-color: #ffffff; width: 18em;">
+ <iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults"
+ style="height: 500px; width: 18em; display: block; text-wrap: unrestricted">
+ </iframe>
+ </div>
+ </div>
+
+ <div id="top">
diff --git a/doc/ignition.in b/doc/ignition.in
new file mode 100644
index 0000000..32cb4bd
--- /dev/null
+++ b/doc/ignition.in
@@ -0,0 +1,2306 @@
+# Doxyfile 1.8.6
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = IgnitionMath
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = @PROJECT_VERSION_FULL@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = YES
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = NO
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = YES
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = YES
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = NO
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = YES
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = YES
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = YES
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE = /tmp/ignition-math_dox.warn
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = @CMAKE_SOURCE_DIR@/doc/mainpage.html \
+ @CMAKE_SOURCE_DIR@/include/ignition/math
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS = *.hh \
+ *.h
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH = examples
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *.cc \
+ *.hh
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = YES
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 3
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER = "@CMAKE_SOURCE_DIR@/doc/header.html"
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER = "@CMAKE_SOURCE_DIR@/doc/footer.html"
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET = "@CMAKE_SOURCE_DIR@/doc/style.css"
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 13
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 76
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Gazebo API Documentation"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = YES
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = letter
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = NO
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH = .
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all refrences to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = NO
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = FreeSans.ttf
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = NO
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = svg
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/doc/ignition_logo.svg b/doc/ignition_logo.svg
new file mode 100644
index 0000000..6fbbc8d
--- /dev/null
+++ b/doc/ignition_logo.svg
@@ -0,0 +1,249 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="473.20953"
+ height="338.49789"
+ id="svg3097"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="ignition_logo.svg">
+ <defs
+ id="defs3099" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.7"
+ inkscape:cx="-8.01737"
+ inkscape:cy="108.51396"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1350"
+ inkscape:window-height="857"
+ inkscape:window-x="457"
+ inkscape:window-y="89"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata3102">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-159.66847,-323.82846)">
+ <g
+ id="g44"
+ transform="matrix(1.25,0,0,-1.25,389.76025,331.23471)">
+ <path
+ d="m 0,0 c 0,-3.272 2.927,-5.924 6.534,-5.924 3.61,0 6.535,2.652 6.535,5.924 0,3.272 -2.925,5.925 -6.535,5.925 C 2.927,5.925 0,3.272 0,0"
+ style="fill:#f1623b;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path46"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g48"
+ transform="matrix(1.25,0,0,-1.25,369.82862,394.56733)">
+ <path
+ d="m 0,0 c 0,-5.404 -4.833,-9.788 -10.797,-9.788 -5.962,0 -10.798,4.384 -10.798,9.788 0,1.761 0.513,3.412 1.412,4.84 1.86,2.956 5.366,4.949 9.386,4.949 C -4.833,9.789 0,5.406 0,0"
+ style="fill:#f1623b;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path50"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g52"
+ transform="matrix(1.25,0,0,-1.25,337.86512,375.95645)">
+ <path
+ d="m 0,0 c 0,-2.816 -2.52,-5.1 -5.626,-5.1 -3.108,0 -5.626,2.284 -5.626,5.1 0,2.816 2.518,5.101 5.626,5.101 C -2.52,5.101 0,2.816 0,0"
+ style="fill:#f1623b;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path54"
+ inkscape:connector-curvature="0" />
+ </g>
+ <path
+ d="m 334.872,370.83683 -7.95625,0 0,-15.96875 7.95625,0 0,15.96875 z"
+ style="fill:#f1623b;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path56"
+ inkscape:connector-curvature="0" />
+ <g
+ id="g58"
+ transform="matrix(1.25,0,0,-1.25,349.28362,389.69421)">
+ <path
+ d="M 0,0 -4.691,-3.898 -15.124,6.425 -10.433,10.321 0,0 z"
+ style="fill:#f1623b;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path60"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g62"
+ transform="matrix(1.25,0,0,-1.25,324.0755,339.67108)">
+ <path
+ d="m 0,0 c 0,0 0.454,-6.8 5.455,-6.8 5.001,0 5.228,5.358 5.228,5.358 0,0 7.954,-11.538 -5.228,-11.538 C -7.729,-12.98 0,0 0,0"
+ style="fill:#f1623b;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path64"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g66"
+ transform="matrix(1.25,0,0,-1.25,422.71925,394.56733)">
+ <path
+ d="m 0,0 c 0,-5.404 4.833,-9.788 10.794,-9.788 5.965,0 10.799,4.384 10.799,9.788 0,1.761 -0.512,3.412 -1.409,4.84 -1.861,2.956 -5.368,4.949 -9.39,4.949 C 4.833,9.789 0,5.406 0,0"
+ style="fill:#f1623b;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path68"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g70"
+ transform="matrix(1.25,0,0,-1.25,454.68275,375.95645)">
+ <path
+ d="m 0,0 c 0,-2.816 2.52,-5.1 5.626,-5.1 3.107,0 5.628,2.284 5.628,5.1 0,2.816 -2.521,5.101 -5.628,5.101 C 2.52,5.101 0,2.816 0,0"
+ style="fill:#f1623b;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path72"
+ inkscape:connector-curvature="0" />
+ </g>
+ <path
+ d="m 457.67575,370.83683 7.9575,0 0,-15.96875 -7.9575,0 0,15.96875 z"
+ style="fill:#f1623b;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path74"
+ inkscape:connector-curvature="0" />
+ <g
+ id="g76"
+ transform="matrix(1.25,0,0,-1.25,443.26312,389.69421)">
+ <path
+ d="M 0,0 4.692,-3.898 15.124,6.425 10.432,10.321 0,0 z"
+ style="fill:#f1623b;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path78"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g80"
+ transform="matrix(1.25,0,0,-1.25,468.47125,339.67108)">
+ <path
+ d="m 0,0 c 0,0 -0.452,-6.8 -5.454,-6.8 -4.999,0 -5.23,5.358 -5.23,5.358 0,0 -7.953,-11.538 5.23,-11.538 C 7.728,-12.98 0,0 0,0"
+ style="fill:#f1623b;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path82"
+ inkscape:connector-curvature="0" />
+ </g>
+ <path
+ d="m 422.7195,379.30433 -52.28125,0 0,-14.42375 52.28125,0 0,14.42375 z m -21.455,-35.80125 0,-6.76125 c 0,-1.81625 -1.62,-3.285 -3.62125,-3.285 -2.00125,0 -3.6225,1.46875 -3.6225,3.285 l 0,6.585 c -20.4625,1.18125 -36.6525,16.37 -36.6525,34.94875 l 0,46.6225 c 0,10.23875 9.1575,12.105 20.4575,12.105 l 37.505,0 c 11.29875,0 20.4575,-1.86625 20.4575,-12.105 l 0,-46.6225 c 0,-17.93 -15.0825,-32.70125 -34.52375,-34.7725"
+ style="fill:#f1623b;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path84"
+ inkscape:connector-curvature="0" />
+ <g
+ id="g86"
+ transform="matrix(1.25,0,0,-1.25,378.879,495.55483)">
+ <path
+ d="m 0,0 c 0,0 1.215,6.836 3.784,6.836 0.135,-6.393 8.377,-10.801 6.187,-21.166 19.458,7.497 20.304,31.971 20.304,31.971 0,0 -0.117,-0.883 4.259,-5.733 7.786,20.727 -10.451,29.928 -10.451,29.928 l -1.348,0.003 C 23.916,40.416 28.458,33.285 15.008,19.623 9.585,25.687 0.048,33.539 5.561,41.89 4.985,41.89 4.394,41.893 3.784,41.893 -20.647,23.148 0,0 0,0"
+ style="fill:#f1623b;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path88"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g90"
+ transform="matrix(1.25,0,0,-1.25,207.40347,592.96504)">
+ <path
+ d="m 0,0 -36.232,0 c -1.087,0 -1.956,0.869 -1.956,2.028 l 0,3.986 c 0,1.16 0.869,2.029 1.956,2.029 l 14.13,0 0,40.29 -6.086,0 c -1.087,0 -1.957,0.87 -1.957,1.957 l 0,4.058 c 0,1.159 0.87,2.028 1.957,2.028 l 20.145,0 c 1.159,0 2.028,-0.869 2.028,-2.028 l 0,-4.058 c 0,-1.087 -0.869,-1.957 -2.028,-1.957 l -6.015,0 0,-40.29 14.058,0 c 1.159,0 2.029,-0.869 2.029,-2.029 l 0,-3.986 C 2.029,0.869 1.159,0 0,0"
+ style="fill:#606163;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path92"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g94"
+ transform="matrix(1.25,0,0,-1.25,260.3051,552.74779)">
+ <path
+ d="m 0,0 -16.087,0 c -4.42,0 -8.043,-3.623 -8.043,-8.044 l 0,-8.044 c 0,-4.42 3.623,-8.043 8.043,-8.043 L 0,-24.131 0,0 z m -8.043,-48.262 -22.174,0 c -1.087,0 -1.957,0.87 -1.957,2.03 l 0,3.986 c 0,1.158 0.87,2.028 1.957,2.028 l 22.174,0 c 4.42,0 8.043,3.623 8.043,8.044 l -16.087,0 c -8.841,0 -16.087,7.246 -16.087,16.086 l 0,8.044 c 0,8.913 7.246,16.16 16.087,16.16 l 22.102,0 c 1.159,0 2.028,-0.942 2.028,-2.03 l 0,-38.26 c 0,-8.842 -7.246,-16.088 -16.086,-16.088"
+ style="fill:#606163;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path96"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g98"
+ transform="matrix(1.25,0,0,-1.25,328.24334,592.96504)">
+ <path
+ d="m 0,0 -3.985,0 c -1.161,0 -2.03,0.869 -2.03,2.028 l 0,22.102 c 0,4.421 -3.623,8.044 -8.043,8.044 l -16.087,0 0,-30.146 C -30.145,0.869 -31.087,0 -32.174,0 l -4.058,0 c -1.087,0 -1.956,0.869 -1.956,2.028 l 0,36.232 c 0,1.088 0.869,2.03 1.956,2.03 l 22.174,0 c 8.84,0 16.086,-7.247 16.086,-16.16 l 0,-22.102 C 2.028,0.869 1.159,0 0,0"
+ style="fill:#606163;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path100"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g102"
+ transform="matrix(1.25,0,0,-1.25,368.55463,532.54879)">
+ <path
+ d="m 0,0 -3.986,0 c -1.159,0 -2.029,0.87 -2.029,1.957 l 0,4.058 c 0,1.159 0.87,2.028 2.029,2.028 l 3.986,0 c 1.159,0 2.029,-0.869 2.029,-2.028 l 0,-4.058 C 2.029,0.87 1.159,0 0,0 m 16.087,-48.333 -36.232,0 c -1.087,0 -1.957,0.869 -1.957,2.028 l 0,3.986 c 0,1.16 0.87,2.029 1.957,2.029 l 14.13,0 0,24.131 -6.087,0 c -1.086,0 -1.957,0.942 -1.957,2.029 l 0,4.057 c 0,1.088 0.871,2.03 1.957,2.03 l 12.102,0 c 1.159,0 2.029,-0.942 2.029,-2.03 l 0,-30.217 14.058,0 c 1.159,0 2.028,-0.869 2 [...]
+ style="fill:#606163;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path104"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g106"
+ transform="matrix(1.25,0,0,-1.25,449.08313,592.96504)">
+ <path
+ d="m 0,0 -6.015,0 c -8.839,0 -16.087,7.246 -16.087,16.086 l 0,16.088 -14.129,0 c -1.088,0 -1.957,0.942 -1.957,2.029 l 0,4.057 c 0,1.088 0.869,2.03 1.957,2.03 l 14.129,0 0,14.058 c 0,1.159 0.871,2.028 2.029,2.028 l 3.986,0 c 1.159,0 2.029,-0.869 2.029,-2.028 l 0,-14.058 14.058,0 c 1.159,0 2.028,-0.942 2.028,-2.03 l 0,-4.057 C 2.028,33.116 1.159,32.174 0,32.174 l -14.058,0 0,-16.088 c 0,-4.42 3.623,-8.043 8.043,-8.043 l 6.015,0 c 1.159,0 2.028,-0.869 2.028,-2.029 l 0,-3.986 C 2.02 [...]
+ style="fill:#606163;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path108"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g110"
+ transform="matrix(1.25,0,0,-1.25,489.39443,532.54879)">
+ <path
+ d="m 0,0 -3.986,0 c -1.159,0 -2.029,0.87 -2.029,1.957 l 0,4.058 c 0,1.159 0.87,2.028 2.029,2.028 l 3.986,0 c 1.159,0 2.029,-0.869 2.029,-2.028 l 0,-4.058 C 2.029,0.87 1.159,0 0,0 m 16.087,-48.333 -36.232,0 c -1.087,0 -1.957,0.869 -1.957,2.028 l 0,3.986 c 0,1.16 0.87,2.029 1.957,2.029 l 14.13,0 0,24.131 -6.087,0 c -1.086,0 -1.957,0.942 -1.957,2.029 l 0,4.057 c 0,1.088 0.871,2.03 1.957,2.03 l 12.102,0 c 1.159,0 2.029,-0.942 2.029,-2.03 l 0,-30.217 14.058,0 c 1.159,0 2.028,-0.869 2 [...]
+ style="fill:#606163;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path112"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g114"
+ transform="matrix(1.25,0,0,-1.25,562.40473,562.80266)">
+ <path
+ d="m 0,0 c 0,4.421 -3.623,8.044 -8.043,8.044 l -8.044,0 c -4.421,0 -8.044,-3.623 -8.044,-8.044 l 0,-8.044 c 0,-4.42 3.623,-8.043 8.044,-8.043 l 8.044,0 c 4.42,0 8.043,3.623 8.043,8.043 L 0,0 z m -8.043,-24.13 -8.044,0 c -8.841,0 -16.087,7.246 -16.087,16.086 l 0,8.044 c 0,8.913 7.246,16.16 16.087,16.16 l 8.044,0 C 0.797,16.16 8.043,8.913 8.043,0 l 0,-8.044 c 0,-8.84 -7.246,-16.086 -16.086,-16.086"
+ style="fill:#606163;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path116"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g118"
+ transform="matrix(1.25,0,0,-1.25,630.34173,592.96504)">
+ <path
+ d="m 0,0 -3.984,0 c -1.161,0 -2.03,0.869 -2.03,2.028 l 0,22.102 c 0,4.421 -3.623,8.044 -8.043,8.044 l -16.088,0 0,-30.146 C -30.145,0.869 -31.086,0 -32.174,0 l -4.056,0 c -1.088,0 -1.958,0.869 -1.958,2.028 l 0,36.232 c 0,1.088 0.87,2.03 1.958,2.03 l 22.173,0 c 8.84,0 16.086,-7.247 16.086,-16.16 l 0,-22.102 C 2.029,0.869 1.16,0 0,0"
+ style="fill:#606163;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path120"
+ inkscape:connector-curvature="0" />
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="516.81549"
+ y="661.64667"
+ id="text3104"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3106"
+ x="516.81549"
+ y="661.64667"
+ style="font-size:48px;fill:#666666">Math</tspan></text>
+ </g>
+</svg>
diff --git a/doc/mainpage.html b/doc/mainpage.html
new file mode 100644
index 0000000..48053a4
--- /dev/null
+++ b/doc/mainpage.html
@@ -0,0 +1,17 @@
+/** \mainpage Ignition: Math API Reference
+
+Welcome to the Ingition Math library API. Should you find problems with this documentation - typos, unclear phrases,
+or insufficient detail - please create a <a
+ href="https://bitbucket.org/osrf/ignition-math/issues/new">new bitbucket issue</a>.
+Include sufficient detail to quickly locate the problematic documentation.
+
+<dl>
+ <dt>API</dt>
+ <dd><a href="classes.html">Class List</a>- Index of all classes in Ignition Math, organized alphabetically</dd><br/>
+ <dd><a href="hierarchy.html">Class Hierarchy</a> - Heirarchical class structure.</dd><br/>
+ <dd><a href="globals.html">Globals</a> - Global defines and variables.</dd><br/>
+ <dd><a href="namespacemembers.html">Namespace Members</a> - Functions and other goodies in the igntion::math namesapce</dd><br/>
+ <dd><a href="files.html">Files</a> - A list of all the files.</dd><br/>
+</dl>
+
+*/
diff --git a/doc/style.css b/doc/style.css
new file mode 100644
index 0000000..43449be
--- /dev/null
+++ b/doc/style.css
@@ -0,0 +1,107 @@
+body, table, div, p, dl {
+ font-family: arial, verdana, sans, sans-serif;
+ background-color: #FFF;
+ font-size: 12px;
+ margin: 0;
+ padding 0;
+}
+
+div#top {
+ margin: 0 0 0 20em;
+}
+div.header {
+ margin-left: 20em;
+}
+
+div.contents {
+ margin-top: 0px;
+ margin-left: 20em;
+ margin-right: 10px;
+}
+
+.floatright
+{
+ float: right;
+ margin: 0 0 1em 1em;
+}
+
+.timestamp {
+ text-align:right;
+ background-color: #DDD;
+ font-size:75%;
+}
+
+
+div.leftbar
+{
+ text-align:left;
+ float: left;
+ border-right: 1px solid #dddddd;
+ width: 18em;
+ margin: 0 0 0 0;
+ padding: 4 4 4 4;
+ background-color: #ffffff;
+ position: fixed;
+ height: 100%;
+}
+
+div.menu {
+ #display:block;
+ background:#ffffff;
+ font-size: 90%;
+ /*border-top: 2px solid #000000;
+ * border-bottom: 2px solid #000000;
+ * */
+ margin: 0 0 10px 0;
+}
+
+div.menu dl {
+ margin-top: 0px;
+ margin-bottom: 5px;
+}
+
+div.menu dt {
+ font-weight:bold;
+ padding:0 4px 4px 4px;
+ font-size: 110%;
+ text-align: left;
+ text-decoration:none;
+}
+
+div.menu dd {
+ font-weight: bold;
+ margin-left: 0px;
+ padding-left: 20px;
+ padding-bottom: 2px;
+ font-size: 100%;
+}
+
+
+div.leftbar img {
+ border:0;
+}
+
+div.submenu dd {
+ font-size: 70%;
+ margin-left: 8px;
+ padding-left: 10px;
+ padding-bottom: 3px;
+}
+
+div.submenu dd .secondline {
+ margin-left: 12px;
+}
+
+#MSearchBox
+{
+ border: 1px solid black;
+ position: static;
+ margin: 10px;
+ display: block;
+ height: 20px;
+}
+
+#MSearchField
+{
+ background:none;
+}
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..538b32c
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
+
+# Find the Ignition-Math library
+find_package(ignition-math4 REQUIRED)
+
+add_executable(vector2_example vector2_example.cc)
+target_link_libraries(vector2_example ${IGNITION-MATH_LIBRARIES})
+
+add_executable(triangle_example triangle_example.cc)
+target_link_libraries(triangle_example ${IGNITION-MATH_LIBRARIES})
+
+add_executable(graph_example graph_example.cc)
+target_link_libraries(graph_example ${IGNITION-MATH_LIBRARIES})
diff --git a/examples/graph_example.cc b/examples/graph_example.cc
new file mode 100644
index 0000000..a30788d
--- /dev/null
+++ b/examples/graph_example.cc
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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 <iostream>
+#include <ignition/math.hh>
+
+int main(int argc, char **argv)
+{
+ // Create a directed graph that is capable of storing integer data in the
+ // vertices and double data on the edges.
+ ignition::math::graph::DirectedGraph<int, double> graph(
+ // Create the vertices, with default data and vertex ids.
+ {
+ {"vertex1"}, {"vertex2"}, {"vertex3"}
+ },
+ // Create the edges, with default data and weight values.
+ {
+ // Edge from vertex 0 to vertex 1. Each number refers to a vertex id.
+ // Vertex ids start from zero.
+ {{0, 1}}, {{1, 2}}
+ });
+
+ // Output in DOT graph format.
+ std::cout << "Graph with default vertex Ids and data, "
+ << "and default edge data.\n"
+ << graph << std::endl;
+
+ // You can assign data to vertices.
+ ignition::math::graph::DirectedGraph<int, double> graph2(
+ // Create the vertices, with custom data and default vertex ids.
+ {
+ {"vertex1", 1}, {"vertex2", 2}, {"vertex3", 10}
+ },
+ // Create the edges, with default data and weight values.
+ {
+ // Edge from vertex 0 to vertex 1. Each number refers to a vertex id
+ // specified above.
+ {{0, 2}}, {{1, 2}}
+ });
+
+ // Output in DOT graph format.
+ std::cout << "Graph with default vertex Ids and custom vertex data, "
+ << "and default edge data.\n"
+ << graph2 << std::endl;
+
+ // It's also possible to specify vertex ids.
+ ignition::math::graph::DirectedGraph<int, double> graph3(
+ // Create the vertices with custom data and vertex ids.
+ {
+ {"vertex1", 1, 2}, {"vertex2", 2, 3}, {"vertex3", 10, 4}
+ },
+ // Create the edges, with custom data and default weight values.
+ {
+ {{2, 3}, 6.3}, {{3, 4}, 4.2}
+ });
+
+ // Output in DOT graph format.
+ std::cout << "Graph with custom vertex Ids and data, and custom edge data.\n"
+ << graph3 << std::endl;
+
+ // Finally, you can also assign weights to the edges.
+ ignition::math::graph::DirectedGraph<int, double> graph4(
+ // Create the vertices with custom data and vertex ids.
+ {
+ {"vertex1", 1, 2}, {"vertex2", 2, 3}, {"vertex3", 10, 4}
+ },
+ // Create the edges, with custom data and default weight values.
+ {
+ {{2, 3}, 6.3, 1.1}, {{3, 4}, 4.2, 2.3}
+ });
+
+ // Output in DOT graph format.
+ std::cout << "Graph with custom vertex Ids and data, "
+ << "and custom edge data and weights.\n"
+ << graph4 << std::endl;
+}
diff --git a/examples/triangle_example.cc b/examples/triangle_example.cc
new file mode 100644
index 0000000..ab92d89
--- /dev/null
+++ b/examples/triangle_example.cc
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <iostream>
+#include <ignition/math.hh>
+
+int main(int argc, char **argv)
+{
+ // Create a triangle with the following vertices:
+ // 1: x = -1, y = 0
+ // 2: x = 0, y = 1
+ // 3: x = 1, y = 0
+ ignition::math::Triangled tri(
+ ignition::math::Vector2d(-1, 0),
+ ignition::math::Vector2d(0, 1),
+ ignition::math::Vector2d(1, 0));
+
+ // The individual vertices are accessible through the [] operator
+ std::cout << "Vertex 1: " << tri[0] << "\n"
+ << "Vertex 2: " << tri[1] << "\n"
+ << "Vertex 3: " << tri[2] << "\n";
+
+ // Each side of the triangle is also accessible via the Side function
+ std::cout << "Side 1: " << tri.Side(0) << "\n"
+ << "Side 2: " << tri.Side(1) << "\n"
+ << "Side 3: " << tri.Side(2) << "\n";
+
+ // It's also possible to set each vertex individually.
+ tri.Set(0, ignition::math::Vector2d(-10, 0));
+ tri.Set(1, ignition::math::Vector2d(0, 20));
+ tri.Set(2, ignition::math::Vector2d(10, 2));
+
+ // Or set all the vertices at once.
+ tri.Set(ignition::math::Vector2d(-1, 0),
+ ignition::math::Vector2d(0, 1),
+ ignition::math::Vector2d(1, 0));
+
+ // You can get the perimeter length and area of the triangle
+ std::cout << "Perimeter=" << tri.Perimeter()
+ << " Area=" << tri.Area() << "\n";
+
+ // The Contains functions check if a line or point is inside the triangle
+ if (tri.Contains(ignition::math::Vector2d(0, 0.5)))
+ std::cout << "Triangle contains the point 0, 0.5\n";
+ else
+ std::cout << "Triangle does not contain the point 0, 0.5\n";
+
+ // The Intersect function check if a line segment intersects the triangle.
+ // It also returns the points of intersection
+ ignition::math::Vector2d pt1, pt2;
+ if (tri.Intersects(ignition::math::Line2d(-2, 0.5, 2, 0.5), pt1, pt2))
+ {
+ std::cout << "A line from (-2, 0.5) to (2, 0.5) intersects "
+ << "the triangle at the\nfollowing points:\n"
+ << "\t Pt1=" << pt1 << "\n"
+ << "\t Pt2=" << pt2 << "\n";
+ }
+ else
+ {
+ std::cout << "A line from (-2, 0.5) to (2, 0.5) does not intersect "
+ << "the triangle\n";
+ }
+
+ // There are more functions in Triangle. Take a look at the API;
+ // http://ignitionrobotics.org/libraries/ign_mat/api
+}
diff --git a/examples/vector2_example.cc b/examples/vector2_example.cc
new file mode 100644
index 0000000..9d16441
--- /dev/null
+++ b/examples/vector2_example.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <iostream>
+#include <ignition/math.hh>
+
+int main(int argc, char **argv)
+{
+ // Create a Vector2 called vec2 of doubles using the typedef Vector2d.
+ // The initial x any y values are zero.\n\n";
+ ignition::math::Vector2d vec2;
+
+ // The x and y component of vec2 can be set at anytime.
+ vec2.Set(2.0, 4.0);
+
+ // The Vector2 class is a template, so you can also create a Vector2 using
+ // ignition::math::Vector2<double>
+ ignition::math::Vector2<double> vec2a;
+
+ vec2a.Set(1.0, 2.0);
+
+ // It's also possible to set initial values. This time we are using
+ // a Vector2 of floats
+ ignition::math::Vector2f vec2b(1.2f, 3.4f);
+
+ // We can output the contents of each vector using std::cout
+ std::cout << "Vec2: " << vec2 << "\n"
+ << "Vec2a: " << vec2a << "\n"
+ << "Vec2b: " << vec2b << "\n";
+
+ // You can also get access to each component in the vector using the
+ // X(), Y() accessors or the [] operator.
+ std::cout << "Vec2: x=" << vec2.X() << " y=" << vec2.Y() << "\n";
+ std::cout << "Vec2a: x=" << vec2a[0] << " y=" << vec2a[1] << "\n";
+ std::cout << "Vec2b: x=" << vec2b.X() << " y=" << vec2b[1] << "\n";
+
+ // The [] operator is clamped to the range [0, 1]
+ std::cout << vec2[3] << std::endl;
+
+ // The Vector2 class overloads many common operators
+ std::cout << vec2 * vec2a << "\n"
+ << vec2 + vec2a << "\n"
+ << vec2 - vec2a << "\n"
+ << vec2 / vec2a << "\n";
+
+ // There are also many useful function such as finding the distance
+ // between two vectors
+ std::cout << vec2.Distance(vec2a) << std::endl;
+
+ // There are more functions in Vector2. Take a look at the API;
+ // http://ignitionrobotics.org/libraries/ign_mat/api
+}
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
new file mode 100644
index 0000000..25ec897
--- /dev/null
+++ b/include/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(ignition)
diff --git a/include/ignition/CMakeLists.txt b/include/ignition/CMakeLists.txt
new file mode 100644
index 0000000..2767b4e
--- /dev/null
+++ b/include/ignition/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(math)
diff --git a/include/ignition/math/Angle.hh b/include/ignition/math/Angle.hh
new file mode 100644
index 0000000..3a8bfb2
--- /dev/null
+++ b/include/ignition/math/Angle.hh
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_ANGLE_HH_
+#define IGNITION_MATH_ANGLE_HH_
+
+#include <iostream>
+#include <ignition/math/Helpers.hh>
+
+/// \brief Macro that converts radians to degrees
+/// \param[in] radians
+/// \return degrees
+#define IGN_RTOD(r) ((r) * 180 / IGN_PI)
+
+/// \brief Converts degrees to radians
+/// \param[in] degrees
+/// \return radians
+#define IGN_DTOR(d) ((d) * IGN_PI / 180)
+
+/// \brief Macro that normalizes an angle in the range -Pi to Pi
+/// \param[in] angle
+/// \return the angle, in range
+#define IGN_NORMALIZE(a) (atan2(sin(a), cos(a)))
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class Angle Angle.hh ignition/math/Angle.hh
+ /// \brief An angle and related functions.
+ class IGNITION_MATH_VISIBLE Angle
+ {
+ /// \brief math::Angle(0)
+ public: static const Angle Zero;
+
+ /// \brief math::Angle(IGN_PI)
+ public: static const Angle Pi;
+
+ /// \brief math::Angle(IGN_PI * 0.5)
+ public: static const Angle HalfPi;
+
+ /// \brief math::Angle(IGN_PI * 2)
+ public: static const Angle TwoPi;
+
+ /// \brief Constructor
+ public: Angle();
+
+ /// \brief Conversion Constructor
+ /// \param[in] _radian Radians
+ // cppcheck-suppress noExplicitConstructor
+ public: Angle(const double _radian);
+
+ /// \brief Copy constructor
+ /// \param[in] _angle Angle to copy
+ public: Angle(const Angle &_angle);
+
+ /// \brief Destructor
+ public: virtual ~Angle();
+
+ /// \brief Set the value from an angle in radians
+ /// \param[in] _radian Radian value
+ public: void Radian(double _radian);
+
+ /// \brief Set the value from an angle in degrees
+ /// \param[in] _degree Degree value
+ public: void Degree(double _degree);
+
+ /// \brief Get the angle in radians
+ /// \return double containing the angle's radian value
+ public: double Radian() const;
+
+ /// \brief Get the angle in degrees
+ /// \return double containing the angle's degree value
+ public: double Degree() const;
+
+ /// \brief Normalize the angle in the range -Pi to Pi
+ public: void Normalize();
+
+ /// \brief Return the angle's radian value
+ /// \return double containing the angle's radian value
+ public: double operator()() const;
+
+ /// \brief Dereference operator
+ /// \return Double containing the angle's radian value
+ public: inline double operator*() const
+ {
+ return value;
+ }
+
+ /// \brief Substraction, result = this - _angle
+ /// \param[in] _angle Angle for substraction
+ /// \return the new angle
+ public: Angle operator-(const Angle &_angle) const;
+
+ /// \brief Addition operator, result = this + _angle
+ /// \param[in] _angle Angle for addition
+ /// \return the new angle
+ public: Angle operator+(const Angle &_angle) const;
+
+ /// \brief Multiplication operator, result = this * _angle
+ /// \param[in] _angle Angle for multiplication
+ /// \return the new angle
+ public: Angle operator*(const Angle &_angle) const;
+
+ /// \brief Division, result = this / _angle
+ /// \param[in] _angle Angle for division
+ /// \return the new angle
+ public: Angle operator/(const Angle &_angle) const;
+
+ /// \brief Subtraction set, this = this - _angle
+ /// \param[in] _angle Angle for subtraction
+ /// \return angle
+ public: Angle operator-=(const Angle &_angle);
+
+ /// \brief Addition set, this = this + _angle
+ /// \param[in] _angle Angle for addition
+ /// \return angle
+ public: Angle operator+=(const Angle &_angle);
+
+ /// \brief Multiplication set, this = this * _angle
+ /// \param[in] _angle Angle for multiplication
+ /// \return angle
+ public: Angle operator*=(const Angle &_angle);
+
+ /// \brief Division set, this = this / _angle
+ /// \param[in] _angle Angle for division
+ /// \return angle
+ public: Angle operator/=(const Angle &_angle);
+
+ /// \brief Equality operator, result = this == _angle
+ /// \param[in] _angle Angle to check for equality
+ /// \return true if this == _angle
+ public: bool operator==(const Angle &_angle) const;
+
+ /// \brief Inequality
+ /// \param[in] _angle Angle to check for inequality
+ /// \return true if this != _angle
+ public: bool operator!=(const Angle &_angle) const;
+
+ /// \brief Less than operator
+ /// \param[in] _angle Angle to check
+ /// \return true if this < _angle
+ public: bool operator<(const Angle &_angle) const;
+
+ /// \brief Less or equal operator
+ /// \param[in] _angle Angle to check
+ /// \return true if this <= _angle
+ public: bool operator<=(const Angle &_angle) const;
+
+ /// \brief Greater than operator
+ /// \param[in] _angle Angle to check
+ /// \return true if this > _angle
+ public: bool operator>(const Angle &_angle) const;
+
+ /// \brief Greater or equal operator
+ /// \param[in] _angle Angle to check
+ /// \return true if this >= _angle
+ public: bool operator>=(const Angle &_angle) const;
+
+ /// \brief Stream insertion operator. Outputs in degrees
+ /// \param[in] _out output stream
+ /// \param[in] _a angle to output
+ /// \return The output stream
+ public: friend std::ostream &operator<<(std::ostream &_out,
+ const ignition::math::Angle &_a)
+ {
+ _out << _a.Radian();
+ return _out;
+ }
+
+ /// \brief Stream extraction operator. Assumes input is in radians
+ /// \param in input stream
+ /// \param pt angle to read value into
+ /// \return The input stream
+ public: friend std::istream &operator>>(std::istream &_in,
+ ignition::math::Angle &_a)
+ {
+ // Skip white spaces
+ _in.setf(std::ios_base::skipws);
+ _in >> _a.value;
+ return _in;
+ }
+
+ /// The angle in radians
+ private: double value;
+ };
+ }
+}
+
+#endif
diff --git a/include/ignition/math/Box.hh b/include/ignition/math/Box.hh
new file mode 100644
index 0000000..327c31a
--- /dev/null
+++ b/include/ignition/math/Box.hh
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_BOX_HH_
+#define IGNITION_MATH_BOX_HH_
+
+#include <iostream>
+#include <tuple>
+#include <ignition/math/Helpers.hh>
+#include <ignition/math/Vector3.hh>
+#include <ignition/math/Line3.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ // Forward declaration of private data
+ class BoxPrivate;
+
+ /// \class Box Box.hh ignition/math/Box.hh
+ /// \brief Mathematical representation of a box and related functions.
+ class IGNITION_MATH_VISIBLE Box
+ {
+ /// \brief Default constructor. This constructor will set the box's
+ /// minimum and maximum corners to the highest (max) and lowest
+ /// floating point values available to indicate that it is uninitialized.
+ /// The default box does not intersect any other boxes or contain any
+ /// points since it has no extent. Its center is the origin and its side
+ /// lengths are 0.
+ public: Box();
+
+ /// \brief Constructor. This constructor will compute the box's
+ /// minimum and maximum corners based on the two arguments.
+ /// \param[in] _vec1 One corner of the box
+ /// \param[in] _vec2 Another corner of the box
+ public: Box(const Vector3d &_vec1, const Vector3d &_vec2);
+
+ /// \brief Constructor. This constructor will compute the box's
+ /// minimum and maximum corners based on the arguments.
+ /// \param[in] _vec1X One corner's X position
+ /// \param[in] _vec1Y One corner's Y position
+ /// \param[in] _vec1Z One corner's Z position
+ /// \param[in] _vec2X Other corner's X position
+ /// \param[in] _vec2Y Other corner's Y position
+ /// \param[in] _vec2Z Other corner's Z position
+ public: Box(double _vec1X, double _vec1Y, double _vec1Z,
+ double _vec2X, double _vec2Y, double _vec2Z);
+
+ /// \brief Copy Constructor
+ /// \param[in] _b Box to copy
+ public: Box(const Box &_b);
+
+ /// \brief Destructor
+ public: virtual ~Box();
+
+ /// \brief Get the length along the x dimension
+ /// \return Double value of the length in the x dimension
+ public: double XLength() const;
+
+ /// \brief Get the length along the y dimension
+ /// \return Double value of the length in the y dimension
+ public: double YLength() const;
+
+ /// \brief Get the length along the z dimension
+ /// \return Double value of the length in the z dimension
+ public: double ZLength() const;
+
+ /// \brief Get the size of the box
+ /// \return Size of the box
+ public: math::Vector3d Size() const;
+
+ /// \brief Get the box center
+ /// \return The center position of the box
+ public: math::Vector3d Center() const;
+
+ /// \brief Merge a box with this box
+ /// \param[in] _box Box to add to this box
+ public: void Merge(const Box &_box);
+
+ /// \brief Assignment operator. Set this box to the parameter
+ /// \param[in] _b Box to copy
+ /// \return The new box.
+ public: Box &operator=(const Box &_b);
+
+ /// \brief Addition operator. result = this + _b
+ /// \param[in] _b Box to add
+ /// \return The new box
+ public: Box operator+(const Box &_b) const;
+
+ /// \brief Addition set operator. this = this + _b
+ /// \param[in] _b Box to add
+ /// \return This new box
+ public: const Box &operator+=(const Box &_b);
+
+ /// \brief Equality test operator
+ /// \param[in] _b Box to test
+ /// \return True if equal
+ public: bool operator==(const Box &_b) const;
+
+ /// \brief Inequality test operator
+ /// \param[in] _b Box to test
+ /// \return True if not equal
+ public: bool operator!=(const Box &_b) const;
+
+ /// \brief Subtract a vector from the min and max values
+ /// \param _v The vector to use during subtraction
+ /// \return The new box
+ public: Box operator-(const Vector3d &_v);
+
+ /// \brief Output operator
+ /// \param[in] _out Output stream
+ /// \param[in] _b Box to output to the stream
+ /// \return The stream
+ public: friend std::ostream &operator<<(std::ostream &_out,
+ const ignition::math::Box &_b)
+ {
+ _out << "Min[" << _b.Min() << "] Max[" << _b.Max() << "]";
+ return _out;
+ }
+
+ /// \brief Get the minimum corner.
+ /// \return The Vector3d that is the minimum corner of the box.
+ public: const Vector3d &Min() const;
+
+ /// \brief Get the maximum corner.
+ /// \return The Vector3d that is the maximum corner of the box.
+ public: const Vector3d &Max() const;
+
+ /// \brief Get a mutable version of the minimum corner.
+ /// \return The Vector3d that is the minimum corner of the box.
+ public: Vector3d &Min();
+
+ /// \brief Get a mutable version of the maximum corner.
+ /// \return The Vector3d that is the maximum corner of the box.
+ public: Vector3d &Max();
+
+ /// \brief Test box intersection. This test will only work if
+ /// both box's minimum corner is less than or equal to their
+ /// maximum corner.
+ /// \param[in] _box Box to check for intersection with this box.
+ /// \return True if this box intersects _box.
+ public: bool Intersects(const Box &_box) const;
+
+ /// \brief Check if a point lies inside the box.
+ /// \param[in] _p Point to check.
+ /// \return True if the point is inside the box.
+ public: bool Contains(const Vector3d &_p) const;
+
+ /// \brief Check if a ray (origin, direction) intersects the box.
+ /// \param[in] _origin Origin of the ray.
+ /// \param[in] _dir Direction of the ray. This ray will be normalized.
+ /// \param[in] _min Minimum allowed distance.
+ /// \param[in] _max Maximum allowed distance.
+ /// \return A boolean
+ public: bool IntersectCheck(const Vector3d &_origin, const Vector3d &_dir,
+ const double _min, const double _max) const;
+
+ /// \brief Check if a ray (origin, direction) intersects the box.
+ /// \param[in] _origin Origin of the ray.
+ /// \param[in] _dir Direction of the ray. This ray will be normalized.
+ /// \param[in] _min Minimum allowed distance.
+ /// \param[in] _max Maximum allowed distance.
+ /// \return A boolean and double tuple. The boolean value is true
+ /// if the line intersects the box.
+ ///
+ /// The double is the distance from
+ /// the ray's start to the closest intersection point on the box,
+ /// minus the _min distance. For example, if _min == 0.5 and the
+ /// intersection happens at a distance of 2.0 from _origin then returned
+ /// distance is 1.5.
+ ///
+ /// The double value is zero when the boolean value is false.
+ public: std::tuple<bool, double> IntersectDist(
+ const Vector3d &_origin, const Vector3d &_dir,
+ const double _min, const double _max) const;
+
+ /// \brief Check if a ray (origin, direction) intersects the box.
+ /// \param[in] _origin Origin of the ray.
+ /// \param[in] _dir Direction of the ray. This ray will be normalized.
+ /// \param[in] _min Minimum allowed distance.
+ /// \param[in] _max Maximum allowed distance.
+ /// \return A boolean, double, Vector3d tuple. The boolean value is true
+ /// if the line intersects the box.
+ ///
+ /// The double is the distance from the ray's start to the closest
+ /// intersection point on the box,
+ /// minus the _min distance. For example, if _min == 0.5 and the
+ /// intersection happens at a distance of 2.0 from _origin then returned
+ /// distance is 1.5.
+ /// The double value is zero when the boolean value is false. The
+ ///
+ /// Vector3d is the intersection point on the box. The Vector3d value
+ /// is zero if the boolean value is false.
+ public: std::tuple<bool, double, Vector3d> Intersect(
+ const Vector3d &_origin, const Vector3d &_dir,
+ const double _min, const double _max) const;
+
+ /// \brief Check if a line intersects the box.
+ /// \param[in] _line The line to check against this box.
+ /// \return A boolean, double, Vector3d tuple. The boolean value is true
+ /// if the line intersects the box. The double is the distance from
+ /// the line's start to the closest intersection point on the box.
+ /// The double value is zero when the boolean value is false. The
+ /// Vector3d is the intersection point on the box. The Vector3d value
+ /// is zero if the boolean value is false.
+ public: std::tuple<bool, double, Vector3d> Intersect(
+ const Line3d &_line) const;
+
+ /// \brief Clip a line to a dimension of the box.
+ /// This is a helper function to Intersects
+ /// \param[in] _d Dimension of the box(0, 1, or 2).
+ /// \param[in] _line Line to clip
+ /// \param[in,out] _low Close distance
+ /// \param[in,out] _high Far distance
+ private: bool ClipLine(const int _d, const Line3d &_line,
+ double &_low, double &_high) const;
+
+ /// \brief Private data pointer
+ private: BoxPrivate *dataPtr;
+ };
+ }
+}
+#endif
diff --git a/include/ignition/math/CMakeLists.txt b/include/ignition/math/CMakeLists.txt
new file mode 100644
index 0000000..5ff7092
--- /dev/null
+++ b/include/ignition/math/CMakeLists.txt
@@ -0,0 +1,2 @@
+
+ign_install_all_headers(ADDITIONAL_DIRS graph)
diff --git a/include/ignition/math/Color.hh b/include/ignition/math/Color.hh
new file mode 100644
index 0000000..cb07d7a
--- /dev/null
+++ b/include/ignition/math/Color.hh
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_COLOR_HH_
+#define IGNITION_MATH_COLOR_HH_
+
+#include <iostream>
+
+#include <ignition/math/Helpers.hh>
+#include <ignition/math/Vector3.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class Color Color.hh ignition/math/Color.hh
+ /// \brief Defines a color using a red (R), green (G), blue (B), and alpha
+ /// (A) component. Each color component is in the range [0..1].
+ class IGNITION_MATH_VISIBLE Color
+ {
+ /// \brief (1, 1, 1)
+ public: static const Color White;
+ /// \brief (0, 0, 0)
+ public: static const Color Black;
+ /// \brief (1, 0, 0)
+ public: static const Color Red;
+ /// \brief (0, 1, 0)
+ public: static const Color Green;
+ /// \brief (0, 0, 1)
+ public: static const Color Blue;
+ /// \brief (1, 1, 0)
+ public: static const Color Yellow;
+ /// \brief (1, 0, 1)
+ public: static const Color Magenta;
+ /// \brief (0, 1, 1)
+ public: static const Color Cyan;
+
+ /// \def RGBA
+ /// \brief A RGBA packed value as an unsigned int
+ public: typedef unsigned int RGBA;
+
+ /// \def BGRA
+ /// \brief A BGRA packed value as an unsigned int
+ public: typedef unsigned int BGRA;
+
+ /// \def ARGB
+ /// \brief A ARGB packed value as an unsigned int
+ public: typedef unsigned int ARGB;
+
+ /// \def ABGR
+ /// \brief A ABGR packed value as an unsigned int
+ public: typedef unsigned int ABGR;
+
+ /// \brief Constructor
+ public: Color();
+
+ /// \brief Constructor
+ /// \param[in] _r Red value (range 0 to 1)
+ /// \param[in] _g Green value (range 0 to 1
+ /// \param[in] _b Blue value (range 0 to 1
+ /// \param[in] _a Alpha value (0=transparent, 1=opaque)
+ public: Color(const float _r, const float _g, const float _b,
+ const float _a = 1.0);
+
+ /// \brief Copy Constructor
+ /// \param[in] _clr Color to copy
+ public: Color(const Color &_clr);
+
+ /// \brief Destructor
+ public: virtual ~Color();
+
+ /// \brief Reset the color to default values to red=0, green=0,
+ /// blue=0, alpha=1.
+ public: void Reset();
+
+ /// \brief Set the contents of the vector
+ /// \param[in] _r Red value (range 0 to 1)
+ /// \param[in] _g Green value (range 0 to 1)
+ /// \param[in] _b Blue value (range 0 to 1)
+ /// \param[in] _a Alpha value (0=transparent, 1=opaque)
+ public: void Set(const float _r = 1, const float _g = 1,
+ const float _b = 1, const float _a = 1);
+
+ /// \brief Get the color in HSV colorspace
+ /// \return HSV values in a Vector3f format. A vector3f containing
+ /// {NAN_F, NAN_F, NAN_F} is returned on error.
+ public: Vector3f HSV() const;
+
+ /// \brief Set a color based on HSV values
+ /// \param[in] _h Hue(0..360)
+ /// \param[in] _s Saturation(0..1)
+ /// \param[in] _v Value(0..1)
+ public: void SetFromHSV(const float _h, const float _s, const float _v);
+
+ /// \brief Get the color in YUV colorspace
+ /// \return the YUV color
+ public: Vector3f YUV() const;
+
+ /// \brief Set from yuv
+ /// \param[in] _y value
+ /// \param[in] _u value
+ /// \param[in] _v value
+ public: void SetFromYUV(const float _y, const float _u, const float _v);
+
+ /// \brief Equal operator
+ /// \param[in] _pt Color to copy
+ /// \return Reference to this color
+ public: Color &operator=(const Color &_pt);
+
+ /// \brief Array index operator
+ /// \param[in] _index Color component index(0=red, 1=green, 2=blue)
+ /// \return r, g, b, or a when _index is 0, 1, 2 or 3. A NAN_F value is
+ /// returned if the _index is invalid
+ public: float operator[](const unsigned int _index);
+
+ /// \brief Get as uint32 RGBA packed value
+ /// \return the color
+ public: RGBA AsRGBA() const;
+
+ /// \brief Get as uint32 BGRA packed value
+ /// \return the color
+ public: BGRA AsBGRA() const;
+
+ /// \brief Get as uint32 ARGB packed value
+ /// \return the color
+ public: ARGB AsARGB() const;
+
+ /// \brief Get as uint32 ABGR packed value
+ /// \return the color
+ public: ABGR AsABGR() const;
+
+ /// \brief Set from uint32 RGBA packed value
+ /// \param[in] _v the new color
+ public: void SetFromRGBA(const RGBA _v);
+
+ /// \brief Set from uint32 BGRA packed value
+ /// \param[in] _v the new color
+ public: void SetFromBGRA(const BGRA _v);
+
+ /// \brief Set from uint32 ARGB packed value
+ /// \param[in] _v the new color
+ public: void SetFromARGB(const ARGB _v);
+
+ /// \brief Set from uint32 ABGR packed value
+ /// \param[in] _v the new color
+ public: void SetFromABGR(const ABGR _v);
+
+ /// \brief Addition operator (this + _pt)
+ /// \param[in] _pt Color to add
+ /// \return The resulting color
+ public: Color operator+(const Color &_pt) const;
+
+ /// \brief Add _v to all color components
+ /// \param[in] _v Value to add to each color component
+ /// \return The resulting color
+ public: Color operator+(const float &_v) const;
+
+ /// \brief Addition equal operator
+ /// \param[in] _pt Color to add
+ /// \return The resulting color
+ public: const Color &operator+=(const Color &_pt);
+
+ /// \brief Subtraction operator
+ /// \param[in] _pt The color to substract
+ /// \return The resulting color
+ public: Color operator-(const Color &_pt) const;
+
+ /// \brief Subtract _v from all color components
+ /// \param[in] _v Value to subtract
+ /// \return The resulting color
+ public: Color operator-(const float &_v) const;
+
+ /// \brief Subtraction equal operator
+ /// \param[in] _pt Color to subtract
+ /// \return The resulting color
+ public: const Color &operator-=(const Color &_pt);
+
+ /// \brief Division operator
+ /// \param[in] _pt Color to divide by
+ /// \return The resulting color
+ public: const Color operator/(const Color &_pt) const;
+
+ /// \brief Divide all color component by _v
+ /// \param[in] _v The value to divide by
+ /// \return The resulting color
+ public: const Color operator/(const float &_v) const;
+
+ /// \brief Division equal operator
+ /// \param[in] _pt Color to divide by
+ /// \return The resulting color
+ public: const Color &operator/=(const Color &_pt);
+
+ /// \brief Multiplication operator
+ /// \param[in] _pt The color to muliply by
+ /// \return The resulting color
+ public: const Color operator*(const Color &_pt) const;
+
+ /// \brief Multiply all color components by _v
+ /// \param[in] _v The value to multiply by
+ /// \return The resulting color
+ public: const Color operator*(const float &_v) const;
+
+ /// \brief Multiplication equal operator
+ /// \param[in] _pt The color to muliply by
+ /// \return The resulting color
+ public: const Color &operator*=(const Color &_pt);
+
+ /// \brief Equality operator
+ /// \param[in] _pt The color to check for equality
+ /// \return True if the this color equals _pt
+ public: bool operator==(const Color &_pt) const;
+
+ /// \brief Inequality operator
+ /// \param[in] _pt The color to check for inequality
+ /// \return True if the this color does not equal _pt
+ public: bool operator!=(const Color &_pt) const;
+
+ /// \brief Clamp the color values to valid ranges
+ private: void Clamp();
+
+ /// \brief Stream insertion operator
+ /// \param[in] _out the output stream
+ /// \param[in] _pt the color
+ /// \return the output stream
+ public: friend std::ostream &operator<<(std::ostream &_out,
+ const Color &_pt)
+ {
+ _out << _pt.r << " " << _pt.g << " " << _pt.b << " " << _pt.a;
+ return _out;
+ }
+
+ /// \brief Stream insertion operator
+ /// \param[in] _in the input stream
+ /// \param[in] _pt
+ public: friend std::istream &operator>> (std::istream &_in, Color &_pt)
+ {
+ // Skip white spaces
+ _in.setf(std::ios_base::skipws);
+ _in >> _pt.r >> _pt.g >> _pt.b >> _pt.a;
+ return _in;
+ }
+
+ /// \brief Get the red value
+ /// \return The red value
+ public: float R() const;
+
+ /// \brief Get the green value
+ /// \return The green value
+ public: float G() const;
+
+ /// \brief Get the blue value
+ /// \return The blue value
+ public: float B() const;
+
+ /// \brief Get the alpha value
+ /// \return The alpha value
+ public: float A() const;
+
+ /// \brief Get a mutable reference to the red value
+ /// \return The red value
+ public: float &R();
+
+ /// \brief Get a mutable reference to the green value
+ /// \return The green value
+ public: float &G();
+
+ /// \brief Get a mutable reference to the blue value
+ /// \return The blue value
+ public: float &B();
+
+ /// \brief Get a mutable reference to the alpha value
+ /// \return The alpha value
+ public: float &A();
+
+ /// \brief Set the red value
+ /// \param _r New red value
+ public: void R(const float _r);
+
+ /// \brief Set the green value
+ /// \param _r New green value
+ public: void G(const float _g);
+
+ /// \brief Set the blue value
+ /// \param _r New blue value
+ public: void B(const float _b);
+
+ /// \brief Set the alpha value
+ /// \param _r New alpha value
+ public: void A(const float _a);
+
+ /// \brief Red value
+ private: float r = 0;
+
+ /// \brief Green value
+ private: float g = 0;
+
+ /// \brief Blue value
+ private: float b = 0;
+
+ /// \brief Alpha value
+ private: float a = 1;
+ };
+ }
+}
+#endif
diff --git a/include/ignition/math/Filter.hh b/include/ignition/math/Filter.hh
new file mode 100644
index 0000000..dfe9ab3
--- /dev/null
+++ b/include/ignition/math/Filter.hh
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2014 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_FILTER_HH_
+#define IGNITION_MATH_FILTER_HH_
+
+#include <ignition/math/Helpers.hh>
+#include <ignition/math/Vector3.hh>
+#include <ignition/math/Quaternion.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class Filter Filter.hh ignition/math/Filter.hh
+ /// \brief Filter base class
+ template <class T>
+ class Filter
+ {
+ /// \brief Destructor.
+ public: virtual ~Filter() {}
+
+ /// \brief Set the output of the filter.
+ /// \param[in] _val New value.
+ public: virtual void Set(const T &_val)
+ {
+ y0 = _val;
+ }
+
+ /// \brief Set the cutoff frequency and sample rate.
+ /// \param[in] _fc Cutoff frequency.
+ /// \param[in] _fs Sample rate.
+ public: virtual void Fc(double _fc, double _fs) = 0;
+
+ /// \brief Get the output of the filter.
+ /// \return Filter's output.
+ public: virtual const T &Value() const
+ {
+ return y0;
+ }
+
+ /// \brief Output.
+ protected: T y0{};
+ };
+
+ /// \class OnePole Filter.hh ignition/math/Filter.hh
+ /// \brief A one-pole DSP filter.
+ /// \sa http://www.earlevel.com/main/2012/12/15/a-one-pole-filter/
+ template <class T>
+ class OnePole : public Filter<T>
+ {
+ /// \brief Constructor.
+ public: OnePole() = default;
+
+ /// \brief Constructor.
+ /// \param[in] _fc Cutoff frequency.
+ /// \param[in] _fs Sample rate.
+ public: OnePole(double _fc, double _fs)
+ {
+ this->Fc(_fc, _fs);
+ }
+
+ // Documentation Inherited.
+ public: virtual void Fc(double _fc, double _fs)
+ {
+ b1 = exp(-2.0 * IGN_PI * _fc / _fs);
+ a0 = 1.0 - b1;
+ }
+
+ /// \brief Update the filter's output.
+ /// \paran[in] _x Input value.
+ /// \return The filter's current output.
+ public: const T& Process(const T &_x)
+ {
+ this->y0 = a0 * _x + b1 * this->y0;
+ return this->y0;
+ }
+
+ /// \brief Input gain control.
+ protected: double a0 = 0;
+
+ /// \brief Gain of the feedback.
+ protected: double b1 = 0;
+ };
+
+ /// \class OnePoleQuaternion Filter.hh ignition/math/Filter.hh
+ /// \brief One-pole quaternion filter.
+ class OnePoleQuaternion : public OnePole<math::Quaterniond>
+ {
+ /// \brief Constructor.
+ public: OnePoleQuaternion()
+ {
+ this->Set(math::Quaterniond(1, 0, 0, 0));
+ }
+
+ /// \brief Constructor.
+ /// \param[in] _fc Cutoff frequency.
+ /// \param[in] _fs Sample rate.
+ public: OnePoleQuaternion(double _fc, double _fs)
+ : OnePole<math::Quaterniond>(_fc, _fs)
+ {
+ this->Set(math::Quaterniond(1, 0, 0, 0));
+ }
+
+ /// \brief Update the filter's output.
+ /// \paran[in] _x Input value.
+ /// \return The filter's current output.
+ public: const math::Quaterniond& Process(
+ const math::Quaterniond &_x)
+ {
+ y0 = math::Quaterniond::Slerp(a0, y0, _x);
+ return y0;
+ }
+ };
+
+ /// \class OnePoleVector3 Filter.hh ignition/math/Filter.hh
+ /// \brief One-pole vector3 filter.
+ class OnePoleVector3 : public OnePole<math::Vector3d>
+ {
+ /// \brief Constructor.
+ public: OnePoleVector3()
+ {
+ this->Set(math::Vector3d(0, 0, 0));
+ }
+
+ /// \brief Constructor.
+ /// \param[in] _fc Cutoff frequency.
+ /// \param[in] _fs Sample rate.
+ public: OnePoleVector3(double _fc, double _fs)
+ : OnePole<math::Vector3d>(_fc, _fs)
+ {
+ this->Set(math::Vector3d(0, 0, 0));
+ }
+ };
+
+ /// \class BiQuad Filter.hh ignition/math/Filter.hh
+ /// \brief Bi-quad filter base class.
+ /// \sa http://www.earlevel.com/main/2003/03/02/the-bilinear-z-transform/
+ template <class T>
+ class BiQuad : public Filter<T>
+ {
+ /// \brief Constructor.
+ public: BiQuad() = default;
+
+ /// \brief Constructor.
+ /// \param[in] _fc Cutoff frequency.
+ /// \param[in] _fs Sample rate.
+ public: BiQuad(double _fc, double _fs)
+ {
+ this->Fc(_fc, _fs);
+ }
+
+ // Documentation Inherited.
+ public: void Fc(double _fc, double _fs)
+ {
+ this->Fc(_fc, _fs, 0.5);
+ }
+
+ /// \brief Set the cutoff frequency, sample rate and Q coefficient.
+ /// \param[in] _fc Cutoff frequency.
+ /// \param[in] _fs Sample rate.
+ /// \param[in] _q Q coefficient.
+ public: void Fc(double _fc, double _fs, double _q)
+ {
+ double k = tan(IGN_PI * _fc / _fs);
+ double kQuadDenom = k * k + k / _q + 1.0;
+ this->a0 = k * k/ kQuadDenom;
+ this->a1 = 2 * this->a0;
+ this->a2 = this->a0;
+ this->b0 = 1.0;
+ this->b1 = 2 * (k * k - 1.0) / kQuadDenom;
+ this->b2 = (k * k - k / _q + 1.0) / kQuadDenom;
+ }
+
+ /// \brief Set the current filter's output.
+ /// \param[in] _val New filter's output.
+ public: virtual void Set(const T &_val)
+ {
+ this->y0 = this->y1 = this->y2 = this->x1 = this->x2 = _val;
+ }
+
+ /// \brief Update the filter's output.
+ /// \param[in] _x Input value.
+ /// \return The filter's current output.
+ public: virtual const T& Process(const T &_x)
+ {
+ this->y0 = this->a0 * _x +
+ this->a1 * this->x1 +
+ this->a2 * this->x2 -
+ this->b1 * this->y1 -
+ this->b2 * this->y2;
+
+ this->x2 = this->x1;
+ this->x1 = _x;
+ this->y2 = this->y1;
+ this->y1 = this->y0;
+ return this->y0;
+ }
+
+ /// \brief Input gain control coefficients.
+ protected: double a0 = 0,
+ a1 = 0,
+ a2 = 0,
+ b0 = 0,
+ b1 = 0,
+ b2 = 0;
+
+ /// \brief Gain of the feedback coefficients.
+ protected: T x1{}, x2{}, y1{}, y2{};
+ };
+
+ /// \class BiQuadVector3 Filter.hh ignition/math/Filter.hh
+ /// \brief BiQuad vector3 filter
+ class BiQuadVector3 : public BiQuad<math::Vector3d>
+ {
+ /// \brief Constructor.
+ public: BiQuadVector3()
+ {
+ this->Set(math::Vector3d(0, 0, 0));
+ }
+
+ /// \brief Constructor.
+ /// \param[in] _fc Cutoff frequency.
+ /// \param[in] _fs Sample rate.
+ public: BiQuadVector3(double _fc, double _fs)
+ : BiQuad<math::Vector3d>(_fc, _fs)
+ {
+ this->Set(math::Vector3d(0, 0, 0));
+ }
+ };
+ }
+}
+
+#endif
diff --git a/include/ignition/math/Frustum.hh b/include/ignition/math/Frustum.hh
new file mode 100644
index 0000000..04aa88b
--- /dev/null
+++ b/include/ignition/math/Frustum.hh
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_FRUSTUM_HH_
+#define IGNITION_MATH_FRUSTUM_HH_
+
+#include <ignition/math/Plane.hh>
+#include <ignition/math/Angle.hh>
+#include <ignition/math/Pose3.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ // Forward declaration of private data
+ class FrustumPrivate;
+
+ /// \brief Mathematical representation of a frustum and related functions.
+ /// This is also known as a view frustum.
+ class IGNITION_MATH_VISIBLE Frustum
+ {
+ /// \brief Planes that define the boundaries of the frustum.
+ public: enum FrustumPlane
+ {
+ /// \brief Near plane
+ FRUSTUM_PLANE_NEAR = 0,
+
+ /// \brief Far plane
+ FRUSTUM_PLANE_FAR = 1,
+
+ /// \brief Left plane
+ FRUSTUM_PLANE_LEFT = 2,
+
+ /// \brief Right plane
+ FRUSTUM_PLANE_RIGHT = 3,
+
+ /// \brief Top plane
+ FRUSTUM_PLANE_TOP = 4,
+
+ /// \brief Bottom plane
+ FRUSTUM_PLANE_BOTTOM = 5
+ };
+
+ /// \brief Default constructor. With the following default values:
+ ///
+ /// * near: 0.0
+ /// * far: 1.0
+ /// * fov: 0.78539 radians (45 degrees)
+ /// * aspect ratio: 1.0
+ /// * pose: Pose3d::Zero
+ public: Frustum();
+
+ /// \brief Constructor
+ /// \param[in] _near Near plane distance. This is the distance from
+ /// the frustum's vertex to the closest plane
+ /// \param[in] _far Far plane distance. This is the distance from the
+ /// frustum's vertex to the farthest plane.
+ /// \param[in] _fov Field of view. The field of view is the
+ /// angle between the frustum's vertex and the edges of the near or far
+ /// plane. This value represents the horizontal angle.
+ /// \param[in] _aspectRatio The aspect ratio, which is the width divided
+ /// by height of the near or far planes.
+ /// \param[in] _pose Pose of the frustum, which is the vertex (top of
+ /// the pyramid).
+ public: Frustum(const double _near,
+ const double _far,
+ const math::Angle &_fov,
+ const double _aspectRatio,
+ const math::Pose3d &_pose = math::Pose3d::Zero);
+
+ /// \brief Copy Constructor
+ /// \param[in] _p Frustum to copy.
+ public: Frustum(const Frustum &_p);
+
+ /// \brief Destructor
+ public: virtual ~Frustum();
+
+ /// \brief Get the near distance. This is the distance from the
+ /// frustum's vertex to the closest plane.
+ /// \return Near distance.
+ /// \sa SetNear
+ public: double Near() const;
+
+ /// \brief Set the near distance. This is the distance from the
+ /// frustum's vertex to the closest plane.
+ /// \param[in] _near Near distance.
+ /// \sa Near
+ public: void SetNear(const double _near);
+
+ /// \brief Get the far distance. This is the distance from the
+ /// frustum's vertex to the farthest plane.
+ /// \return Far distance.
+ /// \sa SetFar
+ public: double Far() const;
+
+ /// \brief Set the far distance. This is the distance from the
+ /// frustum's vertex to the farthest plane.
+ /// \param[in] _far Far distance.
+ /// \sa Far
+ public: void SetFar(const double _far);
+
+ /// \brief Get the horizontal field of view. The field of view is the
+ /// angle between the frustum's vertex and the edges of the near or far
+ /// plane. This value represents the horizontal angle.
+ /// \return The field of view.
+ /// \sa SetFOV
+ public: math::Angle FOV() const;
+
+ /// \brief Set the horizontal field of view. The field of view is the
+ /// angle between the frustum's vertex and the edges of the near or far
+ /// plane. This value represents the horizontal angle.
+ /// \param[in] _fov The field of view.
+ /// \sa FOV
+ public: void SetFOV(const math::Angle &_fov);
+
+ /// \brief Get the aspect ratio, which is the width divided by height
+ /// of the near or far planes.
+ /// \return The frustum's aspect ratio.
+ /// \sa SetAspectRatio
+ public: double AspectRatio() const;
+
+ /// \brief Set the aspect ratio, which is the width divided by height
+ /// of the near or far planes.
+ /// \param[in] _aspectRatio The frustum's aspect ratio.
+ /// \sa AspectRatio
+ public: void SetAspectRatio(const double _aspectRatio);
+
+ /// \brief Get a plane of the frustum.
+ /// \param[in] _plane The plane to return.
+ /// \return Plane of the frustum.
+ public: Planed Plane(const FrustumPlane _plane) const;
+
+ /// \brief Check if a box lies inside the pyramid frustum.
+ /// \param[in] _b Box to check.
+ /// \return True if the box is inside the pyramid frustum.
+ public: bool Contains(const Box &_b) const;
+
+ /// \brief Check if a point lies inside the pyramid frustum.
+ /// \param[in] _p Point to check.
+ /// \return True if the point is inside the pyramid frustum.
+ public: bool Contains(const Vector3d &_p) const;
+
+ /// \brief Get the pose of the frustum
+ /// \return Pose of the frustum
+ /// \sa SetPose
+ public: Pose3d Pose() const;
+
+ /// \brief Set the pose of the frustum
+ /// \param[in] _pose Pose of the frustum, top vertex.
+ /// \sa Pose
+ public: void SetPose(const Pose3d &_pose);
+
+ /// \brief Assignment operator. Set this frustum to the parameter.
+ /// \param[in] _b Frustum to copy
+ /// \return The new frustum.
+ public: Frustum &operator=(const Frustum &_f);
+
+ /// \brief Compute the planes of the frustum. This is called whenever
+ /// a property of the frustum is changed.
+ private: void ComputePlanes();
+
+ /// \internal
+ /// \brief Private data pointer
+ private: FrustumPrivate *dataPtr;
+ };
+ }
+}
+#endif
diff --git a/include/ignition/math/Helpers.hh b/include/ignition/math/Helpers.hh
new file mode 100644
index 0000000..fb4eecd
--- /dev/null
+++ b/include/ignition/math/Helpers.hh
@@ -0,0 +1,770 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_FUNCTIONS_HH_
+#define IGNITION_MATH_FUNCTIONS_HH_
+
+#include <cmath>
+#include <algorithm>
+#include <limits>
+#include <string>
+#include <iostream>
+#include <vector>
+#include <tuple>
+#include <cstdint>
+
+#include "ignition/math/Export.hh"
+
+/// \brief Double maximum value. This value will be similar to 1.79769e+308
+/// \deprecated Use static const value instead.
+#define IGN_DBL_MAX ignition::math::DPRCT_MAX_D
+
+/// \brief Double min value. This value will be similar to 2.22507e-308
+/// \deprecated Use static const value instead.
+#define IGN_DBL_MIN ignition::math::DPRCT_MIN_D
+
+/// \brief Double low value, equivalent to -IGN_DBL_MAX
+/// \deprecated Use static const value instead.
+#define IGN_DBL_LOW ignition::math::DPRCT_LOW_D
+
+/// \brief Double positive infinite value
+/// \deprecated Use static const value instead.
+#define IGN_DBL_INF ignition::math::DPRCT_INF_D
+
+/// \brief Float maximum value. This value will be similar to 3.40282e+38
+/// \deprecated Use static const value instead.
+#define IGN_FLT_MAX ignition::math::DPRCT_MAX_F
+
+/// \brief Float minimum value. This value will be similar to 1.17549e-38
+/// \deprecated Use static const value instead.
+#define IGN_FLT_MIN ignition::math::DPRCT_MIN_F
+
+/// \brief Float lowest value, equivalent to -IGN_FLT_MAX
+/// \deprecated Use static const value instead.
+#define IGN_FLT_LOW ignition::math::DPRCT_LOW_F
+
+/// \brief Float positive infinite value
+/// \deprecated Use static const value instead.
+#define IGN_FLT_INF ignition::math::DPRCT_INF_F
+
+/// \brief 16bit unsigned integer maximum value
+/// \deprecated Use static const value instead.
+#define IGN_UINT16_MAX ignition::math::DPRCT_MAX_UI16
+
+/// \brief 16bit unsigned integer minimum value
+/// \deprecated Use static const value instead.
+#define IGN_UINT16_MIN ignition::math::DPRCT_MIN_UI16
+
+/// \brief 16bit unsigned integer lowest value. This is equivalent to
+/// IGN_UINT16_MIN, and is defined here for completeness.
+/// \deprecated Use static const value instead.
+#define IGN_UINT16_LOW ignition::math::DPRCT_LOW_UI16
+
+/// \brief 16-bit unsigned integer positive infinite value
+/// \deprecated Use static const value instead.
+#define IGN_UINT16_INF ignition::math::DPRCT_INF_UI16
+
+/// \brief 16bit integer maximum value
+/// \deprecated Use static const value instead.
+#define IGN_INT16_MAX ignition::math::DPRCT_MAX_I16
+
+/// \brief 16bit integer minimum value
+/// \deprecated Use static const value instead.
+#define IGN_INT16_MIN ignition::math::DPRCT_MIN_I16
+
+/// \brief 16bit integer lowest value. This is equivalent to IGN_INT16_MIN,
+/// and is defined here for completeness.
+/// \deprecated Use static const value instead.
+#define IGN_INT16_LOW ignition::math::DPRCT_LOW_I16
+
+/// \brief 16-bit integer positive infinite value
+/// \deprecated Use static const value instead.
+#define IGN_INT16_INF ignition::math::DPRCT_INF_I16
+
+/// \brief 32bit unsigned integer maximum value
+/// \deprecated Use static const value instead.
+#define IGN_UINT32_MAX ignition::math::DPRCT_MAX_UI32
+
+/// \brief 32bit unsigned integer minimum value
+/// \deprecated Use static const value instead.
+#define IGN_UINT32_MIN ignition::math::DPRCT_MIN_UI32
+
+/// \brief 32bit unsigned integer lowest value. This is equivalent to
+/// IGN_UINT32_MIN, and is defined here for completeness.
+/// \deprecated Use static const value instead.
+#define IGN_UINT32_LOW ignition::math::DPRCT_LOW_UI32
+
+/// \brief 32-bit unsigned integer positive infinite value
+/// \deprecated Use static const value instead.
+#define IGN_UINT32_INF ignition::math::DPRCT_INF_UI32
+
+/// \brief 32bit integer maximum value
+/// \deprecated Use static const value instead.
+#define IGN_INT32_MAX ignition::math::DPRCT_MAX_I32
+
+/// \brief 32bit integer minimum value
+/// \deprecated Use static const value instead.
+#define IGN_INT32_MIN ignition::math::DPRCT_MIN_I32
+
+/// \brief 32bit integer minimum value. This is equivalent to IGN_INT32_MIN,
+/// and is defined here for completeness.
+/// \deprecated Use static const value instead.
+#define IGN_INT32_LOW ignition::math::DPRCT_LOW_I32
+
+/// \brief 32-bit integer positive infinite value
+/// \deprecated Use static const value instead.
+#define IGN_INT32_INF ignition::math::DPRCT_INF_I32
+
+/// \brief 64bit unsigned integer maximum value
+/// \deprecated Use static const value instead.
+#define IGN_UINT64_MAX ignition::math::DPRCT_MAX_UI64
+
+/// \brief 64bit unsigned integer minimum value
+/// \deprecated Use static const value instead.
+#define IGN_UINT64_MIN ignition::math::DPRCT_MIN_UI64
+
+/// \brief 64bit unsigned integer lowest value. This is equivalent to
+/// IGN_UINT64_MIN, and is defined here for completeness.
+/// \deprecated Use static const value instead.
+#define IGN_UINT64_LOW ignition::math::DPRCT_LOW_UI64
+
+/// \brief 64-bit unsigned integer positive infinite value
+/// \deprecated Use static const value instead.
+#define IGN_UINT64_INF ignition::math::DPRCT_INF_UI64
+
+/// \brief 64bit integer maximum value
+/// \deprecated Use static const value instead.
+#define IGN_INT64_MAX ignition::math::DPRCT_MAX_I64
+
+/// \brief 64bit integer minimum value
+/// \deprecated Use static const value instead.
+#define IGN_INT64_MIN ignition::math::DPRCT_MIN_I64
+
+/// \brief 64bit integer lowest value. This is equivalent to IGN_INT64_MIN,
+/// and is defined here for completeness.
+/// \deprecated Use static const value instead.
+#define IGN_INT64_LOW ignition::math::DPRCT_LOW_I64
+
+/// \brief 64-bit integer positive infinite value
+/// \deprecated Use static const value instead.
+#define IGN_INT64_INF ignition::math::DPRCT_INF_I64
+
+/// \brief Define IGN_PI, IGN_PI_2, and IGN_PI_4.
+/// This was put here for Windows support.
+#ifdef M_PI
+#define IGN_PI M_PI
+#define IGN_PI_2 M_PI_2
+#define IGN_PI_4 M_PI_4
+#define IGN_SQRT2 M_SQRT2
+#else
+#define IGN_PI 3.14159265358979323846
+#define IGN_PI_2 1.57079632679489661923
+#define IGN_PI_4 0.78539816339744830962
+#define IGN_SQRT2 1.41421356237309504880
+#endif
+
+/// \brief Define IGN_FP_VOLATILE for FP equality comparisons
+/// Use volatile parameters when checking floating point equality on
+/// the 387 math coprocessor to work around bugs from the 387 extra precision
+#if defined __FLT_EVAL_METHOD__ && __FLT_EVAL_METHOD__ == 2
+#define IGN_FP_VOLATILE volatile
+#else
+#define IGN_FP_VOLATILE
+#endif
+
+/// \brief Compute sphere volume
+/// \param[in] _radius Sphere radius
+#define IGN_SPHERE_VOLUME(_radius) (4.0*IGN_PI*std::pow(_radius, 3)/3.0)
+
+/// \brief Compute cylinder volume
+/// \param[in] _r Cylinder base radius
+/// \param[in] _l Cylinder length
+#define IGN_CYLINDER_VOLUME(_r, _l) (_l * IGN_PI * std::pow(_r, 2))
+
+/// \brief Compute box volume
+/// \param[in] _x X length
+/// \param[in] _y Y length
+/// \param[in] _z Z length
+#define IGN_BOX_VOLUME(_x, _y, _z) (_x *_y * _z)
+
+/// \brief Compute box volume from a vector
+/// \param[in] _v Vector3d that contains the box's dimensions.
+#define IGN_BOX_VOLUME_V(_v) (_v.X() *_v.Y() * _v.Z())
+
+namespace ignition
+{
+ /// \brief Math classes and function useful in robot applications.
+ namespace math
+ {
+ /// \brief size_t type with a value of 0
+ static const size_t IGN_ZERO_SIZE_T = 0u;
+
+ /// \brief size_t type with a value of 1
+ static const size_t IGN_ONE_SIZE_T = 1u;
+
+ /// \brief size_t type with a value of 2
+ static const size_t IGN_TWO_SIZE_T = 2u;
+
+ /// \brief size_t type with a value of 3
+ static const size_t IGN_THREE_SIZE_T = 3u;
+
+ /// \brief size_t type with a value of 4
+ static const size_t IGN_FOUR_SIZE_T = 4u;
+
+ /// \brief size_t type with a value of 5
+ static const size_t IGN_FIVE_SIZE_T = 5u;
+
+ /// \brief size_t type with a value of 6
+ static const size_t IGN_SIX_SIZE_T = 6u;
+
+ /// \brief size_t type with a value of 7
+ static const size_t IGN_SEVEN_SIZE_T = 7u;
+
+ /// \brief size_t type with a value of 8
+ static const size_t IGN_EIGHT_SIZE_T = 8u;
+
+ /// \brief size_t type with a value of 9
+ static const size_t IGN_NINE_SIZE_T = 9u;
+
+ /// \brief Double maximum value. This value will be similar to 1.79769e+308
+ static const double MAX_D = std::numeric_limits<double>::max();
+
+ /// \brief Double min value. This value will be similar to 2.22507e-308
+ static const double MIN_D = std::numeric_limits<double>::min();
+
+ /// \brief Double low value, equivalent to -MAX_D
+ static const double LOW_D = std::numeric_limits<double>::lowest();
+
+ /// \brief Double positive infinite value
+ static const double INF_D = std::numeric_limits<double>::infinity();
+
+ /// \brief Returns the representation of a quiet not a number (NAN)
+ static const double NAN_D = std::numeric_limits<double>::quiet_NaN();
+
+ /// \brief Float maximum value. This value will be similar to 3.40282e+38
+ static const float MAX_F = std::numeric_limits<float>::max();
+
+ /// \brief Float minimum value. This value will be similar to 1.17549e-38
+ static const float MIN_F = std::numeric_limits<float>::min();
+
+ /// \brief Float low value, equivalent to -MAX_F
+ static const float LOW_F = std::numeric_limits<float>::lowest();
+
+ /// \brief float positive infinite value
+ static const float INF_F = std::numeric_limits<float>::infinity();
+
+ /// \brief Returns the representation of a quiet not a number (NAN)
+ static const float NAN_F = std::numeric_limits<float>::quiet_NaN();
+
+ /// \brief 16bit unsigned integer maximum value
+ static const uint16_t MAX_UI16 = std::numeric_limits<uint16_t>::max();
+
+ /// \brief 16bit unsigned integer minimum value
+ static const uint16_t MIN_UI16 = std::numeric_limits<uint16_t>::min();
+
+ /// \brief 16bit unsigned integer lowest value. This is equivalent to
+ /// IGN_UINT16_MIN, and is defined here for completeness.
+ static const uint16_t LOW_UI16 = std::numeric_limits<uint16_t>::lowest();
+
+ /// \brief 16-bit unsigned integer positive infinite value
+ static const uint16_t INF_UI16 = std::numeric_limits<uint16_t>::infinity();
+
+ /// \brief 16bit unsigned integer maximum value
+ static const int16_t MAX_I16 = std::numeric_limits<int16_t>::max();
+
+ /// \brief 16bit unsigned integer minimum value
+ static const int16_t MIN_I16 = std::numeric_limits<int16_t>::min();
+
+ /// \brief 16bit unsigned integer lowest value. This is equivalent to
+ /// IGN_INT16_MIN, and is defined here for completeness.
+ static const int16_t LOW_I16 = std::numeric_limits<int16_t>::lowest();
+
+ /// \brief 16-bit unsigned integer positive infinite value
+ static const int16_t INF_I16 = std::numeric_limits<int16_t>::infinity();
+
+ /// \brief 32bit unsigned integer maximum value
+ static const uint32_t MAX_UI32 = std::numeric_limits<uint32_t>::max();
+
+ /// \brief 32bit unsigned integer minimum value
+ static const uint32_t MIN_UI32 = std::numeric_limits<uint32_t>::min();
+
+ /// \brief 32bit unsigned integer lowest value. This is equivalent to
+ /// IGN_UINT32_MIN, and is defined here for completeness.
+ static const uint32_t LOW_UI32 = std::numeric_limits<uint32_t>::lowest();
+
+ /// \brief 32-bit unsigned integer positive infinite value
+ static const uint32_t INF_UI32 = std::numeric_limits<uint32_t>::infinity();
+
+ /// \brief 32bit unsigned integer maximum value
+ static const int32_t MAX_I32 = std::numeric_limits<int32_t>::max();
+
+ /// \brief 32bit unsigned integer minimum value
+ static const int32_t MIN_I32 = std::numeric_limits<int32_t>::min();
+
+ /// \brief 32bit unsigned integer lowest value. This is equivalent to
+ /// IGN_INT32_MIN, and is defined here for completeness.
+ static const int32_t LOW_I32 = std::numeric_limits<int32_t>::lowest();
+
+ /// \brief 32-bit unsigned integer positive infinite value
+ static const int32_t INF_I32 = std::numeric_limits<int32_t>::infinity();
+
+ /// \brief 64bit unsigned integer maximum value
+ static const uint64_t MAX_UI64 = std::numeric_limits<uint64_t>::max();
+
+ /// \brief 64bit unsigned integer minimum value
+ static const uint64_t MIN_UI64 = std::numeric_limits<uint64_t>::min();
+
+ /// \brief 64bit unsigned integer lowest value. This is equivalent to
+ /// IGN_UINT64_MIN, and is defined here for completeness.
+ static const uint64_t LOW_UI64 = std::numeric_limits<uint64_t>::lowest();
+
+ /// \brief 64-bit unsigned integer positive infinite value
+ static const uint64_t INF_UI64 = std::numeric_limits<uint64_t>::infinity();
+
+ /// \brief 64bit unsigned integer maximum value
+ static const int64_t MAX_I64 = std::numeric_limits<int64_t>::max();
+
+ /// \brief 64bit unsigned integer minimum value
+ static const int64_t MIN_I64 = std::numeric_limits<int64_t>::min();
+
+ /// \brief 64bit unsigned integer lowest value. This is equivalent to
+ /// IGN_INT64_MIN, and is defined here for completeness.
+ static const int64_t LOW_I64 = std::numeric_limits<int64_t>::lowest();
+
+ /// \brief 64-bit unsigned integer positive infinite value
+ static const int64_t INF_I64 = std::numeric_limits<int64_t>::infinity();
+
+ /// \brief Returns the representation of a quiet not a number (NAN)
+ static const int NAN_I = std::numeric_limits<int>::quiet_NaN();
+
+ // variables created to deprecate macros in this file
+ static const double IGN_DEPRECATED(2) DPRCT_MAX_D = MAX_D;
+ static const double IGN_DEPRECATED(2) DPRCT_MIN_D = MIN_D;
+ static const double IGN_DEPRECATED(2) DPRCT_LOW_D = LOW_D;
+ static const double IGN_DEPRECATED(2) DPRCT_INF_D = INF_D;
+ static const float IGN_DEPRECATED(2) DPRCT_MAX_F = MAX_F;
+ static const float IGN_DEPRECATED(2) DPRCT_MIN_F = MIN_F;
+ static const float IGN_DEPRECATED(2) DPRCT_LOW_F = LOW_F;
+ static const float IGN_DEPRECATED(2) DPRCT_INF_F = INF_F;
+ static const uint16_t IGN_DEPRECATED(2) DPRCT_MAX_UI16 = MAX_UI16;
+ static const uint16_t IGN_DEPRECATED(2) DPRCT_MIN_UI16 = MIN_UI16;
+ static const uint16_t IGN_DEPRECATED(2) DPRCT_LOW_UI16 = LOW_UI16;
+ static const uint16_t IGN_DEPRECATED(2) DPRCT_INF_UI16 = INF_UI16;
+ static const int16_t IGN_DEPRECATED(2) DPRCT_MAX_I16 = MAX_I16;
+ static const int16_t IGN_DEPRECATED(2) DPRCT_MIN_I16 = MIN_I16;
+ static const int16_t IGN_DEPRECATED(2) DPRCT_LOW_I16 = LOW_I16;
+ static const int16_t IGN_DEPRECATED(2) DPRCT_INF_I16 = INF_I16;
+ static const uint32_t IGN_DEPRECATED(2) DPRCT_MAX_UI32 = MAX_UI32;
+ static const uint32_t IGN_DEPRECATED(2) DPRCT_MIN_UI32 = MIN_UI32;
+ static const uint32_t IGN_DEPRECATED(2) DPRCT_LOW_UI32 = LOW_UI32;
+ static const uint32_t IGN_DEPRECATED(2) DPRCT_INF_UI32 = INF_UI32;
+ static const int32_t IGN_DEPRECATED(2) DPRCT_MAX_I32 = MAX_I32;
+ static const int32_t IGN_DEPRECATED(2) DPRCT_MIN_I32 = MIN_I32;
+ static const int32_t IGN_DEPRECATED(2) DPRCT_LOW_I32 = LOW_I32;
+ static const int32_t IGN_DEPRECATED(2) DPRCT_INF_I32 = INF_I32;
+ static const uint64_t IGN_DEPRECATED(2) DPRCT_MAX_UI64 = MAX_UI64;
+ static const uint64_t IGN_DEPRECATED(2) DPRCT_MIN_UI64 = MIN_UI64;
+ static const uint64_t IGN_DEPRECATED(2) DPRCT_LOW_UI64 = LOW_UI64;
+ static const uint64_t IGN_DEPRECATED(2) DPRCT_INF_UI64 = INF_UI64;
+ static const int64_t IGN_DEPRECATED(2) DPRCT_MAX_I64 = MAX_I64;
+ static const int64_t IGN_DEPRECATED(2) DPRCT_MIN_I64 = MIN_I64;
+ static const int64_t IGN_DEPRECATED(2) DPRCT_LOW_I64 = LOW_I64;
+ static const int64_t IGN_DEPRECATED(2) DPRCT_INF_I64 = INF_I64;
+
+ /// \brief Simple clamping function
+ /// \param[in] _v value
+ /// \param[in] _min minimum
+ /// \param[in] _max maximum
+ template<typename T>
+ inline T clamp(T _v, T _min, T _max)
+ {
+ return std::max(std::min(_v, _max), _min);
+ }
+
+ /// \brief check if a float is NaN
+ /// \param[in] _v the value
+ /// \return true if _v is not a number, false otherwise
+ inline bool isnan(float _v)
+ {
+ return (std::isnan)(_v);
+ }
+
+ /// \brief check if a double is NaN
+ /// \param[in] _v the value
+ /// \return true if _v is not a number, false otherwise
+ inline bool isnan(double _v)
+ {
+ return (std::isnan)(_v);
+ }
+
+ /// \brief Fix a nan value.
+ /// \param[in] _v Value to correct.
+ /// \return 0 if _v is NaN, _v otherwise.
+ inline float fixnan(float _v)
+ {
+ return isnan(_v) || std::isinf(_v) ? 0.0f : _v;
+ }
+
+ /// \brief Fix a nan value.
+ /// \param[in] _v Value to correct.
+ /// \return 0 if _v is NaN, _v otherwise.
+ inline double fixnan(double _v)
+ {
+ return isnan(_v) || std::isinf(_v) ? 0.0 : _v;
+ }
+
+ /// \brief Check if parameter is even.
+ /// \param[in] _v Value to check.
+ /// \return True if _v is even.
+ inline bool isEven(const int _v)
+ {
+ return !(_v % 2);
+ }
+
+ /// \brief Check if parameter is even.
+ /// \param[in] _v Value to check.
+ /// \return True if _v is even.
+ inline bool isEven(const unsigned int _v)
+ {
+ return !(_v % 2);
+ }
+
+ /// \brief Check if parameter is odd.
+ /// \param[in] _v Value to check.
+ /// \return True if _v is odd.
+ inline bool isOdd(const int _v)
+ {
+ return (_v % 2) != 0;
+ }
+
+ /// \brief Check if parameter is odd.
+ /// \param[in] _v Value to check.
+ /// \return True if _v is odd.
+ inline bool isOdd(const unsigned int _v)
+ {
+ return (_v % 2) != 0;
+ }
+
+ /// \brief The signum function.
+ ///
+ /// Returns 0 for zero values, -1 for negative values,
+ /// +1 for positive values.
+ /// \param[in] _value The value.
+ /// \return The signum of the value.
+ template<typename T>
+ inline int sgn(T _value)
+ {
+ return (T(0) < _value) - (_value < T(0));
+ }
+
+ /// \brief The signum function.
+ ///
+ /// Returns 0 for zero values, -1 for negative values,
+ /// +1 for positive values.
+ /// \param[in] _value The value.
+ /// \return The signum of the value.
+ template<typename T>
+ inline int signum(T _value)
+ {
+ return sgn(_value);
+ }
+
+ /// \brief get mean of vector of values
+ /// \param[in] _values the vector of values
+ /// \return the mean
+ template<typename T>
+ inline T mean(const std::vector<T> &_values)
+ {
+ T sum = 0;
+ for (unsigned int i = 0; i < _values.size(); ++i)
+ sum += _values[i];
+ return sum / _values.size();
+ }
+
+ /// \brief get variance of vector of values
+ /// \param[in] _values the vector of values
+ /// \return the squared deviation
+ template<typename T>
+ inline T variance(const std::vector<T> &_values)
+ {
+ T avg = mean<T>(_values);
+
+ T sum = 0;
+ for (unsigned int i = 0; i < _values.size(); ++i)
+ sum += (_values[i] - avg) * (_values[i] - avg);
+ return sum / _values.size();
+ }
+
+ /// \brief get the maximum value of vector of values
+ /// \param[in] _values the vector of values
+ /// \return maximum
+ template<typename T>
+ inline T max(const std::vector<T> &_values)
+ {
+ T max = std::numeric_limits<T>::min();
+ for (unsigned int i = 0; i < _values.size(); ++i)
+ if (_values[i] > max)
+ max = _values[i];
+ return max;
+ }
+
+ /// \brief get the minimum value of vector of values
+ /// \param[in] _values the vector of values
+ /// \return minimum
+ template<typename T>
+ inline T min(const std::vector<T> &_values)
+ {
+ T min = std::numeric_limits<T>::max();
+ for (unsigned int i = 0; i < _values.size(); ++i)
+ if (_values[i] < min)
+ min = _values[i];
+ return min;
+ }
+
+ /// \brief check if two values are equal, within a tolerance
+ /// \param[in] _a the first value
+ /// \param[in] _b the second value
+ /// \param[in] _epsilon the tolerance
+ template<typename T>
+ inline bool equal(const T &_a, const T &_b,
+ const T &_epsilon = T(1e-6))
+ {
+ IGN_FP_VOLATILE T diff = std::abs(_a - _b);
+ return diff <= _epsilon;
+ }
+
+ /// \brief inequality test, within a tolerance
+ /// \param[in] _a the first value
+ /// \param[in] _b the second value
+ /// \param[in] _epsilon the tolerance
+ template<typename T>
+ inline bool lessOrNearEqual(const T &_a, const T &_b,
+ const T &_epsilon = 1e-6)
+ {
+ return _a < _b + _epsilon;
+ }
+
+ /// \brief inequality test, within a tolerance
+ /// \param[in] _a the first value
+ /// \param[in] _b the second value
+ /// \param[in] _epsilon the tolerance
+ template<typename T>
+ inline bool greaterOrNearEqual(const T &_a, const T &_b,
+ const T &_epsilon = 1e-6)
+ {
+ return _a > _b - _epsilon;
+ }
+
+ /// \brief get value at a specified precision
+ /// \param[in] _a the number
+ /// \param[in] _precision the precision
+ /// \return the value for the specified precision
+ template<typename T>
+ inline T precision(const T &_a, const unsigned int &_precision)
+ {
+ auto p = std::pow(10, _precision);
+ return static_cast<T>(std::round(_a * p) / p);
+ }
+
+ /// \brief Sort two numbers, such that _a <= _b
+ /// \param[out] _a the first number
+ /// \param[out] _b the second number
+ template<typename T>
+ inline void sort2(T &_a, T &_b)
+ {
+ using std::swap;
+ if (_b < _a)
+ swap(_a, _b);
+ }
+
+ /// \brief Sort three numbers, such that _a <= _b <= _c
+ /// \param[out] _a the first number
+ /// \param[out] _b the second number
+ /// \param[out] _c the third number
+ template<typename T>
+ inline void sort3(T &_a, T &_b, T &_c)
+ {
+ // _a <= _b
+ sort2(_a, _b);
+ // _a <= _c, _b <= _c
+ sort2(_b, _c);
+ // _a <= _b <= _c
+ sort2(_a, _b);
+ }
+
+ /// \brief Is this a power of 2?
+ /// \param[in] _x the number
+ /// \return true if _x is a power of 2, false otherwise
+ inline bool isPowerOfTwo(unsigned int _x)
+ {
+ return ((_x != 0) && ((_x & (~_x + 1)) == _x));
+ }
+
+ /// \brief Get the smallest power of two that is greater or equal to
+ /// a given value
+ /// \param[in] _x the number
+ /// \return the same value if _x is already a power of two. Otherwise,
+ /// it returns the smallest power of two that is greater than _x
+ inline unsigned int roundUpPowerOfTwo(unsigned int _x)
+ {
+ if (_x == 0)
+ return 1;
+
+ if (isPowerOfTwo(_x))
+ return _x;
+
+ while (_x & (_x - 1))
+ _x = _x & (_x - 1);
+
+ _x = _x << 1;
+
+ return _x;
+ }
+
+ /// \brief parse string into an integer
+ /// \param[in] _input the string
+ /// \return an integer, 0 or 0 and a message in the error stream
+ inline int parseInt(const std::string &_input)
+ {
+ const char *p = _input.c_str();
+ if (!*p || *p == '?')
+ return NAN_I;
+
+ int s = 1;
+ while (*p == ' ')
+ p++;
+
+ if (*p == '-')
+ {
+ s = -1;
+ p++;
+ }
+
+ int acc = 0;
+ while (*p >= '0' && *p <= '9')
+ acc = acc * 10 + *p++ - '0';
+
+ if (*p)
+ {
+ std::cerr << "Invalid int numeric format[" << _input << "]\n";
+ return NAN_I;
+ }
+
+ return s * acc;
+ }
+
+ /// \brief parse string into float
+ /// \param _input the string
+ /// \return a floating point number (can be NaN) or 0 with a message in the
+ /// error stream
+ inline double parseFloat(const std::string &_input)
+ {
+ const char *p = _input.c_str();
+ if (!*p || *p == '?')
+ return NAN_D;
+ int s = 1;
+ while (*p == ' ')
+ p++;
+
+ if (*p == '-')
+ {
+ s = -1;
+ p++;
+ }
+
+ double acc = 0;
+ while (*p >= '0' && *p <= '9')
+ acc = acc * 10 + *p++ - '0';
+
+ if (*p == '.')
+ {
+ double k = 0.1;
+ p++;
+ while (*p >= '0' && *p <= '9')
+ {
+ acc += (*p++ - '0') * k;
+ k *= 0.1;
+ }
+ }
+ if (*p == 'e')
+ {
+ int es = 1;
+ int f = 0;
+ p++;
+ if (*p == '-')
+ {
+ es = -1;
+ p++;
+ }
+ else if (*p == '+')
+ {
+ es = 1;
+ p++;
+ }
+ while (*p >= '0' && *p <= '9')
+ f = f * 10 + *p++ - '0';
+
+ acc *= pow(10, f*es);
+ }
+
+ if (*p)
+ {
+ std::cerr << "Invalid double numeric format[" << _input << "]\n";
+ return NAN_D;
+ }
+ return s * acc;
+ }
+
+
+ // Degrade precision on Windows, which cannot handle 'long double'
+ // values properly. See the implementation of Unpair.
+#ifdef _MSC_VER
+ using PairInput = uint16_t;
+ using PairOutput = uint32_t;
+#else
+ using PairInput = uint32_t;
+ using PairOutput = uint64_t;
+#endif
+
+ /// \brief A pairing function that maps two values to a unique third
+ /// value. This is an implement of Szudzik's function.
+ /// \param[in] _a First value, must be a non-negative integer. On
+ /// Windows this value is uint16_t. On Linux/OSX this value is uint32_t.
+ /// \param[in] _b Second value, must be a non-negative integer. On
+ /// Windows this value is uint16_t. On Linux/OSX this value is uint32_t.
+ /// \return A unique non-negative integer value. On Windows the return
+ /// value is uint32_t. On Linux/OSX the return value is uint64_t
+ /// \sa Unpair
+ PairOutput IGNITION_MATH_VISIBLE Pair(
+ const PairInput _a, const PairInput _b);
+
+ /// \brief The reverse of the Pair function. Accepts a key, produced
+ /// from the Pair function, and returns a tuple consisting of the two
+ /// non-negative integer values used to create the _key.
+ /// \param[in] _key A non-negative integer generated from the Pair
+ /// function. On Windows this value is uint32_t. On Linux/OSX, this
+ /// value is uint64_t.
+ /// \return A tuple that consists of the two non-negative integers that
+ /// will generate _key when used with the Pair function. On Windows the
+ /// tuple contains two uint16_t values. On Linux/OSX the tuple contains
+ /// two uint32_t values.
+ /// \sa Pair
+ std::tuple<PairInput, PairInput> IGNITION_MATH_VISIBLE Unpair(
+ const PairOutput _key);
+ }
+}
+
+#endif
diff --git a/include/ignition/math/Inertial.hh b/include/ignition/math/Inertial.hh
new file mode 100644
index 0000000..d2ee498
--- /dev/null
+++ b/include/ignition/math/Inertial.hh
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2016 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_INERTIAL_HH_
+#define IGNITION_MATH_INERTIAL_HH_
+
+#include "ignition/math/MassMatrix3.hh"
+#include "ignition/math/Pose3.hh"
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class Inertial Inertial.hh ignition/math/Inertial.hh
+ /// \brief A class for inertial information about a rigid body
+ /// consisting of the scalar mass, a 3x3 symmetric moment
+ /// of inertia matrix, and center of mass reference frame pose.
+ template<typename T>
+ class Inertial
+ {
+ /// \brief Default Constructor
+ public: Inertial()
+ {}
+
+ /// \brief Constructor.
+ /// \param[in] _massMatrix Mass and inertia matrix.
+ /// \param[in] _pose Pose of center of mass reference frame.
+ public: Inertial(const MassMatrix3<T> &_massMatrix,
+ const Pose3<T> &_pose)
+ : massMatrix(_massMatrix), pose(_pose)
+ {}
+
+ /// \brief Copy constructor.
+ /// \param[in] _inertial Inertial element to copy
+ public: Inertial(const Inertial<T> &_inertial)
+ : massMatrix(_inertial.MassMatrix()), pose(_inertial.Pose())
+ {}
+
+ /// \brief Destructor.
+ public: virtual ~Inertial() {}
+
+ /// \brief Set the mass and inertia matrix.
+ /// \param[in] _m New MassMatrix3 object.
+ /// \return True if the MassMatrix3 is valid.
+ public: bool SetMassMatrix(const MassMatrix3<T> &_m)
+ {
+ this->massMatrix = _m;
+ return this->massMatrix.IsValid();
+ }
+
+ /// \brief Get the mass and inertia matrix.
+ /// \return The MassMatrix3 object.
+ public: const MassMatrix3<T> &MassMatrix() const
+ {
+ return this->massMatrix;
+ }
+
+ /// \brief Set the pose of center of mass reference frame.
+ /// \param[in] _pose New pose.
+ /// \return True if the MassMatrix3 is valid.
+ public: bool SetPose(const Pose3<T> &_pose)
+ {
+ this->pose = _pose;
+ return this->massMatrix.IsValid();
+ }
+
+ /// \brief Get the pose of center of mass reference frame.
+ /// \return The pose of center of mass reference frame.
+ public: const Pose3<T> &Pose() const
+ {
+ return this->pose;
+ }
+
+ /// \brief Get the moment of inertia matrix expressed in the
+ /// base coordinate frame.
+ /// \return Rotated moment of inertia matrix.
+ public: Matrix3<T> MOI() const
+ {
+ auto R = Matrix3<T>(this->pose.Rot());
+ return R * this->massMatrix.MOI() * R.Transposed();
+ }
+
+ /// \brief Set the inertial pose rotation without affecting the
+ /// MOI in the base coordinate frame.
+ /// \param[in] _q New rotation for inertial pose.
+ /// \return True if the MassMatrix3 is valid.
+ public: bool SetInertialRotation(const Quaternion<T> &_q)
+ {
+ auto moi = this->MOI();
+ this->pose.Rot() = _q;
+ auto R = Matrix3<T>(_q);
+ return this->massMatrix.MOI(R.Transposed() * moi * R);
+ }
+
+ /// \brief Set the MassMatrix rotation (eigenvectors of inertia matrix)
+ /// without affecting the MOI in the base coordinate frame.
+ /// Note that symmetries in inertia matrix may prevent the output of
+ /// MassMatrix3::PrincipalAxesOffset to match this function's input _q,
+ /// but it is guaranteed that the MOI in the base frame will not change.
+ /// A negative value of _tol (such as -1e-6) can be passed to ensure
+ /// that diagonal values are always sorted.
+ /// \param[in] _q New rotation.
+ /// \param[in] _tol Relative tolerance given by absolute value
+ /// of _tol. This is passed to the MassMatrix3
+ /// PrincipalMoments and PrincipalAxesOffset functions.
+ /// \return True if the MassMatrix3 is valid.
+ public: bool SetMassMatrixRotation(const Quaternion<T> &_q,
+ const T _tol = 1e-6)
+ {
+ this->pose.Rot() *= this->MassMatrix().PrincipalAxesOffset(_tol) *
+ _q.Inverse();
+ const auto moments = this->MassMatrix().PrincipalMoments(_tol);
+ const auto diag = Matrix3<T>(
+ moments[0], 0, 0,
+ 0, moments[1], 0,
+ 0, 0, moments[2]);
+ const auto R = Matrix3<T>(_q);
+ return this->massMatrix.MOI(R * diag * R.Transposed());
+ }
+
+ /// \brief Equal operator.
+ /// \param[in] _inertial Inertial to copy.
+ /// \return Reference to this object.
+ public: Inertial &operator=(const Inertial<T> &_inertial)
+ {
+ this->massMatrix = _inertial.MassMatrix();
+ this->pose = _inertial.Pose();
+
+ return *this;
+ }
+
+ /// \brief Equality comparison operator.
+ /// \param[in] _inertial Inertial to copy.
+ /// \return true if each component is equal within a default tolerance,
+ /// false otherwise
+ public: bool operator==(const Inertial<T> &_inertial) const
+ {
+ return (this->pose == _inertial.Pose()) &&
+ (this->massMatrix == _inertial.MassMatrix());
+ }
+
+ /// \brief Inequality test operator
+ /// \param[in] _inertial Inertial<T> to test
+ /// \return True if not equal (using the default tolerance of 1e-6)
+ public: bool operator!=(const Inertial<T> &_inertial) const
+ {
+ return !(*this == _inertial);
+ }
+
+ /// \brief Adds inertial properties to current object.
+ /// The mass, center of mass location, and inertia matrix are updated
+ /// as long as the total mass is positive.
+ /// \param[in] _inertial Inertial to add.
+ /// \return Reference to this object.
+ public: Inertial<T> &operator+=(const Inertial<T> &_inertial)
+ {
+ T m1 = this->massMatrix.Mass();
+ T m2 = _inertial.MassMatrix().Mass();
+
+ // Total mass
+ T mass = m1 + m2;
+
+ // Only continue if total mass is positive
+ if (mass <= 0)
+ {
+ return *this;
+ }
+
+ auto com1 = this->Pose().Pos();
+ auto com2 = _inertial.Pose().Pos();
+ // New center of mass location in base frame
+ auto com = (m1*com1 + m2*com2) / mass;
+
+ // Components of new moment of inertia matrix
+ Vector3<T> ixxyyzz;
+ Vector3<T> ixyxzyz;
+ // First add matrices in base frame
+ {
+ auto moi = this->MOI() + _inertial.MOI();
+ ixxyyzz = Vector3<T>(moi(0, 0), moi(1, 1), moi(2, 2));
+ ixyxzyz = Vector3<T>(moi(0, 1), moi(0, 2), moi(1, 2));
+ }
+ // Then account for parallel axis theorem
+ {
+ auto dc = com1 - com;
+ ixxyyzz.X() += m1 * (std::pow(dc[1], 2) + std::pow(dc[2], 2));
+ ixxyyzz.Y() += m1 * (std::pow(dc[2], 2) + std::pow(dc[0], 2));
+ ixxyyzz.Z() += m1 * (std::pow(dc[0], 2) + std::pow(dc[1], 2));
+ ixxyyzz.X() -= m1 * dc[0] * dc[1];
+ ixxyyzz.Y() -= m1 * dc[0] * dc[2];
+ ixxyyzz.Z() -= m1 * dc[1] * dc[2];
+ }
+ {
+ auto dc = com2 - com;
+ ixxyyzz.X() += m2 * (std::pow(dc[1], 2) + std::pow(dc[2], 2));
+ ixxyyzz.Y() += m2 * (std::pow(dc[2], 2) + std::pow(dc[0], 2));
+ ixxyyzz.Z() += m2 * (std::pow(dc[0], 2) + std::pow(dc[1], 2));
+ ixxyyzz.X() -= m2 * dc[0] * dc[1];
+ ixxyyzz.Y() -= m2 * dc[0] * dc[2];
+ ixxyyzz.Z() -= m2 * dc[1] * dc[2];
+ }
+ this->massMatrix = MassMatrix3<T>(mass, ixxyyzz, ixyxzyz);
+ this->pose = Pose3<T>(com, Quaternion<T>::Identity);
+
+ return *this;
+ }
+
+ /// \brief Adds inertial properties to current object.
+ /// The mass, center of mass location, and inertia matrix are updated
+ /// as long as the total mass is positive.
+ /// \param[in] _inertial Inertial to add.
+ /// \return Sum of inertials as new object.
+ public: const Inertial<T> operator+(const Inertial<T> &_inertial) const
+ {
+ return Inertial<T>(*this) += _inertial;
+ }
+
+ /// \brief Mass and inertia matrix of the object expressed in the
+ /// center of mass reference frame.
+ private: MassMatrix3<T> massMatrix;
+
+ /// \brief Pose offset of center of mass reference frame relative
+ /// to a base frame.
+ private: Pose3<T> pose;
+ };
+
+ typedef Inertial<double> Inertiald;
+ typedef Inertial<float> Inertialf;
+ }
+}
+#endif
diff --git a/include/ignition/math/Kmeans.hh b/include/ignition/math/Kmeans.hh
new file mode 100644
index 0000000..17ff01c
--- /dev/null
+++ b/include/ignition/math/Kmeans.hh
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_KMEANS_HH_
+#define IGNITION_MATH_KMEANS_HH_
+
+#include <vector>
+#include <ignition/math/Vector3.hh>
+#include <ignition/math/Helpers.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ // Forward declare private data
+ class KmeansPrivate;
+
+ /// \class Kmeans Kmeans.hh math/gzmath.hh
+ /// \brief K-Means clustering algorithm. Given a set of observations,
+ /// k-means partitions the observations into k sets so as to minimize the
+ /// within-cluster sum of squares.
+ /// Description based on http://en.wikipedia.org/wiki/K-means_clustering.
+ class IGNITION_MATH_VISIBLE Kmeans
+ {
+ /// \brief constructor
+ /// \param[in] _obs Set of observations to cluster.
+ public: explicit Kmeans(const std::vector<Vector3d> &_obs);
+
+ /// \brief Destructor.
+ public: virtual ~Kmeans();
+
+ /// \brief Get the observations to cluster.
+ /// \return The vector of observations.
+ public: std::vector<Vector3d> Observations() const;
+
+ /// \brief Set the observations to cluster.
+ /// \param[in] _obs The new vector of observations.
+ /// \return True if the vector is not empty or false otherwise.
+ public: bool Observations(const std::vector<Vector3d> &_obs);
+
+ /// \brief Add observations to the cluster.
+ /// \param[in] _obs Vector of observations.
+ /// \return True if the _obs vector is not empty or false otherwise.
+ public: bool AppendObservations(const std::vector<Vector3d> &_obs);
+
+ /// \brief Executes the k-means algorithm.
+ /// \param[in] _k Number of partitions to cluster.
+ /// \param[out] _centroids Vector of centroids. Each element contains the
+ /// centroid of one cluster.
+ /// \param[out] _labels Vector of labels. The size of this vector is
+ /// equals to the number of observations. Each element represents the
+ /// cluster to which observation belongs.
+ /// \return True when the operation succeed or false otherwise. The
+ /// operation will fail if the number of observations is not positive,
+ /// if the number of clusters is non positive, or if the number of
+ /// clusters if greater than the number of observations.
+ public: bool Cluster(int _k,
+ std::vector<Vector3d> &_centroids,
+ std::vector<unsigned int> &_labels);
+
+ /// \brief Given an observation, it returns the closest centroid to it.
+ /// \param[in] _p Point to check.
+ /// \return The index of the closest centroid to the point _p.
+ private: unsigned int ClosestCentroid(const Vector3d &_p) const;
+
+ /// \brief Private data pointer
+ private: KmeansPrivate *dataPtr;
+ };
+ }
+}
+
+#endif
diff --git a/include/ignition/math/Line2.hh b/include/ignition/math/Line2.hh
new file mode 100644
index 0000000..2ef886b
--- /dev/null
+++ b/include/ignition/math/Line2.hh
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2014 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_LINE2_HH_
+#define IGNITION_MATH_LINE2_HH_
+
+#include <algorithm>
+#include <ignition/math/Vector2.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class Line2 Line2.hh ignition/math/Line2.hh
+ /// \brief A two dimensional line segment. The line is defined by a
+ /// start and end point.
+ template<typename T>
+ class Line2
+ {
+ /// \brief Constructor.
+ /// \param[in] _ptA Start point of the line segment
+ /// \param[in] _ptB End point of the line segment
+ public: Line2(const math::Vector2<T> &_ptA, const math::Vector2<T> &_ptB)
+ {
+ this->Set(_ptA, _ptB);
+ }
+
+ /// \brief Constructor.
+ /// \param[in] _x1 X coordinate of the start point.
+ /// \param[in] _y1 Y coordinate of the start point.
+ /// \param[in] _x2 X coordinate of the end point.
+ /// \param[in] _y2 Y coordinate of the end point.
+ public: Line2(double _x1, double _y1, double _x2, double _y2)
+ {
+ this->Set(_x1, _y1, _x2, _y2);
+ }
+
+ /// \brief Set the start and end point of the line segment
+ /// \param[in] _ptA Start point of the line segment
+ /// \param[in] _ptB End point of the line segment
+ public: void Set(const math::Vector2<T> &_ptA,
+ const math::Vector2<T> &_ptB)
+ {
+ this->pts[0] = _ptA;
+ this->pts[1] = _ptB;
+ }
+
+ /// \brief Set the start and end point of the line segment
+ /// \param[in] _x1 X coordinate of the start point.
+ /// \param[in] _y1 Y coordinate of the start point.
+ /// \param[in] _x2 X coordinate of the end point.
+ /// \param[in] _y2 Y coordinate of the end point.
+ public: void Set(double _x1, double _y1, double _x2, double _y2)
+ {
+ this->pts[0].Set(_x1, _y1);
+ this->pts[1].Set(_x2, _y2);
+ }
+
+ /// \brief Return the cross product of this line and the given line.
+ /// Give 'a' as this line and 'b' as given line, the equation is:
+ /// (a.start.x - a.end.x) * (b.start.y - b.end.y) -
+ /// (a.start.y - a.end.y) * (b.start.x - b.end.x)
+ /// \param[in] _line Line for the cross product computation.
+ /// \return Return the cross product of this line and the given line.
+ public: double CrossProduct(const Line2<T> &_line) const
+ {
+ return (this->pts[0].X() - this->pts[1].X()) *
+ (_line[0].Y() -_line[1].Y()) -
+ (this->pts[0].Y() - this->pts[1].Y()) *
+ (_line[0].X() - _line[1].X());
+ }
+
+ /// \brief Return the cross product of this line and the given point.
+ /// Given 'a' and 'b' as the start and end points, the equation is:
+ // (_pt.y - a.y) * (b.x - a.x) - (_pt.x - a.x) * (b.y - a.y)
+ /// \param[in] _pt Point for the cross product computation.
+ /// \return Return the cross product of this line and the given point.
+ public: double CrossProduct(const Vector2<T> &_pt) const
+ {
+ return (_pt.Y() - this->pts[0].Y()) *
+ (this->pts[1].X() - this->pts[0].X()) -
+ (_pt.X() - this->pts[0].X()) *
+ (this->pts[1].Y() - this->pts[0].Y());
+ }
+
+ /// \brief Check if the given point is collinear with this line.
+ /// \param[in] _pt The point to check.
+ /// \param[in] _epsilon The error bounds within which the collinear
+ /// check will return true.
+ /// \return Return true if the point is collinear with this line, false
+ /// otherwise.
+ public: bool Collinear(const math::Vector2<T> &_pt,
+ double _epsilon = 1e-6) const
+ {
+ return math::equal(this->CrossProduct(_pt),
+ static_cast<T>(0), _epsilon);
+ }
+
+ /// \brief Check if the given line is parallel with this line.
+ /// \param[in] _line The line to check.
+ /// \param[in] _epsilon The error bounds within which the parallel
+ /// check will return true.
+ /// \return Return true if the line is parallel with this line, false
+ /// otherwise. Return true if either line is a point (line with zero
+ /// length).
+ public: bool Parallel(const math::Line2<T> &_line,
+ double _epsilon = 1e-6) const
+ {
+ return math::equal(this->CrossProduct(_line),
+ static_cast<T>(0), _epsilon);
+ }
+
+ /// \brief Check if the given line is collinear with this line. This
+ /// is the AND of Parallel and Intersect.
+ /// \param[in] _line The line to check.
+ /// \param[in] _epsilon The error bounds within which the collinear
+ /// check will return true.
+ /// \return Return true if the line is collinear with this line, false
+ /// otherwise.
+ public: bool Collinear(const math::Line2<T> &_line,
+ double _epsilon = 1e-6) const
+ {
+ return this->Parallel(_line, _epsilon) &&
+ this->Intersect(_line, _epsilon);
+ }
+
+ /// \brief Return whether the given point is on this line segment.
+ /// \param[in] _pt Point to check.
+ /// \param[in] _epsilon The error bounds within which the OnSegment
+ /// check will return true.
+ /// \return True if the point is on the segement.
+ public: bool OnSegment(const math::Vector2<T> &_pt,
+ double _epsilon = 1e-6) const
+ {
+ return this->Collinear(_pt, _epsilon) && this->Within(_pt, _epsilon);
+ }
+
+ /// \brief Check if the given point is between the start and end
+ /// points of the line segment. This does not imply that the point is
+ /// on the segment.
+ /// \param[in] _pt Point to check.
+ /// \param[in] _epsilon The error bounds within which the within
+ /// check will return true.
+ /// \return True if the point is on the segement.
+ public: bool Within(const math::Vector2<T> &_pt,
+ double _epsilon = 1e-6) const
+ {
+ return _pt.X() <= std::max(this->pts[0].X(),
+ this->pts[1].X()) + _epsilon &&
+ _pt.X() >= std::min(this->pts[0].X(),
+ this->pts[1].X()) - _epsilon &&
+ _pt.Y() <= std::max(this->pts[0].Y(),
+ this->pts[1].Y()) + _epsilon &&
+ _pt.Y() >= std::min(this->pts[0].Y(),
+ this->pts[1].Y()) - _epsilon;
+ }
+
+ /// \brief Check if this line intersects the given line segment.
+ /// \param[in] _line The line to check for intersection.
+ /// \param[in] _epsilon The error bounds within which the intersection
+ /// check will return true.
+ /// \return True if an intersection was found.
+ public: bool Intersect(const Line2<T> &_line,
+ double _epsilon = 1e-6) const
+ {
+ static math::Vector2<T> ignore;
+ return this->Intersect(_line, ignore, _epsilon);
+ }
+
+ /// \brief Check if this line intersects the given line segment. The
+ /// point of intersection is returned in the _result parameter.
+ /// \param[in] _line The line to check for intersection.
+ /// \param[out] _pt The point of intersection. This value is only
+ /// valid if the return value is true.
+ /// \param[in] _epsilon The error bounds within which the intersection
+ /// check will return true.
+ /// \return True if an intersection was found.
+ public: bool Intersect(const Line2<T> &_line, math::Vector2<T> &_pt,
+ double _epsilon = 1e-6) const
+ {
+ double d = this->CrossProduct(_line);
+
+ // d is zero if the two line are collinear. Must check special
+ // cases.
+ if (math::equal(d, 0.0, _epsilon))
+ {
+ // Check if _line's starting point is on the line.
+ if (this->Within(_line[0], _epsilon))
+ {
+ _pt = _line[0];
+ return true;
+ }
+ // Check if _line's ending point is on the line.
+ else if (this->Within(_line[1], _epsilon))
+ {
+ _pt = _line[1];
+ return true;
+ }
+ // Other wise return false.
+ else
+ return false;
+ }
+
+ _pt.X((_line[0].X() - _line[1].X()) *
+ (this->pts[0].X() * this->pts[1].Y() -
+ this->pts[0].Y() * this->pts[1].X()) -
+ (this->pts[0].X() - this->pts[1].X()) *
+ (_line[0].X() * _line[1].Y() - _line[0].Y() * _line[1].X()));
+
+ _pt.Y((_line[0].Y() - _line[1].Y()) *
+ (this->pts[0].X() * this->pts[1].Y() -
+ this->pts[0].Y() * this->pts[1].X()) -
+ (this->pts[0].Y() - this->pts[1].Y()) *
+ (_line[0].X() * _line[1].Y() - _line[0].Y() * _line[1].X()));
+
+ _pt /= d;
+
+ if (_pt.X() < std::min(this->pts[0].X(), this->pts[1].X()) ||
+ _pt.X() > std::max(this->pts[0].X(), this->pts[1].X()) ||
+ _pt.X() < std::min(_line[0].X(), _line[1].X()) ||
+ _pt.X() > std::max(_line[0].X(), _line[1].X()))
+ {
+ return false;
+ }
+
+ if (_pt.Y() < std::min(this->pts[0].Y(), this->pts[1].Y()) ||
+ _pt.Y() > std::max(this->pts[0].Y(), this->pts[1].Y()) ||
+ _pt.Y() < std::min(_line[0].Y(), _line[1].Y()) ||
+ _pt.Y() > std::max(_line[0].Y(), _line[1].Y()))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /// \brief Get the length of the line
+ /// \return The length of the line.
+ public: T Length() const
+ {
+ return sqrt((this->pts[0].X() - this->pts[1].X()) *
+ (this->pts[0].X() - this->pts[1].X()) +
+ (this->pts[0].Y() - this->pts[1].Y()) *
+ (this->pts[0].Y() - this->pts[1].Y()));
+ }
+
+ /// \brief Get the slope of the line
+ /// \return The slope of the line, NAN_D if the line is vertical.
+ public: double Slope() const
+ {
+ if (math::equal(this->pts[1].X(), this->pts[0].X()))
+ return NAN_D;
+
+ return (this->pts[1].Y() - this->pts[0].Y()) /
+ static_cast<double>(this->pts[1].X() - this->pts[0].X());
+ }
+
+ /// \brief Equality operator.
+ /// \param[in] _line Line to compare for equality.
+ /// \return True if the given line is equal to this line
+ public: bool operator==(const Line2<T> &_line) const
+ {
+ return this->pts[0] == _line[0] && this->pts[1] == _line[1];
+ }
+
+ /// \brief Inequality operator.
+ /// \param[in] _line Line to compare for inequality.
+ /// \return True if the given line is not to this line
+ public: bool operator!=(const Line2<T> &_line) const
+ {
+ return !(*this == _line);
+ }
+
+ /// \brief Get the start or end point.
+ /// \param[in] _index 0 = start point, 1 = end point. The _index
+ /// is clamped to the range [0, 1]
+ public: math::Vector2<T> operator[](size_t _index) const
+ {
+ return this->pts[clamp(_index, IGN_ZERO_SIZE_T, IGN_ONE_SIZE_T)];
+ }
+
+ /// \brief Stream extraction operator
+ /// \param[in] _out output stream
+ /// \param[in] _pt Line2 to output
+ /// \return The stream
+ public: friend std::ostream &operator<<(
+ std::ostream &_out, const Line2<T> &_line)
+ {
+ _out << _line[0] << " " << _line[1];
+ return _out;
+ }
+
+ private: math::Vector2<T> pts[2];
+ };
+
+
+ typedef Line2<int> Line2i;
+ typedef Line2<double> Line2d;
+ typedef Line2<float> Line2f;
+ }
+}
+#endif
diff --git a/include/ignition/math/Line3.hh b/include/ignition/math/Line3.hh
new file mode 100644
index 0000000..14b2bd8
--- /dev/null
+++ b/include/ignition/math/Line3.hh
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_LINE3_HH_
+#define IGNITION_MATH_LINE3_HH_
+
+#include <algorithm>
+#include <ignition/math/Vector3.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class Line3 Line3.hh ignition/math/Line3.hh
+ /// \brief A three dimensional line segment. The line is defined by a
+ /// start and end point.
+ template<typename T>
+ class Line3
+ {
+ /// \brief Line Constructor
+ public: Line3() = default;
+
+ /// \brief Copy constructor
+ /// \param[in] _line a line object
+ public: Line3(const Line3<T> &_line)
+ {
+ this->pts[0] = _line[0];
+ this->pts[1] = _line[1];
+ }
+
+ /// \brief Constructor.
+ /// \param[in] _ptA Start point of the line segment
+ /// \param[in] _ptB End point of the line segment
+ public: Line3(const math::Vector3<T> &_ptA, const math::Vector3<T> &_ptB)
+ {
+ this->Set(_ptA, _ptB);
+ }
+
+ /// \brief 2D Constructor where Z coordinates are 0
+ /// \param[in] _x1 X coordinate of the start point.
+ /// \param[in] _y1 Y coordinate of the start point.
+ /// \param[in] _x2 X coordinate of the end point.
+ /// \param[in] _y2 Y coordinate of the end point.
+ public: Line3(const double _x1, const double _y1,
+ const double _x2, const double _y2)
+ {
+ this->Set(_x1, _y1, _x2, _y2);
+ }
+
+ /// \brief Constructor.
+ /// \param[in] _x1 X coordinate of the start point.
+ /// \param[in] _y1 Y coordinate of the start point.
+ /// \param[in] _z1 Z coordinate of the start point.
+ /// \param[in] _x2 X coordinate of the end point.
+ /// \param[in] _y2 Y coordinate of the end point.
+ /// \param[in] _z2 Z coordinate of the end point.
+ public: Line3(const double _x1, const double _y1,
+ const double _z1, const double _x2,
+ const double _y2, const double _z2)
+ {
+ this->Set(_x1, _y1, _z1, _x2, _y2, _z2);
+ }
+
+ /// \brief Set the start and end point of the line segment
+ /// \param[in] _ptA Start point of the line segment
+ /// \param[in] _ptB End point of the line segment
+ public: void Set(const math::Vector3<T> &_ptA,
+ const math::Vector3<T> &_ptB)
+ {
+ this->pts[0] = _ptA;
+ this->pts[1] = _ptB;
+ }
+
+ /// \brief Set the start point of the line segment
+ /// \param[in] _ptA Start point of the line segment
+ public: void SetA(const math::Vector3<T> &_ptA)
+ {
+ this->pts[0] = _ptA;
+ }
+
+ /// \brief Set the end point of the line segment
+ /// \param[in] _ptB End point of the line segment
+ public: void SetB(const math::Vector3<T> &_ptB)
+ {
+ this->pts[1] = _ptB;
+ }
+
+ /// \brief Set the start and end point of the line segment, assuming that
+ /// both points have the same height.
+ /// \param[in] _x1 X coordinate of the start point.
+ /// \param[in] _y1 Y coordinate of the start point.
+ /// \param[in] _x2 X coordinate of the end point.
+ /// \param[in] _y2 Y coordinate of the end point.
+ /// \param[in] _z Z coordinate of both points,
+ /// by default _z is set to 0.
+ public: void Set(const double _x1, const double _y1,
+ const double _x2, const double _y2,
+ const double _z = 0)
+ {
+ this->pts[0].Set(_x1, _y1, _z);
+ this->pts[1].Set(_x2, _y2, _z);
+ }
+
+ /// \brief Set the start and end point of the line segment
+ /// \param[in] _x1 X coordinate of the start point.
+ /// \param[in] _y1 Y coordinate of the start point.
+ /// \param[in] _z1 Z coordinate of the start point.
+ /// \param[in] _x2 X coordinate of the end point.
+ /// \param[in] _y2 Y coordinate of the end point.
+ /// \param[in] _z2 Z coordinate of the end point.
+ public: void Set(const double _x1, const double _y1,
+ const double _z1, const double _x2,
+ const double _y2, const double _z2)
+ {
+ this->pts[0].Set(_x1, _y1, _z1);
+ this->pts[1].Set(_x2, _y2, _z2);
+ }
+
+ /// \brief Get the direction of the line
+ /// \return The direction vector
+ public: math::Vector3<T> Direction() const
+ {
+ return (this->pts[1] - this->pts[0]).Normalize();
+ }
+
+ /// \brief Get the length of the line
+ /// \return The length of the line.
+ public: T Length() const
+ {
+ return this->pts[0].Distance(this->pts[1]);
+ }
+
+ /// \brief Get the shortest line between this line and the
+ /// provided line.
+ ///
+ /// In the case when the two lines are parallel, we choose the first
+ /// point of this line and the closest point in the provided line.
+ /// \param[in] _line Line to compare against this.
+ /// \param[out] _result The shortest line between _line and this.
+ /// \return True if a solution was found. False if a solution is not
+ /// possible.
+ public: bool Distance(const Line3<T> &_line, Line3<T> &_result,
+ const double _epsilon = 1e-6) const
+ {
+ Vector3<T> p13 = this->pts[0] - _line[0];
+ Vector3<T> p43 = _line[1] - _line[0];
+
+ if (std::abs(p43.X()) < _epsilon && std::abs(p43.Y()) < _epsilon &&
+ std::abs(p43.Z()) < _epsilon)
+ {
+ return false;
+ }
+
+ Vector3<T> p21 = this->pts[1] - this->pts[0];
+
+ if (std::abs(p21.X()) < _epsilon && std::abs(p21.Y()) < _epsilon &&
+ std::abs(p21.Z()) < _epsilon)
+ {
+ return false;
+ }
+
+ double d1343 = p13.Dot(p43);
+ double d4321 = p43.Dot(p21);
+ double d1321 = p13.Dot(p21);
+ double d4343 = p43.Dot(p43);
+ double d2121 = p21.Dot(p21);
+
+ double denom = d2121 * d4343 - d4321 * d4321;
+
+ // In this case, we choose the first point in this line,
+ // and the closest point in the provided line.
+ if (std::abs(denom) < _epsilon)
+ {
+ double d1 = this->pts[0].Distance(_line[0]);
+ double d2 = this->pts[0].Distance(_line[1]);
+
+ double d3 = this->pts[1].Distance(_line[0]);
+ double d4 = this->pts[1].Distance(_line[1]);
+
+ if (d1 <= d2 && d1 <= d3 && d1 <= d4)
+ {
+ _result.SetA(this->pts[0]);
+ _result.SetB(_line[0]);
+ }
+ else if (d2 <= d3 && d2 <= d4)
+ {
+ _result.SetA(this->pts[0]);
+ _result.SetB(_line[1]);
+ }
+ else if (d3 <= d4)
+ {
+ _result.SetA(this->pts[1]);
+ _result.SetB(_line[0]);
+ }
+ else
+ {
+ _result.SetA(this->pts[1]);
+ _result.SetB(_line[1]);
+ }
+
+ return true;
+ }
+
+ double numer = d1343 * d4321 - d1321 * d4343;
+
+ double mua = clamp(numer / denom, 0.0, 1.0);
+ double mub = clamp((d1343 + d4321 * mua) / d4343, 0.0, 1.0);
+
+ _result.Set(this->pts[0] + (p21 * mua), _line[0] + (p43 * mub));
+
+ return true;
+ }
+
+ /// \brief Check if this line intersects the given line segment.
+ /// \param[in] _line The line to check for intersection.
+ /// \param[in] _epsilon The error bounds within which the intersection
+ /// check will return true.
+ /// \return True if an intersection was found.
+ public: bool Intersect(const Line3<T> &_line,
+ double _epsilon = 1e-6) const
+ {
+ static math::Vector3<T> ignore;
+ return this->Intersect(_line, ignore, _epsilon);
+ }
+
+ /// \brief Test if this line and the given line are coplanar.
+ /// \param[in] _line Line to check against.
+ /// \param[in] _epsilon The error bounds within which the
+ /// check will return true.
+ /// \return True if the two lines are coplanar.
+ public: bool Coplanar(const Line3<T> &_line,
+ const double _epsilon = 1e-6) const
+ {
+ return std::abs((_line[0] - this->pts[0]).Dot(
+ (this->pts[1] - this->pts[0]).Cross(_line[1] - _line[0])))
+ <= _epsilon;
+ }
+
+ /// \brief Test if this line and the given line are parallel.
+ /// \param[in] _line Line to check against.
+ /// \param[in] _epsilon The error bounds within which the
+ /// check will return true.
+ /// \return True if the two lines are parallel.
+ public: bool Parallel(const Line3<T> &_line,
+ const double _epsilon = 1e-6) const
+ {
+ return (this->pts[1] - this->pts[0]).Cross(
+ _line[1] - _line[0]).Length() <= _epsilon;
+ }
+
+ /// \brief Check if this line intersects the given line segment. The
+ /// point of intersection is returned in the _pt parameter.
+ /// \param[in] _line The line to check for intersection.
+ /// \param[out] _pt The point of intersection. This value is only
+ /// valid if the return value is true.
+ /// \param[in] _epsilon The error bounds within which the intersection
+ /// check will return true.
+ /// \return True if an intersection was found.
+ public: bool Intersect(const Line3<T> &_line, math::Vector3<T> &_pt,
+ double _epsilon = 1e-6) const
+ {
+ // Handle special case when lines are parallel
+ if (this->Parallel(_line, _epsilon))
+ {
+ // Check if _line's starting point is on the line.
+ if (this->Within(_line[0], _epsilon))
+ {
+ _pt = _line[0];
+ return true;
+ }
+ // Check if _line's ending point is on the line.
+ else if (this->Within(_line[1], _epsilon))
+ {
+ _pt = _line[1];
+ return true;
+ }
+ // Otherwise return false.
+ else
+ return false;
+ }
+
+ // Get the line that is the shortest distance between this and _line
+ math::Line3<T> distLine;
+ this->Distance(_line, distLine, _epsilon);
+
+ // If the length of the line is less than epsilon, then they
+ // intersect.
+ if (distLine.Length() < _epsilon)
+ {
+ _pt = distLine[0];
+ return true;
+ }
+
+ return false;
+ }
+
+ /// \brief Check if the given point is between the start and end
+ /// points of the line segment.
+ /// \param[in] _pt Point to check.
+ /// \param[in] _epsilon The error bounds within which the within
+ /// check will return true.
+ /// \return True if the point is on the segement.
+ public: bool Within(const math::Vector3<T> &_pt,
+ double _epsilon = 1e-6) const
+ {
+ return _pt.X() <= std::max(this->pts[0].X(),
+ this->pts[1].X()) + _epsilon &&
+ _pt.X() >= std::min(this->pts[0].X(),
+ this->pts[1].X()) - _epsilon &&
+ _pt.Y() <= std::max(this->pts[0].Y(),
+ this->pts[1].Y()) + _epsilon &&
+ _pt.Y() >= std::min(this->pts[0].Y(),
+ this->pts[1].Y()) - _epsilon &&
+ _pt.Z() <= std::max(this->pts[0].Z(),
+ this->pts[1].Z()) + _epsilon &&
+ _pt.Z() >= std::min(this->pts[0].Z(),
+ this->pts[1].Z()) - _epsilon;
+ }
+
+ /// \brief Equality operator.
+ /// \param[in] _line Line to compare for equality.
+ /// \return True if the given line is equal to this line
+ public: bool operator==(const Line3<T> &_line) const
+ {
+ return this->pts[0] == _line[0] && this->pts[1] == _line[1];
+ }
+
+ /// \brief Inequality operator.
+ /// \param[in] _line Line to compare for inequality.
+ /// \return True if the given line is not to this line
+ public: bool operator!=(const Line3<T> &_line) const
+ {
+ return !(*this == _line);
+ }
+
+ /// \brief Get the start or end point.
+ /// \param[in] _index 0 = start point, 1 = end point. The _index
+ /// parameter is clamped to the range [0, 1].
+ public: math::Vector3<T> operator[](const size_t _index) const
+ {
+ return this->pts[clamp(_index, IGN_ZERO_SIZE_T, IGN_ONE_SIZE_T)];
+ }
+
+ /// \brief Stream extraction operator
+ /// \param[in] _out output stream
+ /// \param[in] _line Line3 to output
+ /// \return The stream
+ public: friend std::ostream &operator<<(
+ std::ostream &_out, const Line3<T> &_line)
+ {
+ _out << _line[0] << " " << _line[1];
+ return _out;
+ }
+
+ /// \brief Assignment operator
+ /// \param[in] _line a new value
+ /// \return this
+ public: Line3 &operator=(const Line3<T> &_line)
+ {
+ this->pts[0] = _line[0];
+ this->pts[1] = _line[1];
+
+ return *this;
+ }
+
+ /// \brief Vector for storing the start and end points of the line
+ private: math::Vector3<T> pts[2];
+ };
+
+ typedef Line3<int> Line3i;
+ typedef Line3<double> Line3d;
+ typedef Line3<float> Line3f;
+ }
+}
+#endif
diff --git a/include/ignition/math/MassMatrix3.hh b/include/ignition/math/MassMatrix3.hh
new file mode 100644
index 0000000..7b7b5f0
--- /dev/null
+++ b/include/ignition/math/MassMatrix3.hh
@@ -0,0 +1,906 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_MASSMATRIX3_HH_
+#define IGNITION_MATH_MASSMATRIX3_HH_
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "ignition/math/Helpers.hh"
+#include "ignition/math/Quaternion.hh"
+#include "ignition/math/Vector2.hh"
+#include "ignition/math/Vector3.hh"
+#include "ignition/math/Matrix3.hh"
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class MassMatrix3 MassMatrix3.hh ignition/math/MassMatrix3.hh
+ /// \brief A class for inertial information about a rigid body
+ /// consisting of the scalar mass and a 3x3 symmetric moment
+ /// of inertia matrix stored as two Vector3's.
+ template<typename T>
+ class MassMatrix3
+ {
+ /// \brief Default Constructor
+ public: MassMatrix3() : mass(0)
+ {}
+
+ /// \brief Constructor.
+ /// \param[in] _mass Mass value in kg if using metric.
+ /// \param[in] _ixxyyzz Diagonal moments of inertia.
+ /// \param[in] _ixyxzyz Off-diagonal moments of inertia
+ public: MassMatrix3(const T &_mass,
+ const Vector3<T> &_ixxyyzz,
+ const Vector3<T> &_ixyxzyz)
+ : mass(_mass), Ixxyyzz(_ixxyyzz), Ixyxzyz(_ixyxzyz)
+ {}
+
+ /// \brief Copy constructor.
+ /// \param[in] _massMatrix MassMatrix3 element to copy
+ public: MassMatrix3(const MassMatrix3<T> &_m)
+ : mass(_m.Mass()), Ixxyyzz(_m.DiagonalMoments()),
+ Ixyxzyz(_m.OffDiagonalMoments())
+ {}
+
+ /// \brief Destructor.
+ public: virtual ~MassMatrix3() {}
+
+ /// \brief Set the mass.
+ /// \param[in] _m New mass value.
+ /// \return True if the MassMatrix3 is valid.
+ public: bool Mass(const T &_m)
+ {
+ this->mass = _m;
+ return this->IsValid();
+ }
+
+ /// \brief Get the mass
+ /// \return The mass value
+ public: T Mass() const
+ {
+ return this->mass;
+ }
+
+ /// \brief Set the moment of inertia matrix.
+ /// \param[in] _ixx X second moment of inertia (MOI) about x axis.
+ /// \param[in] _iyy Y second moment of inertia about y axis.
+ /// \param[in] _izz Z second moment of inertia about z axis.
+ /// \param[in] _ixy XY inertia.
+ /// \param[in] _ixz XZ inertia.
+ /// \param[in] _iyz YZ inertia.
+ /// \return True if the MassMatrix3 is valid.
+ public: bool InertiaMatrix(const T &_ixx, const T &_iyy, const T &_izz,
+ const T &_ixy, const T &_ixz, const T &_iyz)
+ {
+ this->Ixxyyzz.Set(_ixx, _iyy, _izz);
+ this->Ixyxzyz.Set(_ixy, _ixz, _iyz);
+ return this->IsValid();
+ }
+
+ /// \brief Get the diagonal moments of inertia (Ixx, Iyy, Izz).
+ /// \return The diagonal moments.
+ public: Vector3<T> DiagonalMoments() const
+ {
+ return this->Ixxyyzz;
+ }
+
+ /// \brief Get the off-diagonal moments of inertia (Ixy, Ixz, Iyz).
+ /// \return The off-diagonal moments of inertia.
+ public: Vector3<T> OffDiagonalMoments() const
+ {
+ return this->Ixyxzyz;
+ }
+
+ /// \brief Set the diagonal moments of inertia (Ixx, Iyy, Izz).
+ /// \param[in] _ixxyyzz diagonal moments of inertia
+ /// \return True if the MassMatrix3 is valid.
+ public: bool DiagonalMoments(const Vector3<T> &_ixxyyzz)
+ {
+ this->Ixxyyzz = _ixxyyzz;
+ return this->IsValid();
+ }
+
+ /// \brief Set the off-diagonal moments of inertia (Ixy, Ixz, Iyz).
+ /// \param[in] _ixyxzyz off-diagonal moments of inertia
+ /// \return True if the MassMatrix3 is valid.
+ public: bool OffDiagonalMoments(const Vector3<T> &_ixyxzyz)
+ {
+ this->Ixyxzyz = _ixyxzyz;
+ return this->IsValid();
+ }
+
+ /// \brief Get IXX
+ /// \return IXX value
+ public: T IXX() const
+ {
+ return this->Ixxyyzz[0];
+ }
+
+ /// \brief Get IYY
+ /// \return IYY value
+ public: T IYY() const
+ {
+ return this->Ixxyyzz[1];
+ }
+
+ /// \brief Get IZZ
+ /// \return IZZ value
+ public: T IZZ() const
+ {
+ return this->Ixxyyzz[2];
+ }
+
+ /// \brief Get IXY
+ /// \return IXY value
+ public: T IXY() const
+ {
+ return this->Ixyxzyz[0];
+ }
+
+ /// \brief Get IXZ
+ /// \return IXZ value
+ public: T IXZ() const
+ {
+ return this->Ixyxzyz[1];
+ }
+
+ /// \brief Get IYZ
+ /// \return IYZ value
+ public: T IYZ() const
+ {
+ return this->Ixyxzyz[2];
+ }
+
+ /// \brief Set IXX
+ /// \param[in] _v IXX value
+ /// \return True if the MassMatrix3 is valid.
+ public: bool IXX(const T &_v)
+ {
+ this->Ixxyyzz.X(_v);
+ return this->IsValid();
+ }
+
+ /// \brief Set IYY
+ /// \param[in] _v IYY value
+ /// \return True if the MassMatrix3 is valid.
+ public: bool IYY(const T &_v)
+ {
+ this->Ixxyyzz.Y(_v);
+ return this->IsValid();
+ }
+
+ /// \brief Set IZZ
+ /// \param[in] _v IZZ value
+ /// \return True if the MassMatrix3 is valid.
+ public: bool IZZ(const T &_v)
+ {
+ this->Ixxyyzz.Z(_v);
+ return this->IsValid();
+ }
+
+ /// \brief Set IXY
+ /// \param[in] _v IXY value
+ /// \return True if the MassMatrix3 is valid.
+ public: bool IXY(const T &_v)
+ {
+ this->Ixyxzyz.X(_v);
+ return this->IsValid();
+ }
+
+ /// \brief Set IXZ
+ /// \param[in] _v IXZ value
+ /// \return True if the MassMatrix3 is valid.
+ public: bool IXZ(const T &_v)
+ {
+ this->Ixyxzyz.Y(_v);
+ return this->IsValid();
+ }
+
+ /// \brief Set IYZ
+ /// \param[in] _v IYZ value
+ /// \return True if the MassMatrix3 is valid.
+ public: bool IYZ(const T &_v)
+ {
+ this->Ixyxzyz.Z(_v);
+ return this->IsValid();
+ }
+
+ /// \brief returns Moments of Inertia as a Matrix3
+ /// \return Moments of Inertia as a Matrix3
+ public: Matrix3<T> MOI() const
+ {
+ return Matrix3<T>(
+ this->Ixxyyzz[0], this->Ixyxzyz[0], this->Ixyxzyz[1],
+ this->Ixyxzyz[0], this->Ixxyyzz[1], this->Ixyxzyz[2],
+ this->Ixyxzyz[1], this->Ixyxzyz[2], this->Ixxyyzz[2]);
+ }
+
+ /// \brief Sets Moments of Inertia (MOI) from a Matrix3.
+ /// Symmetric component of input matrix is used by averaging
+ /// off-axis terms.
+ /// \param[in] Moments of Inertia as a Matrix3
+ /// \return True if the MassMatrix3 is valid.
+ public: bool MOI(const Matrix3<T> &_moi)
+ {
+ this->Ixxyyzz.Set(_moi(0, 0), _moi(1, 1), _moi(2, 2));
+ this->Ixyxzyz.Set(
+ 0.5*(_moi(0, 1) + _moi(1, 0)),
+ 0.5*(_moi(0, 2) + _moi(2, 0)),
+ 0.5*(_moi(1, 2) + _moi(2, 1)));
+ return this->IsValid();
+ }
+
+ /// \brief Equal operator.
+ /// \param[in] _massMatrix MassMatrix3 to copy.
+ /// \return Reference to this object.
+ public: MassMatrix3 &operator=(const MassMatrix3<T> &_massMatrix)
+ {
+ this->mass = _massMatrix.Mass();
+ this->Ixxyyzz = _massMatrix.DiagonalMoments();
+ this->Ixyxzyz = _massMatrix.OffDiagonalMoments();
+
+ return *this;
+ }
+
+ /// \brief Equality comparison operator.
+ /// \param[in] _m MassMatrix3 to copy.
+ /// \return true if each component is equal within a default tolerance,
+ /// false otherwise
+ public: bool operator==(const MassMatrix3<T> &_m) const
+ {
+ return equal<T>(this->mass, _m.Mass()) &&
+ (this->Ixxyyzz == _m.DiagonalMoments()) &&
+ (this->Ixyxzyz == _m.OffDiagonalMoments());
+ }
+
+ /// \brief Inequality test operator
+ /// \param[in] _m MassMatrix3<T> to test
+ /// \return True if not equal (using the default tolerance of 1e-6)
+ public: bool operator!=(const MassMatrix3<T> &_m) const
+ {
+ return !(*this == _m);
+ }
+
+ /// \brief Verify that inertia values are positive definite
+ /// \return True if mass is positive and moment of inertia matrix
+ /// is positive definite.
+ public: bool IsPositive() const
+ {
+ // Check if mass and determinants of all upper left submatrices
+ // of moment of inertia matrix are positive
+ return (this->mass > 0) &&
+ (this->IXX() > 0) &&
+ (this->IXX()*this->IYY() - std::pow(this->IXY(), 2) > 0) &&
+ (this->MOI().Determinant() > 0);
+ }
+
+ /// \brief Verify that inertia values are positive definite
+ /// and satisfy the triangle inequality.
+ /// \return True if IsPositive and moment of inertia satisfies
+ /// the triangle inequality.
+ public: bool IsValid() const
+ {
+ return this->IsPositive() && ValidMoments(this->PrincipalMoments());
+ }
+
+ /// \brief Verify that principal moments are positive
+ /// and satisfy the triangle inequality.
+ /// \param[in] _moments Principal moments of inertia.
+ /// \return True if moments of inertia are positive
+ /// and satisfy the triangle inequality.
+ public: static bool ValidMoments(const Vector3<T> &_moments)
+ {
+ return _moments[0] > 0 &&
+ _moments[1] > 0 &&
+ _moments[2] > 0 &&
+ _moments[0] + _moments[1] > _moments[2] &&
+ _moments[1] + _moments[2] > _moments[0] &&
+ _moments[2] + _moments[0] > _moments[1];
+ }
+
+ /// \brief Compute principal moments of inertia,
+ /// which are the eigenvalues of the moment of inertia matrix.
+ /// \param[in] _tol Relative tolerance given by absolute value
+ /// of _tol.
+ /// Negative values of _tol are interpreted as a flag that
+ /// causes principal moments to always be sorted from smallest
+ /// to largest.
+ /// \return Principal moments of inertia.
+ /// If the matrix is already diagonal and _tol is positive,
+ /// they are returned in the existing order.
+ /// Otherwise, the moments are sorted from smallest to largest.
+ public: Vector3<T> PrincipalMoments(const T _tol = 1e-6) const
+ {
+ // Compute tolerance relative to maximum value of inertia diagonal
+ T tol = _tol * this->Ixxyyzz.Max();
+ if (this->Ixyxzyz.Equal(Vector3<T>::Zero, tol))
+ {
+ // Matrix is already diagonalized, return diagonal moments
+ return this->Ixxyyzz;
+ }
+
+ // Algorithm based on http://arxiv.org/abs/1306.6291v4
+ // A Method for Fast Diagonalization of a 2x2 or 3x3 Real Symmetric
+ // Matrix, by Maarten Kronenburg
+ Vector3<T> Id(this->Ixxyyzz);
+ Vector3<T> Ip(this->Ixyxzyz);
+ // b = Ixx + Iyy + Izz
+ T b = Id.Sum();
+ // c = Ixx*Iyy - Ixy^2 + Ixx*Izz - Ixz^2 + Iyy*Izz - Iyz^2
+ T c = Id[0]*Id[1] - std::pow(Ip[0], 2)
+ + Id[0]*Id[2] - std::pow(Ip[1], 2)
+ + Id[1]*Id[2] - std::pow(Ip[2], 2);
+ // d = Ixx*Iyz^2 + Iyy*Ixz^2 + Izz*Ixy^2 - Ixx*Iyy*Izz - 2*Ixy*Ixz*Iyz
+ T d = Id[0]*std::pow(Ip[2], 2)
+ + Id[1]*std::pow(Ip[1], 2)
+ + Id[2]*std::pow(Ip[0], 2)
+ - Id[0]*Id[1]*Id[2]
+ - 2*Ip[0]*Ip[1]*Ip[2];
+ // p = b^2 - 3c
+ T p = std::pow(b, 2) - 3*c;
+
+ // At this point, it is important to check that p is not close
+ // to zero, since its inverse is used to compute delta.
+ // In equation 4.7, p is expressed as a sum of squares
+ // that is only zero if the matrix is diagonal
+ // with identical principal moments.
+ // This check has no test coverage, since this function returns
+ // immediately if a diagonal matrix is detected.
+ if (p < std::pow(tol, 2))
+ return b / 3.0 * Vector3<T>::One;
+
+ // q = 2b^3 - 9bc - 27d
+ T q = 2*std::pow(b, 3) - 9*b*c - 27*d;
+
+ // delta = acos(q / (2 * p^(1.5)))
+ // additionally clamp the argument to [-1,1]
+ T delta = acos(clamp<T>(0.5 * q / std::pow(p, 1.5), -1, 1));
+
+ // sort the moments from smallest to largest
+ T moment0 = (b + 2*sqrt(p) * cos(delta / 3.0)) / 3.0;
+ T moment1 = (b + 2*sqrt(p) * cos((delta + 2*IGN_PI)/3.0)) / 3.0;
+ T moment2 = (b + 2*sqrt(p) * cos((delta - 2*IGN_PI)/3.0)) / 3.0;
+ sort3(moment0, moment1, moment2);
+ return Vector3<T>(moment0, moment1, moment2);
+ }
+
+ /// \brief Compute rotational offset of principal axes.
+ /// \param[in] _tol Relative tolerance given by absolute value
+ /// of _tol.
+ /// Negative values of _tol are interpreted as a flag that
+ /// causes principal moments to always be sorted from smallest
+ /// to largest.
+ /// \return Quaternion representing rotational offset of principal axes.
+ /// With a rotation matrix constructed from this quaternion R(q)
+ /// and a diagonal matrix L with principal moments on the diagonal,
+ /// the original moment of inertia matrix MOI can be reconstructed
+ /// with MOI = R(q).Transpose() * L * R(q)
+ public: Quaternion<T> PrincipalAxesOffset(const T _tol = 1e-6) const
+ {
+ // Compute tolerance relative to maximum value of inertia diagonal
+ T tol = _tol * this->Ixxyyzz.Max();
+ Vector3<T> moments = this->PrincipalMoments(tol);
+ if (moments.Equal(this->Ixxyyzz, tol) ||
+ (math::equal<T>(moments[0], moments[1], std::abs(tol)) &&
+ math::equal<T>(moments[0], moments[2], std::abs(tol))))
+ {
+ // matrix is already aligned with principal axes
+ // or all three moments are approximately equal
+ // return identity rotation
+ return Quaternion<T>::Identity;
+ }
+
+ // Algorithm based on http://arxiv.org/abs/1306.6291v4
+ // A Method for Fast Diagonalization of a 2x2 or 3x3 Real Symmetric
+ // Matrix, by Maarten Kronenburg
+ // A real, symmetric matrix can be diagonalized by an orthogonal matrix
+ // (due to the finite-dimensional spectral theorem
+ // https://en.wikipedia.org/wiki/Spectral_theorem
+ // #Hermitian_maps_and_Hermitian_matrices ),
+ // and another name for orthogonal matrix is rotation matrix.
+ // Section 5 of the paper shows how to compute Euler angles
+ // phi1, phi2, and phi3 that map to a rotation matrix.
+ // In some cases, there are multiple possible values for a given angle,
+ // such as phi1, that are denoted as phi11, phi12, phi11a, phi12a, etc.
+ // Similar variable names are used to the paper so that the paper
+ // can be used as an additional reference.
+
+ // f1, f2 defined in equations 5.5, 5.6
+ Vector2<T> f1(this->Ixyxzyz[0], -this->Ixyxzyz[1]);
+ Vector2<T> f2(this->Ixxyyzz[1] - this->Ixxyyzz[2],
+ -2*this->Ixyxzyz[2]);
+
+ // Check if two moments are equal, since different equations are used
+ // The moments vector is already sorted, so just check adjacent values.
+ Vector2<T> momentsDiff(moments[0] - moments[1],
+ moments[1] - moments[2]);
+
+ // index of unequal moment
+ int unequalMoment = -1;
+ if (equal<T>(momentsDiff[0], 0, std::abs(tol)))
+ unequalMoment = 2;
+ else if (equal<T>(momentsDiff[1], 0, std::abs(tol)))
+ unequalMoment = 0;
+
+ if (unequalMoment >= 0)
+ {
+ // moments[1] is the repeated value
+ // it is not equal to moments[unequalMoment]
+ // momentsDiff3 = lambda - lambda3
+ T momentsDiff3 = moments[1] - moments[unequalMoment];
+ // eq 5.21:
+ // s = cos(phi2)^2 = (A_11 - lambda3) / (lambda - lambda3)
+ // s >= 0 since A_11 is in range [lambda, lambda3]
+ T s = (this->Ixxyyzz[0] - moments[unequalMoment]) / momentsDiff3;
+ // set phi3 to zero for repeated moments (eq 5.23)
+ T phi3 = 0;
+ // phi2 = +- acos(sqrt(s))
+ // start with just the positive value
+ // also clamp the acos argument to prevent NaN's
+ T phi2 = acos(clamp<T>(ClampedSqrt(s), -1, 1));
+
+ // The paper defines variables phi11 and phi12
+ // which are candidate values of angle phi1.
+ // phi12 is straightforward to compute as a function of f2 and g2.
+ // eq 5.25:
+ Vector2<T> g2(momentsDiff3 * s, 0);
+ // combining eq 5.12 and 5.14, and subtracting psi2
+ // instead of multiplying by its rotation matrix:
+ math::Angle phi12(0.5*(Angle2(g2, tol) - Angle2(f2, tol)));
+ phi12.Normalize();
+
+ // The paragraph prior to equation 5.16 describes how to choose
+ // the candidate value of phi1 based on the length
+ // of the f1 and f2 vectors.
+ // * When |f1| != 0 and |f2| != 0, then one should choose the
+ // value of phi2 so that phi11 = phi12
+ // * When |f1| == 0 and f2 != 0, then phi1 = phi12
+ // phi11 can be ignored, and either sign of phi2 can be used
+ // * The case of |f2| == 0 can be ignored at this point in the code
+ // since having a repeated moment when |f2| == 0 implies that
+ // the matrix is diagonal. But this function returns a unit
+ // quaternion for diagonal matrices, so we can assume |f2| != 0
+ // See MassMatrix3.ipynb for a more complete discussion.
+ //
+ // Since |f2| != 0, we only need to consider |f1|
+ // * |f1| == 0: phi1 = phi12
+ // * |f1| != 0: choose phi2 so that phi11 == phi12
+ // In either case, phi1 = phi12,
+ // and the sign of phi2 must be chosen to make phi11 == phi12
+ T phi1 = phi12.Radian();
+
+ bool f1small = f1.SquaredLength() < std::pow(tol, 2);
+ if (!f1small)
+ {
+ // a: phi2 > 0
+ // eq. 5.24
+ Vector2<T> g1a(0, 0.5*momentsDiff3 * sin(2*phi2));
+ // combining eq 5.11 and 5.13, and subtracting psi1
+ // instead of multiplying by its rotation matrix:
+ math::Angle phi11a(Angle2(g1a, tol) - Angle2(f1, tol));
+ phi11a.Normalize();
+
+ // b: phi2 < 0
+ // eq. 5.24
+ Vector2<T> g1b(0, 0.5*momentsDiff3 * sin(-2*phi2));
+ // combining eq 5.11 and 5.13, and subtracting psi1
+ // instead of multiplying by its rotation matrix:
+ math::Angle phi11b(Angle2(g1b, tol) - Angle2(f1, tol));
+ phi11b.Normalize();
+
+ // choose sign of phi2
+ // based on whether phi11a or phi11b is closer to phi12
+ // use sin and cos to account for angle wrapping
+ T erra = std::pow(sin(phi1) - sin(phi11a.Radian()), 2)
+ + std::pow(cos(phi1) - cos(phi11a.Radian()), 2);
+ T errb = std::pow(sin(phi1) - sin(phi11b.Radian()), 2)
+ + std::pow(cos(phi1) - cos(phi11b.Radian()), 2);
+ if (errb < erra)
+ {
+ phi2 *= -1;
+ }
+ }
+
+ // I determined these arguments using trial and error
+ Quaternion<T> result = Quaternion<T>(-phi1, -phi2, -phi3).Inverse();
+
+ // Previous equations assume repeated moments are at the beginning
+ // of the moments vector (moments[0] == moments[1]).
+ // We have the vectors sorted by size, so it's possible that the
+ // repeated moments are at the end (moments[1] == moments[2]).
+ // In this case (unequalMoment == 0), we apply an extra
+ // rotation that exchanges moment[0] and moment[2]
+ // Rotation matrix = [ 0 0 1]
+ // [ 0 1 0]
+ // [-1 0 0]
+ // That is equivalent to a 90 degree pitch
+ if (unequalMoment == 0)
+ result *= Quaternion<T>(0, IGN_PI_2, 0);
+
+ return result;
+ }
+
+ // No repeated principal moments
+ // eq 5.1:
+ T v = (std::pow(this->Ixyxzyz[0], 2) + std::pow(this->Ixyxzyz[1], 2)
+ +(this->Ixxyyzz[0] - moments[2])
+ *(this->Ixxyyzz[0] + moments[2] - moments[0] - moments[1]))
+ / ((moments[1] - moments[2]) * (moments[2] - moments[0]));
+ // value of w depends on v
+ T w;
+ if (v < std::abs(tol))
+ {
+ // first sentence after eq 5.4:
+ // "In the case that v = 0, then w = 1."
+ w = 1;
+ }
+ else
+ {
+ // eq 5.2:
+ w = (this->Ixxyyzz[0] - moments[2] + (moments[2] - moments[1])*v)
+ / ((moments[0] - moments[1]) * v);
+ }
+ // initialize values of angle phi1, phi2, phi3
+ T phi1 = 0;
+ // eq 5.3: start with positive value
+ T phi2 = acos(clamp<T>(ClampedSqrt(v), -1, 1));
+ // eq 5.4: start with positive value
+ T phi3 = acos(clamp<T>(ClampedSqrt(w), -1, 1));
+
+ // compute g1, g2 for phi2,phi3 >= 0
+ // equations 5.7, 5.8
+ Vector2<T> g1(
+ 0.5* (moments[0]-moments[1])*ClampedSqrt(v)*sin(2*phi3),
+ 0.5*((moments[0]-moments[1])*w + moments[1]-moments[2])*sin(2*phi2));
+ Vector2<T> g2(
+ (moments[0]-moments[1])*(1 + (v-2)*w) + (moments[1]-moments[2])*v,
+ (moments[0]-moments[1])*sin(phi2)*sin(2*phi3));
+
+ // The paragraph prior to equation 5.16 describes how to choose
+ // the candidate value of phi1 based on the length
+ // of the f1 and f2 vectors.
+ // * The case of |f1| == |f2| == 0 implies a repeated moment,
+ // which should not be possible at this point in the code
+ // * When |f1| != 0 and |f2| != 0, then one should choose the
+ // value of phi2 so that phi11 = phi12
+ // * When |f1| == 0 and f2 != 0, then phi1 = phi12
+ // phi11 can be ignored, and either sign of phi2, phi3 can be used
+ // * When |f2| == 0 and f1 != 0, then phi1 = phi11
+ // phi12 can be ignored, and either sign of phi2, phi3 can be used
+ bool f1small = f1.SquaredLength() < std::pow(tol, 2);
+ bool f2small = f2.SquaredLength() < std::pow(tol, 2);
+ if (f1small && f2small)
+ {
+ // this should never happen
+ // f1small && f2small implies a repeated moment
+ // return invalid quaternion
+ /// \todo Use a mock class to test this line
+ return Quaternion<T>::Zero;
+ }
+ else if (f1small)
+ {
+ // use phi12 (equations 5.12, 5.14)
+ math::Angle phi12(0.5*(Angle2(g2, tol) - Angle2(f2, tol)));
+ phi12.Normalize();
+ phi1 = phi12.Radian();
+ }
+ else if (f2small)
+ {
+ // use phi11 (equations 5.11, 5.13)
+ math::Angle phi11(Angle2(g1, tol) - Angle2(f1, tol));
+ phi11.Normalize();
+ phi1 = phi11.Radian();
+ }
+ else
+ {
+ // check for when phi11 == phi12
+ // eqs 5.11, 5.13:
+ math::Angle phi11(Angle2(g1, tol) - Angle2(f1, tol));
+ phi11.Normalize();
+ // eqs 5.12, 5.14:
+ math::Angle phi12(0.5*(Angle2(g2, tol) - Angle2(f2, tol)));
+ phi12.Normalize();
+ T err = std::pow(sin(phi11.Radian()) - sin(phi12.Radian()), 2)
+ + std::pow(cos(phi11.Radian()) - cos(phi12.Radian()), 2);
+ phi1 = phi11.Radian();
+ math::Vector2<T> signsPhi23(1, 1);
+ // case a: phi2 <= 0
+ {
+ Vector2<T> g1a = Vector2<T>(1, -1) * g1;
+ Vector2<T> g2a = Vector2<T>(1, -1) * g2;
+ math::Angle phi11a(Angle2(g1a, tol) - Angle2(f1, tol));
+ math::Angle phi12a(0.5*(Angle2(g2a, tol) - Angle2(f2, tol)));
+ phi11a.Normalize();
+ phi12a.Normalize();
+ T erra = std::pow(sin(phi11a.Radian()) - sin(phi12a.Radian()), 2)
+ + std::pow(cos(phi11a.Radian()) - cos(phi12a.Radian()), 2);
+ if (erra < err)
+ {
+ err = erra;
+ phi1 = phi11a.Radian();
+ signsPhi23.Set(-1, 1);
+ }
+ }
+ // case b: phi3 <= 0
+ {
+ Vector2<T> g1b = Vector2<T>(-1, 1) * g1;
+ Vector2<T> g2b = Vector2<T>(1, -1) * g2;
+ math::Angle phi11b(Angle2(g1b, tol) - Angle2(f1, tol));
+ math::Angle phi12b(0.5*(Angle2(g2b, tol) - Angle2(f2, tol)));
+ phi11b.Normalize();
+ phi12b.Normalize();
+ T errb = std::pow(sin(phi11b.Radian()) - sin(phi12b.Radian()), 2)
+ + std::pow(cos(phi11b.Radian()) - cos(phi12b.Radian()), 2);
+ if (errb < err)
+ {
+ err = errb;
+ phi1 = phi11b.Radian();
+ signsPhi23.Set(1, -1);
+ }
+ }
+ // case c: phi2,phi3 <= 0
+ {
+ Vector2<T> g1c = Vector2<T>(-1, -1) * g1;
+ Vector2<T> g2c = g2;
+ math::Angle phi11c(Angle2(g1c, tol) - Angle2(f1, tol));
+ math::Angle phi12c(0.5*(Angle2(g2c, tol) - Angle2(f2, tol)));
+ phi11c.Normalize();
+ phi12c.Normalize();
+ T errc = std::pow(sin(phi11c.Radian()) - sin(phi12c.Radian()), 2)
+ + std::pow(cos(phi11c.Radian()) - cos(phi12c.Radian()), 2);
+ if (errc < err)
+ {
+ err = errc;
+ phi1 = phi11c.Radian();
+ signsPhi23.Set(-1, -1);
+ }
+ }
+
+ // apply sign changes
+ phi2 *= signsPhi23[0];
+ phi3 *= signsPhi23[1];
+ }
+
+ // I determined these arguments using trial and error
+ return Quaternion<T>(-phi1, -phi2, -phi3).Inverse();
+ }
+
+ /// \brief Get dimensions and rotation offset of uniform box
+ /// with equivalent mass and moment of inertia.
+ /// To compute this, the Matrix3 is diagonalized.
+ /// The eigenvalues on the diagonal and the rotation offset
+ /// of the principal axes are returned.
+ /// \param[in] _size Dimensions of box aligned with principal axes.
+ /// \param[in] _rot Rotational offset of principal axes.
+ /// \param[in] _tol Relative tolerance.
+ /// \return True if box properties were computed successfully.
+ public: bool EquivalentBox(Vector3<T> &_size,
+ Quaternion<T> &_rot,
+ const T _tol = 1e-6) const
+ {
+ if (!this->IsPositive())
+ {
+ // inertia is not positive, cannot compute equivalent box
+ return false;
+ }
+
+ Vector3<T> moments = this->PrincipalMoments(_tol);
+ if (!ValidMoments(moments))
+ {
+ // principal moments don't satisfy the triangle identity
+ return false;
+ }
+
+ // The reason for checking that the principal moments satisfy
+ // the triangle inequality
+ // I1 + I2 - I3 >= 0
+ // is to ensure that the arguments to sqrt in these equations
+ // are positive and the box size is real.
+ _size.X(sqrt(6*(moments.Y() + moments.Z() - moments.X()) / this->mass));
+ _size.Y(sqrt(6*(moments.Z() + moments.X() - moments.Y()) / this->mass));
+ _size.Z(sqrt(6*(moments.X() + moments.Y() - moments.Z()) / this->mass));
+
+ _rot = this->PrincipalAxesOffset(_tol);
+
+ if (_rot == Quaternion<T>::Zero)
+ {
+ // _rot is an invalid quaternion
+ /// \todo Use a mock class to test this line
+ return false;
+ }
+
+ return true;
+ }
+
+ /// \brief Set inertial properties based on mass and equivalent box.
+ /// \param[in] _mass Mass to set.
+ /// \param[in] _size Size of equivalent box.
+ /// \param[in] _rot Rotational offset of equivalent box.
+ /// \return True if inertial properties were set successfully.
+ public: bool SetFromBox(const T _mass,
+ const Vector3<T> &_size,
+ const Quaternion<T> &_rot = Quaternion<T>::Identity)
+ {
+ // Check that _mass and _size are strictly positive
+ // and that quatenion is valid
+ if (_mass <= 0 || _size.Min() <= 0 || _rot == Quaternion<T>::Zero)
+ {
+ return false;
+ }
+ this->Mass(_mass);
+ return this->SetFromBox(_size, _rot);
+ }
+
+ /// \brief Set inertial properties based on equivalent box
+ /// using the current mass value.
+ /// \param[in] _size Size of equivalent box.
+ /// \param[in] _rot Rotational offset of equivalent box.
+ /// \return True if inertial properties were set successfully.
+ public: bool SetFromBox(const Vector3<T> &_size,
+ const Quaternion<T> &_rot = Quaternion<T>::Identity)
+ {
+ // Check that _mass and _size are strictly positive
+ // and that quatenion is valid
+ if (this->Mass() <= 0 || _size.Min() <= 0 ||
+ _rot == Quaternion<T>::Zero)
+ {
+ return false;
+ }
+
+ // Diagonal matrix L with principal moments
+ Matrix3<T> L;
+ T x2 = std::pow(_size.X(), 2);
+ T y2 = std::pow(_size.Y(), 2);
+ T z2 = std::pow(_size.Z(), 2);
+ L(0, 0) = this->mass / 12.0 * (y2 + z2);
+ L(1, 1) = this->mass / 12.0 * (z2 + x2);
+ L(2, 2) = this->mass / 12.0 * (x2 + y2);
+ Matrix3<T> R(_rot);
+ return this->MOI(R * L * R.Transposed());
+ }
+
+ /// \brief Set inertial properties based on mass and equivalent cylinder
+ /// aligned with Z axis.
+ /// \param[in] _mass Mass to set.
+ /// \param[in] _length Length of cylinder along Z axis.
+ /// \param[in] _radius Radius of cylinder.
+ /// \param[in] _rot Rotational offset of equivalent cylinder.
+ /// \return True if inertial properties were set successfully.
+ public: bool SetFromCylinderZ(const T _mass,
+ const T _length,
+ const T _radius,
+ const Quaternion<T> &_rot = Quaternion<T>::Identity)
+ {
+ // Check that _mass, _radius and _length are strictly positive
+ // and that quatenion is valid
+ if (_mass <= 0 || _length <= 0 || _radius <= 0 ||
+ _rot == Quaternion<T>::Zero)
+ {
+ return false;
+ }
+ this->Mass(_mass);
+ return this->SetFromCylinderZ(_length, _radius, _rot);
+ }
+
+ /// \brief Set inertial properties based on equivalent cylinder
+ /// aligned with Z axis using the current mass value.
+ /// \param[in] _length Length of cylinder along Z axis.
+ /// \param[in] _radius Radius of cylinder.
+ /// \param[in] _rot Rotational offset of equivalent cylinder.
+ /// \return True if inertial properties were set successfully.
+ public: bool SetFromCylinderZ(const T _length,
+ const T _radius,
+ const Quaternion<T> &_rot)
+ {
+ // Check that _mass and _size are strictly positive
+ // and that quatenion is valid
+ if (this->Mass() <= 0 || _length <= 0 || _radius <= 0 ||
+ _rot == Quaternion<T>::Zero)
+ {
+ return false;
+ }
+
+ // Diagonal matrix L with principal moments
+ T radius2 = std::pow(_radius, 2);
+ Matrix3<T> L;
+ L(0, 0) = this->mass / 12.0 * (3*radius2 + std::pow(_length, 2));
+ L(1, 1) = L(0, 0);
+ L(2, 2) = this->mass / 2.0 * radius2;
+ Matrix3<T> R(_rot);
+ return this->MOI(R * L * R.Transposed());
+ }
+
+ /// \brief Set inertial properties based on mass and equivalent sphere.
+ /// \param[in] _mass Mass to set.
+ /// \param[in] _radius Radius of equivalent, uniform sphere.
+ /// \return True if inertial properties were set successfully.
+ public: bool SetFromSphere(const T _mass, const T _radius)
+ {
+ // Check that _mass and _radius are strictly positive
+ if (_mass <= 0 || _radius <= 0)
+ {
+ return false;
+ }
+ this->Mass(_mass);
+ return this->SetFromSphere(_radius);
+ }
+
+ /// \brief Set inertial properties based on equivalent sphere
+ /// using the current mass value.
+ /// \param[in] _radius Radius of equivalent, uniform sphere.
+ /// \return True if inertial properties were set successfully.
+ public: bool SetFromSphere(const T _radius)
+ {
+ // Check that _mass and _radius are strictly positive
+ if (this->Mass() <= 0 || _radius <= 0)
+ {
+ return false;
+ }
+
+ // Diagonal matrix L with principal moments
+ T radius2 = std::pow(_radius, 2);
+ Matrix3<T> L;
+ L(0, 0) = 0.4 * this->mass * radius2;
+ L(1, 1) = 0.4 * this->mass * radius2;
+ L(2, 2) = 0.4 * this->mass * radius2;
+ return this->MOI(L);
+ }
+
+ /// \brief Square root of positive numbers, otherwise zero.
+ /// \param[in] _x Number to be square rooted.
+ /// \return sqrt(_x) if _x > 0, otherwise 0
+ private: static inline T ClampedSqrt(const T &_x)
+ {
+ if (_x <= 0)
+ return 0;
+ return sqrt(_x);
+ }
+
+ /// \brief Angle formed by direction of a Vector2.
+ /// \param[in] _v Vector whose direction is to be computed.
+ /// \param[in] _eps Minimum length of vector required for computing angle.
+ /// \return Angle formed between vector and X axis,
+ /// or zero if vector has length less than 1e-6.
+ private: static T Angle2(const Vector2<T> &_v, const T _eps = 1e-6)
+ {
+ if (_v.SquaredLength() < std::pow(_eps, 2))
+ return 0;
+ return atan2(_v[1], _v[0]);
+ }
+
+ /// \brief Mass of the object. Default is 0.0.
+ private: T mass;
+
+ /// \brief Principal moments of inertia. Default is (0.0 0.0 0.0)
+ /// These Moments of Inertia are specified in the local frame.
+ /// Where Ixxyyzz.x is Ixx, Ixxyyzz.y is Iyy and Ixxyyzz.z is Izz.
+ private: Vector3<T> Ixxyyzz;
+
+ /// \brief Product moments of inertia. Default is (0.0 0.0 0.0)
+ /// These MOI off-diagonals are specified in the local frame.
+ /// Where Ixyxzyz.x is Ixy, Ixyxzyz.y is Ixz and Ixyxzyz.z is Iyz.
+ private: Vector3<T> Ixyxzyz;
+ };
+
+ typedef MassMatrix3<double> MassMatrix3d;
+ typedef MassMatrix3<float> MassMatrix3f;
+ }
+}
+#endif
diff --git a/include/ignition/math/MassMatrix3.ipynb b/include/ignition/math/MassMatrix3.ipynb
new file mode 100644
index 0000000..0e94e96
--- /dev/null
+++ b/include/ignition/math/MassMatrix3.ipynb
@@ -0,0 +1,134 @@
+{
+ "metadata": {
+ "name": "",
+ "signature": "sha256:806f7b6e2812a7f1e8ff67e708d852d06c6ed452860f8c9bb02bb4ff9ea5fdbf"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+ {
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Diagonalization of 3x3 symmetric matrix\n",
+ "\n",
+ "The `MassMatrix3` class uses an algorithm described in [A Method for Fast Diagonalization of a 2x2 or 3x3 Real Symmetric Matrix](http://arxiv.org/abs/1306.6291v4) by M.J. Kronenburg. This document proves that when a matrix $\\textbf{A}$ has a repeated eigenvalue and the vector $\\textbf{f}_2$ has length 0, then $\\textbf{A}$ is a diagonal matrix.\n",
+ "\n",
+ "The symmetric 3x3 matrix $\\textbf{A}$ has diagonal elements $A_{11}$, $A_{22}$, and $A_{33}$ and off-diagonal terms $A_{12}$, $A_{13}$, and $A_{23}$. The vector $\\textbf{f}_2$ is defined in (5.6) as\n",
+ "\n",
+ "$\\textbf{f}_2 = [A_{22} - A_{33}, -2A_{23}]^T$\n",
+ "\n",
+ "When $\\textbf{f}_2$ has length zero, the following are true:\n",
+ "\n",
+ "$A_{22} = A_{33} = A$\n",
+ "\n",
+ "$A_{23} = 0$\n",
+ "\n",
+ "In Section 4, conditions are given for which a matrix will have at least 1 repeated eigenvalue $\\lambda$ based on the coefficients of its characteristic equation:\n",
+ "\n",
+ "$\\lambda^3 - b \\lambda^2 + c \\lambda + d = 0$\n",
+ "\n",
+ "With $p = b^2 - 3c$ and $q = 2b^3 - 9bc - 27d$, there is at least one repeated eigenvalue when $q^2 = 4p^3$\n",
+ "\n",
+ "These conditions are manipulated symbolically below:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "from sympy import *"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "# Identify condition in which there is a repeated eigenvalue (Q^2 - 4P^3 = 0)\n",
+ "B, C, D, P, Q = symbols('B C D P Q')\n",
+ "P = B**2 - 3*C\n",
+ "Q = 2*B**3 - 9*B*C - 27*D\n",
+ "simplify(expand(Q**2 - 4*P**3))"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [
+ {
+ "metadata": {},
+ "output_type": "pyout",
+ "prompt_number": 13,
+ "text": [
+ "-108*B**3*D - 27*B**2*C**2 + 486*B*C*D + 108*C**3 + 729*D**2"
+ ]
+ }
+ ],
+ "prompt_number": 13
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "First the condition in which there is a repeated eigen value ($q^2 = 4p^3$) is expressed in terms of $b$, $c$, and $d$ and simplified to:\n",
+ "\n",
+ "$-108b^3d - 27b^2c^2 + 486bcd + 108c^3 + 729d^2 = 0$"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "# Expand expression for which there is a repeated eigenvalue\n",
+ "A11, A12, A13, A = symbols('A11 A12 A13 A')\n",
+ "b, c, d = symbols('b c d')\n",
+ "b = A11 + 2*A\n",
+ "c = A11*A + A11*A + A**2 - A12**2 - A13**2\n",
+ "d = A*A13**2 + A*A12**2 - A11*A**2\n",
+ "factor(simplify(-108*b**3*d - 27*b**2*c**2 + 486*b*c*d + 108*c**3 + 729*d**2))"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [
+ {
+ "metadata": {},
+ "output_type": "pyout",
+ "prompt_number": 16,
+ "text": [
+ "-27*(A12**2 + A13**2)**2*(A**2 - 2*A*A11 + A11**2 + 4*A12**2 + 4*A13**2)"
+ ]
+ }
+ ],
+ "prompt_number": 16
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This condition is then expressed in terms of $A11$, $A$, $A12$, and $A13$ corresponding to $\\textbf{f}_2 = 0$.\n",
+ "After simplification and factoring, there are 2 cases for which the expression is equal to zero:\n",
+ "\n",
+ "$A_{12}^2 + A_{13}^2 = 0$\n",
+ "\n",
+ "$4A_{12}^2 + 4A_{13}^2 + (A - A_{11})^2 = 0$\n",
+ "\n",
+ "In both cases, $A_{12} = A_{13} = 0$ is a pre-requisite. Since $A_{23} = 0$ was already assumed, this implies that\n",
+ "the matrix must be diagonal if it has any repeated eigenvalues and $\\textbf{f}_2 = 0$."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ }
+ ],
+ "metadata": {}
+ }
+ ]
+}
\ No newline at end of file
diff --git a/include/ignition/math/Matrix3.hh b/include/ignition/math/Matrix3.hh
new file mode 100644
index 0000000..7fd2704
--- /dev/null
+++ b/include/ignition/math/Matrix3.hh
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_MATRIX3_HH_
+#define IGNITION_MATH_MATRIX3_HH_
+
+#include <algorithm>
+#include <cstring>
+#include <ignition/math/Helpers.hh>
+#include <ignition/math/Vector3.hh>
+#include <ignition/math/Quaternion.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ template <typename T> class Quaternion;
+
+ /// \class Matrix3 Matrix3.hh ignition/math/Matrix3.hh
+ /// \brief A 3x3 matrix class
+ template<typename T>
+ class Matrix3
+ {
+ /// \brief Identity matrix
+ public: static const Matrix3<T> Identity;
+
+ /// \brief Zero matrix
+ public: static const Matrix3<T> Zero;
+
+ /// \brief Constructor
+ public: Matrix3()
+ {
+ std::memset(this->data, 0, sizeof(this->data[0][0])*9);
+ }
+
+ /// \brief Copy constructor
+ /// \param _m Matrix to copy
+ public: Matrix3(const Matrix3<T> &_m)
+ {
+ std::memcpy(this->data, _m.data, sizeof(this->data[0][0])*9);
+ }
+
+ /// \brief Constructor
+ /// \param[in] _v00 Row 0, Col 0 value
+ /// \param[in] _v01 Row 0, Col 1 value
+ /// \param[in] _v02 Row 0, Col 2 value
+ /// \param[in] _v10 Row 1, Col 0 value
+ /// \param[in] _v11 Row 1, Col 1 value
+ /// \param[in] _v12 Row 1, Col 2 value
+ /// \param[in] _v20 Row 2, Col 0 value
+ /// \param[in] _v21 Row 2, Col 1 value
+ /// \param[in] _v22 Row 2, Col 2 value
+ public: Matrix3(T _v00, T _v01, T _v02,
+ T _v10, T _v11, T _v12,
+ T _v20, T _v21, T _v22)
+ {
+ this->data[0][0] = _v00;
+ this->data[0][1] = _v01;
+ this->data[0][2] = _v02;
+ this->data[1][0] = _v10;
+ this->data[1][1] = _v11;
+ this->data[1][2] = _v12;
+ this->data[2][0] = _v20;
+ this->data[2][1] = _v21;
+ this->data[2][2] = _v22;
+ }
+
+ /// \brief Construct Matrix3 from a quaternion.
+ /// \param[in] _q Quaternion.
+ public: explicit Matrix3(const Quaternion<T> &_q)
+ {
+ Quaternion<T> qt = _q;
+ qt.Normalize();
+ this->Set(1 - 2*qt.Y()*qt.Y() - 2 *qt.Z()*qt.Z(),
+ 2 * qt.X()*qt.Y() - 2*qt.Z()*qt.W(),
+ 2 * qt.X() * qt.Z() + 2 * qt.Y() * qt.W(),
+ 2 * qt.X() * qt.Y() + 2 * qt.Z() * qt.W(),
+ 1 - 2*qt.X()*qt.X() - 2 * qt.Z()*qt.Z(),
+ 2 * qt.Y() * qt.Z() - 2 * qt.X() * qt.W(),
+ 2 * qt.X() * qt.Z() - 2 * qt.Y() * qt.W(),
+ 2 * qt.Y() * qt.Z() + 2 * qt.X() * qt.W(),
+ 1 - 2 * qt.X()*qt.X() - 2 * qt.Y()*qt.Y());
+ }
+
+ /// \brief Desctructor
+ public: virtual ~Matrix3() {}
+
+ /// \brief Set values
+ /// \param[in] _v00 Row 0, Col 0 value
+ /// \param[in] _v01 Row 0, Col 1 value
+ /// \param[in] _v02 Row 0, Col 2 value
+ /// \param[in] _v10 Row 1, Col 0 value
+ /// \param[in] _v11 Row 1, Col 1 value
+ /// \param[in] _v12 Row 1, Col 2 value
+ /// \param[in] _v20 Row 2, Col 0 value
+ /// \param[in] _v21 Row 2, Col 1 value
+ /// \param[in] _v22 Row 2, Col 2 value
+ public: void Set(T _v00, T _v01, T _v02,
+ T _v10, T _v11, T _v12,
+ T _v20, T _v21, T _v22)
+ {
+ this->data[0][0] = _v00;
+ this->data[0][1] = _v01;
+ this->data[0][2] = _v02;
+ this->data[1][0] = _v10;
+ this->data[1][1] = _v11;
+ this->data[1][2] = _v12;
+ this->data[2][0] = _v20;
+ this->data[2][1] = _v21;
+ this->data[2][2] = _v22;
+ }
+
+ /// \brief Set the matrix from three axis (1 per column)
+ /// \param[in] _xAxis The x axis
+ /// \param[in] _yAxis The y axis
+ /// \param[in] _zAxis The z axis
+ public: void Axes(const Vector3<T> &_xAxis,
+ const Vector3<T> &_yAxis,
+ const Vector3<T> &_zAxis)
+ {
+ this->Col(0, _xAxis);
+ this->Col(1, _yAxis);
+ this->Col(2, _zAxis);
+ }
+
+ /// \brief Set the matrix from an axis and angle
+ /// \param[in] _axis the axis
+ /// \param[in] _angle ccw rotation around the axis in radians
+ public: void Axis(const Vector3<T> &_axis, T _angle)
+ {
+ T c = cos(_angle);
+ T s = sin(_angle);
+ T C = 1-c;
+
+ this->data[0][0] = _axis.X()*_axis.X()*C + c;
+ this->data[0][1] = _axis.X()*_axis.Y()*C - _axis.Z()*s;
+ this->data[0][2] = _axis.X()*_axis.Z()*C + _axis.Y()*s;
+
+ this->data[1][0] = _axis.Y()*_axis.X()*C + _axis.Z()*s;
+ this->data[1][1] = _axis.Y()*_axis.Y()*C + c;
+ this->data[1][2] = _axis.Y()*_axis.Z()*C - _axis.X()*s;
+
+ this->data[2][0] = _axis.Z()*_axis.X()*C - _axis.Y()*s;
+ this->data[2][1] = _axis.Z()*_axis.Y()*C + _axis.X()*s;
+ this->data[2][2] = _axis.Z()*_axis.Z()*C + c;
+ }
+
+ /// \brief Set the matrix to represent rotation from
+ /// vector _v1 to vector _v2, so that
+ /// _v2.Normalize() == this * _v1.Normalize() holds.
+ ///
+ /// \param[in] _v1 The first vector
+ /// \param[in] _v2 The second vector
+ public: void From2Axes(const Vector3<T> &_v1, const Vector3<T> &_v2)
+ {
+ const T _v1LengthSquared = _v1.SquaredLength();
+ if (_v1LengthSquared <= 0.0)
+ {
+ // zero vector - we can't handle this
+ this->Set(1, 0, 0, 0, 1, 0, 0, 0, 1);
+ return;
+ }
+
+ const T _v2LengthSquared = _v2.SquaredLength();
+ if (_v2LengthSquared <= 0.0)
+ {
+ // zero vector - we can't handle this
+ this->Set(1, 0, 0, 0, 1, 0, 0, 0, 1);
+ return;
+ }
+
+ const T dot = _v1.Dot(_v2) / sqrt(_v1LengthSquared * _v2LengthSquared);
+ if (fabs(dot - 1.0) <= 1e-6)
+ {
+ // the vectors are parallel
+ this->Set(1, 0, 0, 0, 1, 0, 0, 0, 1);
+ return;
+ }
+ else if (fabs(dot + 1.0) <= 1e-6)
+ {
+ // the vectors are opposite
+ this->Set(-1, 0, 0, 0, -1, 0, 0, 0, -1);
+ return;
+ }
+
+ const Vector3<T> cross = _v1.Cross(_v2).Normalize();
+
+ this->Axis(cross, acos(dot));
+ }
+
+ /// \brief Set a column.
+ /// \param[in] _c The colum index [0, 1, 2]. _col is clamped to the
+ /// range [0, 2].
+ /// \param[in] _v The value to set in each row of the column.
+ public: void Col(unsigned int _c, const Vector3<T> &_v)
+ {
+ unsigned int c = clamp(_c, 0u, 2u);
+
+ this->data[0][c] = _v.X();
+ this->data[1][c] = _v.Y();
+ this->data[2][c] = _v.Z();
+ }
+
+ /// \brief Equal operator. this = _mat
+ /// \param _mat Incoming matrix
+ /// \return itself
+ public: Matrix3<T> &operator=(const Matrix3<T> &_mat)
+ {
+ memcpy(this->data, _mat.data, sizeof(this->data[0][0])*9);
+ return *this;
+ }
+
+ /// \brief returns the element wise difference of two matrices
+ public: Matrix3<T> operator-(const Matrix3<T> &_m) const
+ {
+ return Matrix3<T>(
+ this->data[0][0] - _m(0, 0),
+ this->data[0][1] - _m(0, 1),
+ this->data[0][2] - _m(0, 2),
+ this->data[1][0] - _m(1, 0),
+ this->data[1][1] - _m(1, 1),
+ this->data[1][2] - _m(1, 2),
+ this->data[2][0] - _m(2, 0),
+ this->data[2][1] - _m(2, 1),
+ this->data[2][2] - _m(2, 2));
+ }
+
+ /// \brief returns the element wise sum of two matrices
+ public: Matrix3<T> operator+(const Matrix3<T> &_m) const
+ {
+ return Matrix3<T>(
+ this->data[0][0]+_m(0, 0),
+ this->data[0][1]+_m(0, 1),
+ this->data[0][2]+_m(0, 2),
+ this->data[1][0]+_m(1, 0),
+ this->data[1][1]+_m(1, 1),
+ this->data[1][2]+_m(1, 2),
+ this->data[2][0]+_m(2, 0),
+ this->data[2][1]+_m(2, 1),
+ this->data[2][2]+_m(2, 2));
+ }
+
+ /// \brief returns the element wise scalar multiplication
+ public: Matrix3<T> operator*(const T &_s) const
+ {
+ return Matrix3<T>(
+ _s * this->data[0][0], _s * this->data[0][1], _s * this->data[0][2],
+ _s * this->data[1][0], _s * this->data[1][1], _s * this->data[1][2],
+ _s * this->data[2][0], _s * this->data[2][1], _s * this->data[2][2]);
+ }
+
+ /// \brief Matrix multiplication operator
+ /// \param[in] _m Matrix3<T> to multiply
+ /// \return product of this * _m
+ public: Matrix3<T> operator*(const Matrix3<T> &_m) const
+ {
+ return Matrix3<T>(
+ // first row
+ this->data[0][0]*_m(0, 0)+
+ this->data[0][1]*_m(1, 0)+
+ this->data[0][2]*_m(2, 0),
+
+ this->data[0][0]*_m(0, 1)+
+ this->data[0][1]*_m(1, 1)+
+ this->data[0][2]*_m(2, 1),
+
+ this->data[0][0]*_m(0, 2)+
+ this->data[0][1]*_m(1, 2)+
+ this->data[0][2]*_m(2, 2),
+
+ // second row
+ this->data[1][0]*_m(0, 0)+
+ this->data[1][1]*_m(1, 0)+
+ this->data[1][2]*_m(2, 0),
+
+ this->data[1][0]*_m(0, 1)+
+ this->data[1][1]*_m(1, 1)+
+ this->data[1][2]*_m(2, 1),
+
+ this->data[1][0]*_m(0, 2)+
+ this->data[1][1]*_m(1, 2)+
+ this->data[1][2]*_m(2, 2),
+
+ // third row
+ this->data[2][0]*_m(0, 0)+
+ this->data[2][1]*_m(1, 0)+
+ this->data[2][2]*_m(2, 0),
+
+ this->data[2][0]*_m(0, 1)+
+ this->data[2][1]*_m(1, 1)+
+ this->data[2][2]*_m(2, 1),
+
+ this->data[2][0]*_m(0, 2)+
+ this->data[2][1]*_m(1, 2)+
+ this->data[2][2]*_m(2, 2));
+ }
+
+ /// \brief Multiplication operator with Vector3 on the right
+ /// treated like a column vector.
+ /// \param _vec Vector3
+ /// \return Resulting vector from multiplication
+ public: Vector3<T> operator*(const Vector3<T> &_vec) const
+ {
+ return Vector3<T>(
+ this->data[0][0]*_vec.X() + this->data[0][1]*_vec.Y() +
+ this->data[0][2]*_vec.Z(),
+ this->data[1][0]*_vec.X() + this->data[1][1]*_vec.Y() +
+ this->data[1][2]*_vec.Z(),
+ this->data[2][0]*_vec.X() + this->data[2][1]*_vec.Y() +
+ this->data[2][2]*_vec.Z());
+ }
+
+ /// \brief Matrix multiplication operator for scaling.
+ /// \param[in] _s Scaling factor.
+ /// \param[in] _m Input matrix.
+ /// \return A scaled matrix.
+ public: friend inline Matrix3<T> operator*(T _s, const Matrix3<T> &_m)
+ {
+ return _m * _s;
+ }
+
+ /// \brief Matrix left multiplication operator for Vector3.
+ /// Treats the Vector3 like a row vector multiplying the matrix
+ /// from the left.
+ /// \param[in] _v Input vector.
+ /// \param[in] _m Input matrix.
+ /// \return The product vector.
+ public: friend inline Vector3<T> operator*(const Vector3<T> &_v,
+ const Matrix3<T> &_m)
+ {
+ return Vector3<T>(
+ _m(0, 0)*_v.X() + _m(1, 0)*_v.Y() + _m(2, 0)*_v.Z(),
+ _m(0, 1)*_v.X() + _m(1, 1)*_v.Y() + _m(2, 1)*_v.Z(),
+ _m(0, 2)*_v.X() + _m(1, 2)*_v.Y() + _m(2, 2)*_v.Z());
+ }
+
+ /// \brief Equality test with tolerance.
+ /// \param[in] _m the matrix to compare to
+ /// \param[in] _tol equality tolerance.
+ /// \return true if the elements of the matrices are equal within
+ /// the tolerence specified by _tol.
+ public: bool Equal(const Matrix3 &_m, const T &_tol) const
+ {
+ return equal<T>(this->data[0][0], _m(0, 0), _tol)
+ && equal<T>(this->data[0][1], _m(0, 1), _tol)
+ && equal<T>(this->data[0][2], _m(0, 2), _tol)
+ && equal<T>(this->data[1][0], _m(1, 0), _tol)
+ && equal<T>(this->data[1][1], _m(1, 1), _tol)
+ && equal<T>(this->data[1][2], _m(1, 2), _tol)
+ && equal<T>(this->data[2][0], _m(2, 0), _tol)
+ && equal<T>(this->data[2][1], _m(2, 1), _tol)
+ && equal<T>(this->data[2][2], _m(2, 2), _tol);
+ }
+
+ /// \brief Equality test operator
+ /// \param[in] _m Matrix3<T> to test
+ /// \return True if equal (using the default tolerance of 1e-6)
+ public: bool operator==(const Matrix3<T> &_m) const
+ {
+ return this->Equal(_m, static_cast<T>(1e-6));
+ }
+
+ /// \brief Set the matrix3 from a quaternion
+ /// \param[in] _q Quaternion to set the matrix3 from.
+ /// \return Reference to the new matrix3 object.
+ public: Matrix3<T> &operator=(const Quaternion<T> &_q)
+ {
+ return *this = Matrix3<T>(_q);
+ }
+
+ /// \brief Inequality test operator
+ /// \param[in] _m Matrix3<T> to test
+ /// \return True if not equal (using the default tolerance of 1e-6)
+ public: bool operator!=(const Matrix3<T> &_m) const
+ {
+ return !(*this == _m);
+ }
+
+ /// \brief Array subscript operator
+ /// \param[in] _row row index. _row is clamped to the range [0,2]
+ /// \param[in] _col column index. _col is clamped to the range [0,2]
+ /// \return a pointer to the row
+ public: inline const T &operator()(size_t _row, size_t _col) const
+ {
+ return this->data[clamp(_row, IGN_ZERO_SIZE_T, IGN_TWO_SIZE_T)]
+ [clamp(_col, IGN_ZERO_SIZE_T, IGN_TWO_SIZE_T)];
+ }
+
+ /// \brief Array subscript operator
+ /// \param[in] _row row index. _row is clamped to the range [0,2]
+ /// \param[in] _col column index. _col is clamped to the range [0,2]
+ /// \return a pointer to the row
+ public: inline T &operator()(size_t _row, size_t _col)
+ {
+ return this->data[clamp(_row, IGN_ZERO_SIZE_T, IGN_TWO_SIZE_T)]
+ [clamp(_col, IGN_ZERO_SIZE_T, IGN_TWO_SIZE_T)];
+ }
+
+ /// \brief Return the determinant of the matrix
+ /// \return Determinant of this matrix.
+ public: T Determinant() const
+ {
+ T t0 = this->data[2][2]*this->data[1][1]
+ - this->data[2][1]*this->data[1][2];
+
+ T t1 = -(this->data[2][2]*this->data[1][0]
+ -this->data[2][0]*this->data[1][2]);
+
+ T t2 = this->data[2][1]*this->data[1][0]
+ - this->data[2][0]*this->data[1][1];
+
+ return t0 * this->data[0][0]
+ + t1 * this->data[0][1]
+ + t2 * this->data[0][2];
+ }
+
+ /// \brief Return the inverse matrix
+ /// \return Inverse of this matrix.
+ public: Matrix3<T> Inverse() const
+ {
+ T t0 = this->data[2][2]*this->data[1][1] -
+ this->data[2][1]*this->data[1][2];
+
+ T t1 = -(this->data[2][2]*this->data[1][0] -
+ this->data[2][0]*this->data[1][2]);
+
+ T t2 = this->data[2][1]*this->data[1][0] -
+ this->data[2][0]*this->data[1][1];
+
+ T invDet = 1.0 / (t0 * this->data[0][0] +
+ t1 * this->data[0][1] +
+ t2 * this->data[0][2]);
+
+ return invDet * Matrix3<T>(
+ t0,
+ - (this->data[2][2] * this->data[0][1] -
+ this->data[2][1] * this->data[0][2]),
+ + (this->data[1][2] * this->data[0][1] -
+ this->data[1][1] * this->data[0][2]),
+ t1,
+ + (this->data[2][2] * this->data[0][0] -
+ this->data[2][0] * this->data[0][2]),
+ - (this->data[1][2] * this->data[0][0] -
+ this->data[1][0] * this->data[0][2]),
+ t2,
+ - (this->data[2][1] * this->data[0][0] -
+ this->data[2][0] * this->data[0][1]),
+ + (this->data[1][1] * this->data[0][0] -
+ this->data[1][0] * this->data[0][1]));
+ }
+
+ /// \brief Transpose this matrix.
+ public: void Transpose()
+ {
+ std::swap(this->data[0][1], this->data[1][0]);
+ std::swap(this->data[0][2], this->data[2][0]);
+ std::swap(this->data[1][2], this->data[2][1]);
+ }
+
+ /// \brief Return the transpose of this matrix
+ /// \return Transpose of this matrix.
+ public: Matrix3<T> Transposed() const
+ {
+ return Matrix3<T>(
+ this->data[0][0], this->data[1][0], this->data[2][0],
+ this->data[0][1], this->data[1][1], this->data[2][1],
+ this->data[0][2], this->data[1][2], this->data[2][2]);
+ }
+
+ /// \brief Stream insertion operator
+ /// \param[in] _out Output stream
+ /// \param[in] _m Matrix to output
+ /// \return the stream
+ public: friend std::ostream &operator<<(
+ std::ostream &_out, const ignition::math::Matrix3<T> &_m)
+ {
+ _out << precision(_m(0, 0), 6) << " "
+ << precision(_m(0, 1), 6) << " "
+ << precision(_m(0, 2), 6) << " "
+ << precision(_m(1, 0), 6) << " "
+ << precision(_m(1, 1), 6) << " "
+ << precision(_m(1, 2), 6) << " "
+ << precision(_m(2, 0), 6) << " "
+ << precision(_m(2, 1), 6) << " "
+ << precision(_m(2, 2), 6);
+
+ return _out;
+ }
+ /// \brief Stream extraction operator
+ /// \param _in input stream
+ /// \param _pt Matrix3 to read values into
+ /// \return the stream
+ public: friend std::istream &operator>>(
+ std::istream &_in, ignition::math::Matrix3<T> &_m)
+ {
+ // Skip white spaces
+ _in.setf(std::ios_base::skipws);
+ T d[9];
+ _in >> d[0] >> d[1] >> d[2]
+ >> d[3] >> d[4] >> d[5]
+ >> d[6] >> d[7] >> d[8];
+
+ _m.Set(d[0], d[1], d[2],
+ d[3], d[4], d[5],
+ d[6], d[7], d[8]);
+ return _in;
+ }
+
+ /// \brief the 3x3 matrix
+ private: T data[3][3];
+ };
+
+ template<typename T>
+ const Matrix3<T> Matrix3<T>::Identity(
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1);
+
+ template<typename T>
+ const Matrix3<T> Matrix3<T>::Zero(
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0);
+
+ typedef Matrix3<int> Matrix3i;
+ typedef Matrix3<double> Matrix3d;
+ typedef Matrix3<float> Matrix3f;
+ }
+}
+
+#endif
diff --git a/include/ignition/math/Matrix4.hh b/include/ignition/math/Matrix4.hh
new file mode 100644
index 0000000..2784954
--- /dev/null
+++ b/include/ignition/math/Matrix4.hh
@@ -0,0 +1,890 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_MATRIX4_HH_
+#define IGNITION_MATH_MATRIX4_HH_
+
+#include <algorithm>
+#include <ignition/math/Helpers.hh>
+#include <ignition/math/Matrix3.hh>
+#include <ignition/math/Vector3.hh>
+#include <ignition/math/Pose3.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class Matrix4 Matrix4.hh ignition/math/Matrix4.hh
+ /// \brief A 4x4 matrix class
+ template<typename T>
+ class Matrix4
+ {
+ /// \brief Identity matrix
+ public: static const Matrix4<T> Identity;
+
+ /// \brief Zero matrix
+ public: static const Matrix4<T> Zero;
+
+ /// \brief Constructor
+ public: Matrix4()
+ {
+ memset(this->data, 0, sizeof(this->data[0][0])*16);
+ }
+
+ /// \brief Copy constructor
+ /// \param _m Matrix to copy
+ public: Matrix4(const Matrix4<T> &_m)
+ {
+ memcpy(this->data, _m.data, sizeof(this->data[0][0])*16);
+ }
+
+ /// \brief Constructor
+ /// \param[in] _v00 Row 0, Col 0 value
+ /// \param[in] _v01 Row 0, Col 1 value
+ /// \param[in] _v02 Row 0, Col 2 value
+ /// \param[in] _v03 Row 0, Col 3 value
+ /// \param[in] _v10 Row 1, Col 0 value
+ /// \param[in] _v11 Row 1, Col 1 value
+ /// \param[in] _v12 Row 1, Col 2 value
+ /// \param[in] _v13 Row 1, Col 3 value
+ /// \param[in] _v20 Row 2, Col 0 value
+ /// \param[in] _v21 Row 2, Col 1 value
+ /// \param[in] _v22 Row 2, Col 2 value
+ /// \param[in] _v23 Row 2, Col 3 value
+ /// \param[in] _v30 Row 3, Col 0 value
+ /// \param[in] _v31 Row 3, Col 1 value
+ /// \param[in] _v32 Row 3, Col 2 value
+ /// \param[in] _v33 Row 3, Col 3 value
+ public: Matrix4(T _v00, T _v01, T _v02, T _v03,
+ T _v10, T _v11, T _v12, T _v13,
+ T _v20, T _v21, T _v22, T _v23,
+ T _v30, T _v31, T _v32, T _v33)
+ {
+ this->Set(_v00, _v01, _v02, _v03,
+ _v10, _v11, _v12, _v13,
+ _v20, _v21, _v22, _v23,
+ _v30, _v31, _v32, _v33);
+ }
+
+ /// \brief Construct Matrix4 from a quaternion.
+ /// \param[in] _q Quaternion.
+ public: explicit Matrix4(const Quaternion<T> &_q)
+ {
+ Quaternion<T> qt = _q;
+ qt.Normalize();
+ this->Set(1 - 2*qt.Y()*qt.Y() - 2 *qt.Z()*qt.Z(),
+ 2 * qt.X()*qt.Y() - 2*qt.Z()*qt.W(),
+ 2 * qt.X() * qt.Z() + 2 * qt.Y() * qt.W(),
+ 0,
+
+ 2 * qt.X() * qt.Y() + 2 * qt.Z() * qt.W(),
+ 1 - 2*qt.X()*qt.X() - 2 * qt.Z()*qt.Z(),
+ 2 * qt.Y() * qt.Z() - 2 * qt.X() * qt.W(),
+ 0,
+
+ 2 * qt.X() * qt.Z() - 2 * qt.Y() * qt.W(),
+ 2 * qt.Y() * qt.Z() + 2 * qt.X() * qt.W(),
+ 1 - 2 * qt.X()*qt.X() - 2 * qt.Y()*qt.Y(),
+ 0,
+
+ 0, 0, 0, 1);
+ }
+
+ /// \brief Construct Matrix4 from a math::Pose3
+ /// \param[in] _pose Pose.
+ public: explicit Matrix4(const Pose3<T> &_pose) : Matrix4(_pose.Rot())
+ {
+ this->Translate(_pose.Pos());
+ }
+
+ /// \brief Destructor
+ public: virtual ~Matrix4() {}
+
+ /// \brief Change the values
+ /// \param[in] _v00 Row 0, Col 0 value
+ /// \param[in] _v01 Row 0, Col 1 value
+ /// \param[in] _v02 Row 0, Col 2 value
+ /// \param[in] _v03 Row 0, Col 3 value
+ /// \param[in] _v10 Row 1, Col 0 value
+ /// \param[in] _v11 Row 1, Col 1 value
+ /// \param[in] _v12 Row 1, Col 2 value
+ /// \param[in] _v13 Row 1, Col 3 value
+ /// \param[in] _v20 Row 2, Col 0 value
+ /// \param[in] _v21 Row 2, Col 1 value
+ /// \param[in] _v22 Row 2, Col 2 value
+ /// \param[in] _v23 Row 2, Col 3 value
+ /// \param[in] _v30 Row 3, Col 0 value
+ /// \param[in] _v31 Row 3, Col 1 value
+ /// \param[in] _v32 Row 3, Col 2 value
+ /// \param[in] _v33 Row 3, Col 3 value
+ public: void Set(
+ T _v00, T _v01, T _v02, T _v03,
+ T _v10, T _v11, T _v12, T _v13,
+ T _v20, T _v21, T _v22, T _v23,
+ T _v30, T _v31, T _v32, T _v33)
+ {
+ this->data[0][0] = _v00;
+ this->data[0][1] = _v01;
+ this->data[0][2] = _v02;
+ this->data[0][3] = _v03;
+
+ this->data[1][0] = _v10;
+ this->data[1][1] = _v11;
+ this->data[1][2] = _v12;
+ this->data[1][3] = _v13;
+
+ this->data[2][0] = _v20;
+ this->data[2][1] = _v21;
+ this->data[2][2] = _v22;
+ this->data[2][3] = _v23;
+
+ this->data[3][0] = _v30;
+ this->data[3][1] = _v31;
+ this->data[3][2] = _v32;
+ this->data[3][3] = _v33;
+ }
+
+ /// \brief Set the upper-left 3x3 matrix from an axis and angle
+ /// \param[in] _axis the axis
+ /// \param[in] _angle ccw rotation around the axis in radians
+ public: void Axis(const Vector3<T> &_axis, T _angle)
+ {
+ T c = cos(_angle);
+ T s = sin(_angle);
+ T C = 1-c;
+
+ this->data[0][0] = _axis.X()*_axis.X()*C + c;
+ this->data[0][1] = _axis.X()*_axis.Y()*C - _axis.Z()*s;
+ this->data[0][2] = _axis.X()*_axis.Z()*C + _axis.Y()*s;
+
+ this->data[1][0] = _axis.Y()*_axis.X()*C + _axis.Z()*s;
+ this->data[1][1] = _axis.Y()*_axis.Y()*C + c;
+ this->data[1][2] = _axis.Y()*_axis.Z()*C - _axis.X()*s;
+
+ this->data[2][0] = _axis.Z()*_axis.X()*C - _axis.Y()*s;
+ this->data[2][1] = _axis.Z()*_axis.Y()*C + _axis.X()*s;
+ this->data[2][2] = _axis.Z()*_axis.Z()*C + c;
+ }
+
+ /// \brief Set the translational values [ (0, 3) (1, 3) (2, 3) ]
+ /// \param[in] _t Values to set
+ public: void Translate(const Vector3<T> &_t)
+ {
+ this->data[0][3] = _t.X();
+ this->data[1][3] = _t.Y();
+ this->data[2][3] = _t.Z();
+ }
+
+ /// \brief Set the translational values [ (0, 3) (1, 3) (2, 3) ]
+ /// \param[in] _x X translation value.
+ /// \param[in] _y Y translation value.
+ /// \param[in] _z Z translation value.
+ public: void Translate(T _x, T _y, T _z)
+ {
+ this->data[0][3] = _x;
+ this->data[1][3] = _y;
+ this->data[2][3] = _z;
+ }
+
+ /// \brief Get the translational values as a Vector3
+ /// \return x,y,z translation values
+ public: Vector3<T> Translation() const
+ {
+ return Vector3<T>(this->data[0][3], this->data[1][3], this->data[2][3]);
+ }
+
+ /// \brief Get the scale values as a Vector3<T>
+ /// \return x,y,z scale values
+ public: Vector3<T> Scale() const
+ {
+ return Vector3<T>(this->data[0][0], this->data[1][1], this->data[2][2]);
+ }
+
+ /// \brief Get the rotation as a quaternion
+ /// \return the rotation
+ public: Quaternion<T> Rotation() const
+ {
+ Quaternion<T> q;
+ /// algorithm from Ogre::Quaternion<T> source, which in turn is based on
+ /// Ken Shoemake's article "Quaternion<T> Calculus and Fast Animation".
+ T trace = this->data[0][0] + this->data[1][1] + this->data[2][2];
+ T root;
+ if (trace > 0)
+ {
+ root = sqrt(trace + 1.0);
+ q.W(root / 2.0);
+ root = 1.0 / (2.0 * root);
+ q.X((this->data[2][1] - this->data[1][2]) * root);
+ q.Y((this->data[0][2] - this->data[2][0]) * root);
+ q.Z((this->data[1][0] - this->data[0][1]) * root);
+ }
+ else
+ {
+ static unsigned int s_iNext[3] = {1, 2, 0};
+ unsigned int i = 0;
+ if (this->data[1][1] > this->data[0][0])
+ i = 1;
+ if (this->data[2][2] > this->data[i][i])
+ i = 2;
+ unsigned int j = s_iNext[i];
+ unsigned int k = s_iNext[j];
+
+ root = sqrt(this->data[i][i] - this->data[j][j] -
+ this->data[k][k] + 1.0);
+
+ T a, b, c;
+ a = root / 2.0;
+ root = 1.0 / (2.0 * root);
+ b = (this->data[j][i] + this->data[i][j]) * root;
+ c = (this->data[k][i] + this->data[i][k]) * root;
+
+ switch (i)
+ {
+ default:
+ case 0: q.X(a); break;
+ case 1: q.Y(a); break;
+ case 2: q.Z(a); break;
+ };
+ switch (j)
+ {
+ default:
+ case 0: q.X(b); break;
+ case 1: q.Y(b); break;
+ case 2: q.Z(b); break;
+ };
+ switch (k)
+ {
+ default:
+ case 0: q.X(c); break;
+ case 1: q.Y(c); break;
+ case 2: q.Z(c); break;
+ };
+
+ q.W((this->data[k][j] - this->data[j][k]) * root);
+ }
+
+ return q;
+ }
+
+ /// \brief Get the rotation as a Euler angles
+ /// \param[in] _firstSolution True to get the first Euler solution,
+ /// false to get the second.
+ /// \return the rotation
+ public: Vector3<T> EulerRotation(bool _firstSolution) const
+ {
+ Vector3<T> euler;
+ Vector3<T> euler2;
+
+ T m31 = this->data[2][0];
+ T m11 = this->data[0][0];
+ T m12 = this->data[0][1];
+ T m13 = this->data[0][2];
+ T m32 = this->data[2][1];
+ T m33 = this->data[2][2];
+ T m21 = this->data[1][0];
+
+ if (std::abs(m31) >= 1.0)
+ {
+ euler.Z(0.0);
+ euler2.Z(0.0);
+
+ if (m31 < 0.0)
+ {
+ euler.Y(IGN_PI / 2.0);
+ euler2.Y(IGN_PI / 2.0);
+ euler.X(atan2(m12, m13));
+ euler2.X(atan2(m12, m13));
+ }
+ else
+ {
+ euler.Y(-IGN_PI / 2.0);
+ euler2.Y(-IGN_PI / 2.0);
+ euler.X(atan2(-m12, -m13));
+ euler2.X(atan2(-m12, -m13));
+ }
+ }
+ else
+ {
+ euler.Y(-asin(m31));
+ euler2.Y(IGN_PI - euler.Y());
+
+ euler.X(atan2(m32 / cos(euler.Y()), m33 / cos(euler.Y())));
+ euler2.X(atan2(m32 / cos(euler2.Y()), m33 / cos(euler2.Y())));
+
+ euler.Z(atan2(m21 / cos(euler.Y()), m11 / cos(euler.Y())));
+ euler2.Z(atan2(m21 / cos(euler2.Y()), m11 / cos(euler2.Y())));
+ }
+
+ if (_firstSolution)
+ return euler;
+ else
+ return euler2;
+ }
+
+ /// \brief Get the transformation as math::Pose
+ /// \return the pose
+ public: Pose3<T> Pose() const
+ {
+ return Pose3<T>(this->Translation(), this->Rotation());
+ }
+
+ /// \brief Set the scale
+ /// \param[in] _s scale
+ public: void Scale(const Vector3<T> &_s)
+ {
+ this->data[0][0] = _s.X();
+ this->data[1][1] = _s.Y();
+ this->data[2][2] = _s.Z();
+ this->data[3][3] = 1.0;
+ }
+
+ /// \brief Set the scale
+ /// \param[in] _x X scale value.
+ /// \param[in] _y Y scale value.
+ /// \param[in] _z Z scale value.
+ public: void Scale(T _x, T _y, T _z)
+ {
+ this->data[0][0] = _x;
+ this->data[1][1] = _y;
+ this->data[2][2] = _z;
+ this->data[3][3] = 1.0;
+ }
+
+ /// \brief Return true if the matrix is affine
+ /// \return true if the matrix is affine, false otherwise
+ public: bool IsAffine() const
+ {
+ return equal(this->data[3][0], static_cast<T>(0)) &&
+ equal(this->data[3][1], static_cast<T>(0)) &&
+ equal(this->data[3][2], static_cast<T>(0)) &&
+ equal(this->data[3][3], static_cast<T>(1));
+ }
+
+ /// \brief Perform an affine transformation
+ /// \param[in] _v Vector3 value for the transformation
+ /// \return The result of the transformation. A default constructed
+ /// Vector3<T> is returned if this matrix is not affine.
+ /// \deprecated Use bool TransformAffine(const Vector3<T> &_v,
+ /// Vector3<T> &_result) const;
+ public: Vector3<T> TransformAffine(const Vector3<T> &_v) const
+#ifndef _WIN32
+ IGN_DEPRECATED(3.0)
+#endif
+ {
+ if (this->IsAffine())
+ {
+ return Vector3<T>(this->data[0][0]*_v.X() + this->data[0][1]*_v.Y() +
+ this->data[0][2]*_v.Z() + this->data[0][3],
+ this->data[1][0]*_v.X() + this->data[1][1]*_v.Y() +
+ this->data[1][2]*_v.Z() + this->data[1][3],
+ this->data[2][0]*_v.X() + this->data[2][1]*_v.Y() +
+ this->data[2][2]*_v.Z() + this->data[2][3]);
+ }
+ else
+ {
+ return Vector3<T>();
+ }
+ }
+
+ /// \brief Perform an affine transformation
+ /// \param[in] _v Vector3 value for the transformation
+ /// \param[out] _result The result of the transformation. _result is
+ /// not changed if this matrix is not affine.
+ /// \return True if this matrix is affine, false otherwise.
+ public: bool TransformAffine(const Vector3<T> &_v,
+ Vector3<T> &_result) const
+ {
+ if (!this->IsAffine())
+ return false;
+
+ _result.Set(this->data[0][0]*_v.X() + this->data[0][1]*_v.Y() +
+ this->data[0][2]*_v.Z() + this->data[0][3],
+ this->data[1][0]*_v.X() + this->data[1][1]*_v.Y() +
+ this->data[1][2]*_v.Z() + this->data[1][3],
+ this->data[2][0]*_v.X() + this->data[2][1]*_v.Y() +
+ this->data[2][2]*_v.Z() + this->data[2][3]);
+ return true;
+ }
+
+ /// \brief Return the determinant of the matrix
+ /// \return Determinant of this matrix.
+ public: T Determinant() const
+ {
+ T v0, v1, v2, v3, v4, v5, t00, t10, t20, t30;
+
+ v0 = this->data[2][0]*this->data[3][1]
+ - this->data[2][1]*this->data[3][0];
+ v1 = this->data[2][0]*this->data[3][2]
+ - this->data[2][2]*this->data[3][0];
+ v2 = this->data[2][0]*this->data[3][3]
+ - this->data[2][3]*this->data[3][0];
+ v3 = this->data[2][1]*this->data[3][2]
+ - this->data[2][2]*this->data[3][1];
+ v4 = this->data[2][1]*this->data[3][3]
+ - this->data[2][3]*this->data[3][1];
+ v5 = this->data[2][2]*this->data[3][3]
+ - this->data[2][3]*this->data[3][2];
+
+ t00 = v5*this->data[1][1] - v4*this->data[1][2] + v3*this->data[1][3];
+ t10 = -v5*this->data[1][0] + v2*this->data[1][2] - v1*this->data[1][3];
+ t20 = v4*this->data[1][0] - v2*this->data[1][1] + v0*this->data[1][3];
+ t30 = -v3*this->data[1][0] + v1*this->data[1][1] - v0*this->data[1][2];
+
+ return t00 * this->data[0][0]
+ + t10 * this->data[0][1]
+ + t20 * this->data[0][2]
+ + t30 * this->data[0][3];
+ }
+
+ /// \brief Return the inverse matrix.
+ /// This is a non-destructive operation.
+ /// \return Inverse of this matrix.
+ public: Matrix4<T> Inverse() const
+ {
+ T v0, v1, v2, v3, v4, v5, t00, t10, t20, t30;
+ Matrix4<T> r;
+
+ v0 = this->data[2][0]*this->data[3][1] -
+ this->data[2][1]*this->data[3][0];
+ v1 = this->data[2][0]*this->data[3][2] -
+ this->data[2][2]*this->data[3][0];
+ v2 = this->data[2][0]*this->data[3][3] -
+ this->data[2][3]*this->data[3][0];
+ v3 = this->data[2][1]*this->data[3][2] -
+ this->data[2][2]*this->data[3][1];
+ v4 = this->data[2][1]*this->data[3][3] -
+ this->data[2][3]*this->data[3][1];
+ v5 = this->data[2][2]*this->data[3][3] -
+ this->data[2][3]*this->data[3][2];
+
+ t00 = +(v5*this->data[1][1] -
+ v4*this->data[1][2] + v3*this->data[1][3]);
+ t10 = -(v5*this->data[1][0] -
+ v2*this->data[1][2] + v1*this->data[1][3]);
+ t20 = +(v4*this->data[1][0] -
+ v2*this->data[1][1] + v0*this->data[1][3]);
+ t30 = -(v3*this->data[1][0] -
+ v1*this->data[1][1] + v0*this->data[1][2]);
+
+ T invDet = 1 / (t00 * this->data[0][0] + t10 * this->data[0][1] +
+ t20 * this->data[0][2] + t30 * this->data[0][3]);
+
+ r(0, 0) = t00 * invDet;
+ r(1, 0) = t10 * invDet;
+ r(2, 0) = t20 * invDet;
+ r(3, 0) = t30 * invDet;
+
+ r(0, 1) = -(v5*this->data[0][1] -
+ v4*this->data[0][2] + v3*this->data[0][3]) * invDet;
+ r(1, 1) = +(v5*this->data[0][0] -
+ v2*this->data[0][2] + v1*this->data[0][3]) * invDet;
+ r(2, 1) = -(v4*this->data[0][0] -
+ v2*this->data[0][1] + v0*this->data[0][3]) * invDet;
+ r(3, 1) = +(v3*this->data[0][0] -
+ v1*this->data[0][1] + v0*this->data[0][2]) * invDet;
+
+ v0 = this->data[1][0]*this->data[3][1] -
+ this->data[1][1]*this->data[3][0];
+ v1 = this->data[1][0]*this->data[3][2] -
+ this->data[1][2]*this->data[3][0];
+ v2 = this->data[1][0]*this->data[3][3] -
+ this->data[1][3]*this->data[3][0];
+ v3 = this->data[1][1]*this->data[3][2] -
+ this->data[1][2]*this->data[3][1];
+ v4 = this->data[1][1]*this->data[3][3] -
+ this->data[1][3]*this->data[3][1];
+ v5 = this->data[1][2]*this->data[3][3] -
+ this->data[1][3]*this->data[3][2];
+
+ r(0, 2) = +(v5*this->data[0][1] -
+ v4*this->data[0][2] + v3*this->data[0][3]) * invDet;
+ r(1, 2) = -(v5*this->data[0][0] -
+ v2*this->data[0][2] + v1*this->data[0][3]) * invDet;
+ r(2, 2) = +(v4*this->data[0][0] -
+ v2*this->data[0][1] + v0*this->data[0][3]) * invDet;
+ r(3, 2) = -(v3*this->data[0][0] -
+ v1*this->data[0][1] + v0*this->data[0][2]) * invDet;
+
+ v0 = this->data[2][1]*this->data[1][0] -
+ this->data[2][0]*this->data[1][1];
+ v1 = this->data[2][2]*this->data[1][0] -
+ this->data[2][0]*this->data[1][2];
+ v2 = this->data[2][3]*this->data[1][0] -
+ this->data[2][0]*this->data[1][3];
+ v3 = this->data[2][2]*this->data[1][1] -
+ this->data[2][1]*this->data[1][2];
+ v4 = this->data[2][3]*this->data[1][1] -
+ this->data[2][1]*this->data[1][3];
+ v5 = this->data[2][3]*this->data[1][2] -
+ this->data[2][2]*this->data[1][3];
+
+ r(0, 3) = -(v5*this->data[0][1] -
+ v4*this->data[0][2] + v3*this->data[0][3]) * invDet;
+ r(1, 3) = +(v5*this->data[0][0] -
+ v2*this->data[0][2] + v1*this->data[0][3]) * invDet;
+ r(2, 3) = -(v4*this->data[0][0] -
+ v2*this->data[0][1] + v0*this->data[0][3]) * invDet;
+ r(3, 3) = +(v3*this->data[0][0] -
+ v1*this->data[0][1] + v0*this->data[0][2]) * invDet;
+
+ return r;
+ }
+
+ /// \brief Transpose this matrix.
+ public: void Transpose()
+ {
+ std::swap(this->data[0][1], this->data[1][0]);
+ std::swap(this->data[0][2], this->data[2][0]);
+ std::swap(this->data[0][3], this->data[3][0]);
+ std::swap(this->data[1][2], this->data[2][1]);
+ std::swap(this->data[1][3], this->data[3][1]);
+ std::swap(this->data[2][3], this->data[3][2]);
+ }
+
+ /// \brief Return the transpose of this matrix
+ /// \return Transpose of this matrix.
+ public: Matrix4<T> Transposed() const
+ {
+ return Matrix4<T>(
+ this->data[0][0], this->data[1][0], this->data[2][0], this->data[3][0],
+ this->data[0][1], this->data[1][1], this->data[2][1], this->data[3][1],
+ this->data[0][2], this->data[1][2], this->data[2][2], this->data[3][2],
+ this->data[0][3], this->data[1][3], this->data[2][3], this->data[3][3]);
+ }
+
+ /// \brief Equal operator. this = _mat
+ /// \param _mat Incoming matrix
+ /// \return itself
+ public: Matrix4<T> &operator=(const Matrix4<T> &_mat)
+ {
+ memcpy(this->data, _mat.data, sizeof(this->data[0][0])*16);
+ return *this;
+ }
+
+ /// \brief Equal operator for 3x3 matrix
+ /// \param _mat Incoming matrix
+ /// \return itself
+ public: const Matrix4<T> &operator=(const Matrix3<T> &_mat)
+ {
+ this->data[0][0] = _mat(0, 0);
+ this->data[0][1] = _mat(0, 1);
+ this->data[0][2] = _mat(0, 2);
+
+ this->data[1][0] = _mat(1, 0);
+ this->data[1][1] = _mat(1, 1);
+ this->data[1][2] = _mat(1, 2);
+
+ this->data[2][0] = _mat(2, 0);
+ this->data[2][1] = _mat(2, 1);
+ this->data[2][2] = _mat(2, 2);
+
+ return *this;
+ }
+
+ /// \brief Multiplication operator
+ /// \param _mat Incoming matrix
+ /// \return This matrix * _mat
+ public: Matrix4<T> operator*(const Matrix4<T> &_m2) const
+ {
+ return Matrix4<T>(
+ this->data[0][0] * _m2(0, 0) +
+ this->data[0][1] * _m2(1, 0) +
+ this->data[0][2] * _m2(2, 0) +
+ this->data[0][3] * _m2(3, 0),
+
+ this->data[0][0] * _m2(0, 1) +
+ this->data[0][1] * _m2(1, 1) +
+ this->data[0][2] * _m2(2, 1) +
+ this->data[0][3] * _m2(3, 1),
+
+ this->data[0][0] * _m2(0, 2) +
+ this->data[0][1] * _m2(1, 2) +
+ this->data[0][2] * _m2(2, 2) +
+ this->data[0][3] * _m2(3, 2),
+
+ this->data[0][0] * _m2(0, 3) +
+ this->data[0][1] * _m2(1, 3) +
+ this->data[0][2] * _m2(2, 3) +
+ this->data[0][3] * _m2(3, 3),
+
+ this->data[1][0] * _m2(0, 0) +
+ this->data[1][1] * _m2(1, 0) +
+ this->data[1][2] * _m2(2, 0) +
+ this->data[1][3] * _m2(3, 0),
+
+ this->data[1][0] * _m2(0, 1) +
+ this->data[1][1] * _m2(1, 1) +
+ this->data[1][2] * _m2(2, 1) +
+ this->data[1][3] * _m2(3, 1),
+
+ this->data[1][0] * _m2(0, 2) +
+ this->data[1][1] * _m2(1, 2) +
+ this->data[1][2] * _m2(2, 2) +
+ this->data[1][3] * _m2(3, 2),
+
+ this->data[1][0] * _m2(0, 3) +
+ this->data[1][1] * _m2(1, 3) +
+ this->data[1][2] * _m2(2, 3) +
+ this->data[1][3] * _m2(3, 3),
+
+ this->data[2][0] * _m2(0, 0) +
+ this->data[2][1] * _m2(1, 0) +
+ this->data[2][2] * _m2(2, 0) +
+ this->data[2][3] * _m2(3, 0),
+
+ this->data[2][0] * _m2(0, 1) +
+ this->data[2][1] * _m2(1, 1) +
+ this->data[2][2] * _m2(2, 1) +
+ this->data[2][3] * _m2(3, 1),
+
+ this->data[2][0] * _m2(0, 2) +
+ this->data[2][1] * _m2(1, 2) +
+ this->data[2][2] * _m2(2, 2) +
+ this->data[2][3] * _m2(3, 2),
+
+ this->data[2][0] * _m2(0, 3) +
+ this->data[2][1] * _m2(1, 3) +
+ this->data[2][2] * _m2(2, 3) +
+ this->data[2][3] * _m2(3, 3),
+
+ this->data[3][0] * _m2(0, 0) +
+ this->data[3][1] * _m2(1, 0) +
+ this->data[3][2] * _m2(2, 0) +
+ this->data[3][3] * _m2(3, 0),
+
+ this->data[3][0] * _m2(0, 1) +
+ this->data[3][1] * _m2(1, 1) +
+ this->data[3][2] * _m2(2, 1) +
+ this->data[3][3] * _m2(3, 1),
+
+ this->data[3][0] * _m2(0, 2) +
+ this->data[3][1] * _m2(1, 2) +
+ this->data[3][2] * _m2(2, 2) +
+ this->data[3][3] * _m2(3, 2),
+
+ this->data[3][0] * _m2(0, 3) +
+ this->data[3][1] * _m2(1, 3) +
+ this->data[3][2] * _m2(2, 3) +
+ this->data[3][3] * _m2(3, 3));
+ }
+
+ /// \brief Multiplication operator
+ /// \param _vec Vector3
+ /// \return Resulting vector from multiplication
+ public: Vector3<T> operator*(const Vector3<T> &_vec) const
+ {
+ return Vector3<T>(
+ this->data[0][0]*_vec.X() + this->data[0][1]*_vec.Y() +
+ this->data[0][2]*_vec.Z() + this->data[0][3],
+ this->data[1][0]*_vec.X() + this->data[1][1]*_vec.Y() +
+ this->data[1][2]*_vec.Z() + this->data[1][3],
+ this->data[2][0]*_vec.X() + this->data[2][1]*_vec.Y() +
+ this->data[2][2]*_vec.Z() + this->data[2][3]);
+ }
+
+ /// \brief Get the value at the specified row, column index
+ /// \param[in] _col The column index. Index values are clamped to a
+ /// range of [0, 3].
+ /// \param[in] _row the row index. Index values are clamped to a
+ /// range of [0, 3].
+ /// \return The value at the specified index
+ public: inline const T &operator()(const size_t _row,
+ const size_t _col) const
+ {
+ return this->data[clamp(_row, IGN_ZERO_SIZE_T, IGN_THREE_SIZE_T)][
+ clamp(_col, IGN_ZERO_SIZE_T, IGN_THREE_SIZE_T)];
+ }
+
+ /// \brief Get a mutable version the value at the specified row,
+ /// column index
+ /// \param[in] _col The column index. Index values are clamped to a
+ /// range of [0, 3].
+ /// \param[in] _row the row index. Index values are clamped to a
+ /// range of [0, 3].
+ /// \return The value at the specified index
+ public: inline T &operator()(const size_t _row, const size_t _col)
+ {
+ return this->data[clamp(_row, IGN_ZERO_SIZE_T, IGN_THREE_SIZE_T)]
+ [clamp(_col, IGN_ZERO_SIZE_T, IGN_THREE_SIZE_T)];
+ }
+
+ /// \brief Equality test with tolerance.
+ /// \param[in] _m the matrix to compare to
+ /// \param[in] _tol equality tolerance.
+ /// \return true if the elements of the matrices are equal within
+ /// the tolerence specified by _tol.
+ public: bool Equal(const Matrix4 &_m, const T &_tol) const
+ {
+ return equal<T>(this->data[0][0], _m(0, 0), _tol)
+ && equal<T>(this->data[0][1], _m(0, 1), _tol)
+ && equal<T>(this->data[0][2], _m(0, 2), _tol)
+ && equal<T>(this->data[0][3], _m(0, 3), _tol)
+ && equal<T>(this->data[1][0], _m(1, 0), _tol)
+ && equal<T>(this->data[1][1], _m(1, 1), _tol)
+ && equal<T>(this->data[1][2], _m(1, 2), _tol)
+ && equal<T>(this->data[1][3], _m(1, 3), _tol)
+ && equal<T>(this->data[2][0], _m(2, 0), _tol)
+ && equal<T>(this->data[2][1], _m(2, 1), _tol)
+ && equal<T>(this->data[2][2], _m(2, 2), _tol)
+ && equal<T>(this->data[2][3], _m(2, 3), _tol)
+ && equal<T>(this->data[3][0], _m(3, 0), _tol)
+ && equal<T>(this->data[3][1], _m(3, 1), _tol)
+ && equal<T>(this->data[3][2], _m(3, 2), _tol)
+ && equal<T>(this->data[3][3], _m(3, 3), _tol);
+ }
+
+ /// \brief Equality operator
+ /// \param[in] _m Matrix3 to test
+ /// \return true if the 2 matrices are equal (using the tolerance 1e-6),
+ /// false otherwise
+ public: bool operator==(const Matrix4<T> &_m) const
+ {
+ return this->Equal(_m, static_cast<T>(1e-6));
+ }
+
+ /// \brief Inequality test operator
+ /// \param[in] _m Matrix4<T> to test
+ /// \return True if not equal (using the default tolerance of 1e-6)
+ public: bool operator!=(const Matrix4<T> &_m) const
+ {
+ return !(*this == _m);
+ }
+
+ /// \brief Stream insertion operator
+ /// \param _out output stream
+ /// \param _m Matrix to output
+ /// \return the stream
+ public: friend std::ostream &operator<<(
+ std::ostream &_out, const ignition::math::Matrix4<T> &_m)
+ {
+ _out << precision(_m(0, 0), 6) << " "
+ << precision(_m(0, 1), 6) << " "
+ << precision(_m(0, 2), 6) << " "
+ << precision(_m(0, 3), 6) << " "
+ << precision(_m(1, 0), 6) << " "
+ << precision(_m(1, 1), 6) << " "
+ << precision(_m(1, 2), 6) << " "
+ << precision(_m(1, 3), 6) << " "
+ << precision(_m(2, 0), 6) << " "
+ << precision(_m(2, 1), 6) << " "
+ << precision(_m(2, 2), 6) << " "
+ << precision(_m(2, 3), 6) << " "
+ << precision(_m(3, 0), 6) << " "
+ << precision(_m(3, 1), 6) << " "
+ << precision(_m(3, 2), 6) << " "
+ << precision(_m(3, 3), 6);
+
+ return _out;
+ }
+
+ /// \brief Stream extraction operator
+ /// \param _in input stream
+ /// \param _pt Matrix4<T> to read values into
+ /// \return the stream
+ public: friend std::istream &operator>>(
+ std::istream &_in, ignition::math::Matrix4<T> &_m)
+ {
+ // Skip white spaces
+ _in.setf(std::ios_base::skipws);
+ T d[16];
+ _in >> d[0] >> d[1] >> d[2] >> d[3]
+ >> d[4] >> d[5] >> d[6] >> d[7]
+ >> d[8] >> d[9] >> d[10] >> d[11]
+ >> d[12] >> d[13] >> d[14] >> d[15];
+
+ _m.Set(d[0], d[1], d[2], d[3],
+ d[4], d[5], d[6], d[7],
+ d[8], d[9], d[10], d[11],
+ d[12], d[13], d[14], d[15]);
+ return _in;
+ }
+
+ /// \brief Get transform which translates to _eye and rotates the X axis
+ /// so it faces the _target. The rotation is such that Z axis is in the
+ /// _up direction, if possible. The coordinate system is right-handed,
+ /// \param[in] _eye Coordinate frame translation.
+ /// \param[in] _target Point which the X axis should face. If _target is
+ /// equal to _eye, the X axis won't be rotated.
+ /// \param[in] _up Direction in which the Z axis should point. If _up is
+ /// zero or parallel to X, it will be set to +Z.
+ /// \return Transformation matrix.
+ public: static Matrix4<T> LookAt(const Vector3<T> &_eye,
+ const Vector3<T> &_target, const Vector3<T> &_up = Vector3<T>::UnitZ)
+ {
+ // Most important constraint: direction to point X axis at
+ auto front = _target - _eye;
+
+ // Case when _eye == _target
+ if (front == Vector3<T>::Zero)
+ front = Vector3<T>::UnitX;
+ front.Normalize();
+
+ // Desired direction to point Z axis at
+ auto up = _up;
+
+ // Case when _up == Zero
+ if (up == Vector3<T>::Zero)
+ up = Vector3<T>::UnitZ;
+ else
+ up.Normalize();
+
+ // Case when _up is parallel to X
+ if (up.Cross(Vector3<T>::UnitX) == Vector3<T>::Zero)
+ up = Vector3<T>::UnitZ;
+
+ // Find direction to point Y axis at
+ auto left = up.Cross(front);
+
+ // Case when front is parallel to up
+ if (left == Vector3<T>::Zero)
+ left = Vector3<T>::UnitY;
+ else
+ left.Normalize();
+
+ // Fix up direction so it's perpendicular to XY
+ up = (front.Cross(left)).Normalize();
+
+ return Matrix4<T>(
+ front.X(), left.X(), up.X(), _eye.X(),
+ front.Y(), left.Y(), up.Y(), _eye.Y(),
+ front.Z(), left.Z(), up.Z(), _eye.Z(),
+ 0, 0, 0, 1);
+ }
+
+ /// \brief The 4x4 matrix
+ private: T data[4][4];
+ };
+
+ template<typename T>
+ const Matrix4<T> Matrix4<T>::Identity(
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+
+ template<typename T>
+ const Matrix4<T> Matrix4<T>::Zero(
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0);
+
+ typedef Matrix4<int> Matrix4i;
+ typedef Matrix4<double> Matrix4d;
+ typedef Matrix4<float> Matrix4f;
+ }
+}
+#endif
diff --git a/include/ignition/math/OrientedBox.hh b/include/ignition/math/OrientedBox.hh
new file mode 100644
index 0000000..083eb41
--- /dev/null
+++ b/include/ignition/math/OrientedBox.hh
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_ORIENTEDBOX_HH_
+#define IGNITION_MATH_ORIENTEDBOX_HH_
+
+#include <iostream>
+#include <ignition/math/Helpers.hh>
+#include <ignition/math/Matrix4.hh>
+#include <ignition/math/Pose3.hh>
+#include <ignition/math/Vector3.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \brief Mathematical representation of a box which can be arbitrarily
+ /// positioned and rotated.
+ template<typename T>
+ class OrientedBox
+ {
+ /// \brief Default constructor
+ public: OrientedBox() : size(Vector3<T>::Zero), pose(Pose3<T>::Zero)
+ {
+ }
+
+ /// \brief Constructor which takes size and pose.
+ /// \param[in] _size Box size, in its own coordinate frame. Its absolute
+ /// value will be taken, so the size is non-negative.
+ /// \param[in] _pose Box pose.
+ public: OrientedBox(const Vector3<T> &_size, const Pose3<T> &_pose)
+ : size(_size), pose(_pose)
+ {
+ // Enforce non-negative size
+ this->size = this->size.Abs();
+ }
+
+ /// \brief Constructor which takes only the size.
+ /// \param[in] _size Box size, in its own coordinate frame. Its absolute
+ /// value will be taken, so the size is non-negative.
+ public: explicit OrientedBox(const Vector3<T> &_size)
+ : size(_size), pose(Pose3<T>::Zero)
+ {
+ // Enforce non-negative size
+ this->size = this->size.Abs();
+ }
+
+ /// \brief Copy constructor.
+ /// \param[in] _b OrientedBox to copy.
+ public: OrientedBox(const OrientedBox<T> &_b)
+ : size(_b.size), pose(_b.pose)
+ {
+ }
+
+ /// \brief Destructor
+ public: virtual ~OrientedBox()
+ {
+ }
+
+ /// \brief Get the length along the x dimension
+ /// \return Value of the length in the x dimension
+ public: T XLength() const
+ {
+ return this->size.X();
+ }
+
+ /// \brief Get the length along the y dimension
+ /// \return Value of the length in the y dimension
+ public: T YLength() const
+ {
+ return this->size.Y();
+ }
+
+ /// \brief Get the length along the z dimension
+ /// \return Value of the length in the z dimension
+ public: T ZLength() const
+ {
+ return this->size.Z();
+ }
+
+ /// \brief Get the size of the box
+ /// \return Size of the box
+ public: const Vector3<T> &Size() const
+ {
+ return this->size;
+ }
+
+ /// \brief Get the box pose, which is the pose of its center.
+ /// \return The pose of the box.
+ public: const Pose3<T> &Pose() const
+ {
+ return this->pose;
+ }
+
+ /// \brief Set the box size.
+ /// \param[in] _size Box size, in its own coordinate frame. Its absolute
+ /// value will be taken, so the size is non-negative.
+ public: void Size(Vector3<T> &_size)
+ {
+ // Enforce non-negative size
+ this->size = _size.Abs();
+ }
+
+ /// \brief Set the box pose.
+ /// \param[in] _pose Box pose.
+ public: void Pose(Pose3<T> &_pose)
+ {
+ this->pose = _pose;
+ }
+
+ /// \brief Assignment operator. Set this box to the parameter
+ /// \param[in] _b OrientedBox to copy
+ /// \return The new box.
+ public: OrientedBox &operator=(const OrientedBox<T> &_b)
+ {
+ this->size = _b.size;
+ this->pose = _b.pose;
+ return *this;
+ }
+
+ /// \brief Equality test operator
+ /// \param[in] _b OrientedBox to test
+ /// \return True if equal
+ public: bool operator==(const OrientedBox<T> &_b) const
+ {
+ return this->size == _b.size && this->pose == _b.pose;
+ }
+
+ /// \brief Inequality test operator
+ /// \param[in] _b OrientedBox to test
+ /// \return True if not equal
+ public: bool operator!=(const OrientedBox<T> &_b) const
+ {
+ return this->size != _b.size || this->pose != _b.pose;
+ }
+
+ /// \brief Output operator
+ /// \param[in] _out Output stream
+ /// \param[in] _b OrientedBox to output to the stream
+ /// \return The stream
+ public: friend std::ostream &operator<<(std::ostream &_out,
+ const OrientedBox<T> &_b)
+ {
+ _out << "Size[" << _b.Size() << "] Pose[" << _b.Pose() << "]";
+ return _out;
+ }
+
+ /// \brief Check if a point lies inside the box.
+ /// \param[in] _p Point to check.
+ /// \return True if the point is inside the box.
+ public: bool Contains(const Vector3d &_p) const
+ {
+ // Move point to box frame
+ auto t = Matrix4<T>(this->pose).Inverse();
+ auto p = t *_p;
+
+ return p.X() >= -this->size.X()*0.5 && p.X() <= this->size.X()*0.5 &&
+ p.Y() >= -this->size.Y()*0.5 && p.Y() <= this->size.Y()*0.5 &&
+ p.Z() >= -this->size.Z()*0.5 && p.Z() <= this->size.Z()*0.5;
+ }
+
+ /// \brief The size of the box in its local frame.
+ private: Vector3<T> size;
+
+ /// \brief The pose of the center of the box.
+ private: Pose3<T> pose;
+ };
+
+ typedef OrientedBox<int> OrientedBoxi;
+ typedef OrientedBox<double> OrientedBoxd;
+ typedef OrientedBox<float> OrientedBoxf;
+ }
+}
+#endif
diff --git a/include/ignition/math/PID.hh b/include/ignition/math/PID.hh
new file mode 100644
index 0000000..0587a62
--- /dev/null
+++ b/include/ignition/math/PID.hh
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2016 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_PID_HH_
+#define IGNITION_MATH_PID_HH_
+
+#include <chrono>
+#include <ignition/math/Helpers.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class PID PID.hh ignition/math/PID.hh
+ /// \brief Generic PID controller class.
+ /// Generic proportional-integral-derivative controller class that
+ /// keeps track of PID-error states and control inputs given
+ /// the state of a system and a user specified target state.
+ /// It includes a user-adjustable command offset term (feed-forward).
+ class IGNITION_MATH_VISIBLE PID
+ {
+ /// \brief Constructor, zeros out Pid values when created and
+ /// initialize Pid-gains and integral term limits:[iMax:iMin]-[I1:I2].
+ ///
+ /// Disable command clamping by setting _cmdMin to a value larger
+ /// than _cmdMax. Command clamping is disabled by default.
+ ///
+ /// Disable integral clamping by setting _iMin to a value larger
+ /// than _iMax. Integral clamping is disabled by default.
+ ///
+ /// \param[in] _p The proportional gain.
+ /// \param[in] _i The integral gain.
+ /// \param[in] _d The derivative gain.
+ /// \param[in] _imax The integral upper limit.
+ /// \param[in] _imin The integral lower limit.
+ /// \param[in] _cmdMax Output max value.
+ /// \param[in] _cmdMin Output min value.
+ /// \param[in] _cmdOffset Command offset (feed-forward).
+ public: PID(const double _p = 0.0,
+ const double _i = 0.0,
+ const double _d = 0.0,
+ const double _imax = -1.0,
+ const double _imin = 0.0,
+ const double _cmdMax = -1.0,
+ const double _cmdMin = 0.0,
+ const double _cmdOffset = 0.0);
+
+ /// \brief Destructor
+ public: ~PID() = default;
+
+ /// \brief Initialize PID-gains and integral term
+ /// limits:[iMax:iMin]-[I1:I2].
+ ///
+ /// Disable command clamping by setting _cmdMin to a value larger
+ /// than _cmdMax. Command clamping is disabled by default.
+ ///
+ /// Disable integral clamping by setting _iMin to a value larger
+ /// than _iMax. Integral clamping is disabled by default.
+ ///
+ /// \param[in] _p The proportional gain.
+ /// \param[in] _i The integral gain.
+ /// \param[in] _d The derivative gain.
+ /// \param[in] _imax The integral upper limit.
+ /// \param[in] _imin The integral lower limit.
+ /// \param[in] _cmdMax Output max value.
+ /// \param[in] _cmdMin Output min value.
+ /// \param[in] _cmdOffset Command offset (feed-forward).
+ public: void Init(const double _p = 0.0,
+ const double _i = 0.0,
+ const double _d = 0.0,
+ const double _imax = -1.0,
+ const double _imin = 0.0,
+ const double _cmdMax = -1.0,
+ const double _cmdMin = 0.0,
+ const double _cmdOffset = 0.0);
+
+ /// \brief Set the proportional Gain.
+ /// \param[in] _p proportional gain value
+ public: void SetPGain(const double _p);
+
+ /// \brief Set the integral Gain.
+ /// \param[in] _i integral gain value
+ public: void SetIGain(const double _i);
+
+ /// \brief Set the derivtive Gain.
+ /// \param[in] _d derivative gain value
+ public: void SetDGain(const double _d);
+
+ /// \brief Set the integral upper limit.
+ /// \param[in] _i integral upper limit value
+ public: void SetIMax(const double _i);
+
+ /// \brief Set the integral lower limit.
+ /// \param[in] _i integral lower limit value
+ public: void SetIMin(const double _i);
+
+ /// \brief Set the maximum value for the command.
+ /// \param[in] _c The maximum value
+ public: void SetCmdMax(const double _c);
+
+ /// \brief Set the minimum value for the command.
+ /// \param[in] _c The minimum value
+ public: void SetCmdMin(const double _c);
+
+ /// \brief Set the offset value for the command,
+ /// which is added to the result of the PID controller.
+ /// \param[in] _c The offset value
+ public: void SetCmdOffset(const double _c);
+
+ /// \brief Get the proportional Gain.
+ /// \return The proportional gain value
+ public: double PGain() const;
+
+ /// \brief Get the integral Gain.
+ /// \return The integral gain value
+ public: double IGain() const;
+
+ /// \brief Get the derivative Gain.
+ /// \return The derivative gain value
+ public: double DGain() const;
+
+ /// \brief Get the integral upper limit.
+ /// \return The integral upper limit value
+ public: double IMax() const;
+
+ /// \brief Get the integral lower limit.
+ /// \return The integral lower limit value
+ public: double IMin() const;
+
+ /// \brief Get the maximum value for the command.
+ /// \return The maximum value
+ public: double CmdMax() const;
+
+ /// \brief Get the maximum value for the command.
+ /// \return The maximum value
+ public: double CmdMin() const;
+
+ /// \brief Get the offset value for the command.
+ /// \return The offset value
+ public: double CmdOffset() const;
+
+ /// \brief Update the Pid loop with nonuniform time step size.
+ /// \param[in] _error Error since last call (p_state - p_target).
+ /// \param[in] _dt Change in time since last update call.
+ /// Normally, this is called at every time step,
+ /// The return value is an updated command to be passed
+ /// to the object being controlled.
+ /// \return the command value
+ public: double Update(const double _error,
+ const std::chrono::duration<double> &_dt);
+
+ /// \brief Set current target command for this PID controller.
+ /// \param[in] _cmd New command
+ public: void SetCmd(const double _cmd);
+
+ /// \brief Return current command for this PID controller.
+ /// \return the command value
+ public: double Cmd() const;
+
+ /// \brief Return PID error terms for the controller.
+ /// \param[in] _pe The proportional error.
+ /// \param[in] _ie The integral of gain times error.
+ /// \param[in] _de The derivative error.
+ public: void Errors(double &_pe, double &_ie, double &_de) const;
+
+ /// \brief Assignment operator
+ /// \param[in] _p a reference to a PID to assign values from
+ /// \return reference to this instance
+ public: PID &operator=(const PID &_p);
+
+ /// \brief Reset the errors and command.
+ public: void Reset();
+
+ /// \brief Error at a previous step.
+ private: double pErrLast = 0.0;
+
+ /// \brief Current error.
+ private: double pErr = 0.0;
+
+ /// \brief Integral of gain times error.
+ private: double iErr = 0.0;
+
+ /// \brief Derivative error.
+ private: double dErr = 0.0;
+
+ /// \brief Gain for proportional control.
+ private: double pGain;
+
+ /// \brief Gain for integral control.
+ private: double iGain = 0.0;
+
+ /// \brief Gain for derivative control.
+ private: double dGain = 0.0;
+
+ /// \brief Maximum clamping value for integral term.
+ private: double iMax = -1.0;
+
+ /// \brief Minim clamping value for integral term.
+ private: double iMin = 0.0;
+
+ /// \brief Command value.
+ private: double cmd = 0.0;
+
+ /// \brief Max command clamping value.
+ private: double cmdMax = -1.0;
+
+ /// \brief Min command clamping value.
+ private: double cmdMin = 0.0;
+
+ /// \brief Command offset.
+ private: double cmdOffset = 0.0;
+ };
+ }
+}
+#endif
diff --git a/include/ignition/math/Plane.hh b/include/ignition/math/Plane.hh
new file mode 100644
index 0000000..27b8f59
--- /dev/null
+++ b/include/ignition/math/Plane.hh
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_PLANE_HH_
+#define IGNITION_MATH_PLANE_HH_
+
+#include <ignition/math/Box.hh>
+#include <ignition/math/Vector3.hh>
+#include <ignition/math/Vector2.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class Plane Plane.hh ignition/math/Plane.hh
+ /// \brief A plane and related functions.
+ template<typename T>
+ class Plane
+ {
+ /// \brief Enum used to indicate a side of the plane, no side, or both
+ /// sides for entities on the plane.
+ /// \sa Side
+ public: enum PlaneSide
+ {
+ /// \brief Negative side of the plane. This is the side that is
+ /// opposite the normal.
+ NEGATIVE_SIDE = 0,
+
+ /// \brief Positive side of the plane. This is the side that has the
+ /// normal vector.
+ POSITIVE_SIDE = 1,
+
+ /// \brief On the plane.
+ NO_SIDE = 2,
+
+ /// \brief On both sides of the plane.
+ BOTH_SIDE = 3
+ };
+
+ /// \brief Constructor
+ public: Plane()
+ : d(0.0)
+ {
+ }
+
+ /// \brief Constructor from a normal and a distance
+ /// \param[in] _normal The plane normal
+ /// \param[in] _offset Offset along the normal
+ public: Plane(const Vector3<T> &_normal, T _offset = 0.0)
+ : normal(_normal), d(_offset)
+ {
+ }
+
+ /// \brief Constructor
+ /// \param[in] _normal The plane normal
+ /// \param[in] _size Size of the plane
+ /// \param[in] _offset Offset along the normal
+ public: Plane(const Vector3<T> &_normal, const Vector2<T> &_size,
+ T _offset)
+ {
+ this->Set(_normal, _size, _offset);
+ }
+
+ /// \brief Destructor
+ public: virtual ~Plane() {}
+
+ /// \brief Set the plane
+ /// \param[in] _normal The plane normal
+ /// \param[in] _offset Offset along the normal
+ public: void Set(const Vector3<T> &_normal, T _offset)
+ {
+ this->normal = _normal;
+ this->d = _offset;
+ }
+
+ /// \brief Set the plane
+ /// \param[in] _normal The plane normal
+ /// \param[in] _size Size of the plane
+ /// \param[in] _offset Offset along the normal
+ public: void Set(const Vector3<T> &_normal, const Vector2<T> &_size,
+ T _offset)
+ {
+ this->normal = _normal;
+ this->size = _size;
+ this->d = _offset;
+ }
+
+ /// \brief The distance to the plane from the given point. The
+ /// distance can be negative, which indicates the point is on the
+ /// negative side of the plane.
+ /// \param[in] _point 3D point to calculate distance from.
+ /// \return Distance from the point to the plane.
+ /// \sa Side
+ public: T Distance(const Vector3<T> &_point) const
+ {
+ return this->normal.Dot(_point) - this->d;
+ }
+
+ /// \brief The side of the plane a point is on.
+ /// \param[in] _point The 3D point to check.
+ /// \return Plane::NEGATIVE_SIDE if the distance from the point to the
+ /// plane is negative, Plane::POSITIVE_SIDE if the distance from the
+ /// point to the plane is positive, or Plane::NO_SIDE if the
+ /// point is on the plane.
+ public: PlaneSide Side(const Vector3<T> &_point) const
+ {
+ T dist = this->Distance(_point);
+
+ if (dist < 0.0)
+ return NEGATIVE_SIDE;
+
+ if (dist > 0.0)
+ return POSITIVE_SIDE;
+
+ return NO_SIDE;
+ }
+
+ /// \brief The side of the plane a box is on.
+ /// \param[in] _box The 3D box to check.
+ /// \return Plane::NEGATIVE_SIDE if the distance from the box to the
+ /// plane is negative, Plane::POSITIVE_SIDE if the distance from the
+ /// box to the plane is positive, or Plane::BOTH_SIDE if the
+ /// box is on the plane.
+ public: PlaneSide Side(const math::Box &_box) const
+ {
+ double dist = this->Distance(_box.Center());
+ double maxAbsDist = this->normal.AbsDot(_box.Size()/2.0);
+
+ if (dist < -maxAbsDist)
+ return NEGATIVE_SIDE;
+
+ if (dist > maxAbsDist)
+ return POSITIVE_SIDE;
+
+ return BOTH_SIDE;
+ }
+
+ /// \brief Get distance to the plane give an origin and direction
+ /// \param[in] _origin the origin
+ /// \param[in] _dir a direction
+ /// \return the shortest distance
+ public: T Distance(const Vector3<T> &_origin,
+ const Vector3<T> &_dir) const
+ {
+ T denom = this->normal.Dot(_dir);
+
+ if (std::abs(denom) < 1e-3)
+ {
+ // parallel
+ return 0;
+ }
+ else
+ {
+ T nom = _origin.Dot(this->normal) - this->d;
+ T t = -(nom/denom);
+ return t;
+ }
+ }
+
+ /// \brief Get the plane size
+ public: inline const Vector2<T> &Size() const
+ {
+ return this->size;
+ }
+
+ /// \brief Get the plane size
+ public: inline Vector2<T> &Size()
+ {
+ return this->size;
+ }
+
+ /// \brief Get the plane offset
+ public: inline const Vector3<T> &Normal() const
+ {
+ return this->normal;
+ }
+
+ /// \brief Get the plane offset
+ public: inline Vector3<T> &Normal()
+ {
+ return this->normal;
+ }
+
+ /// \brief Get the plane offset
+ public: inline T Offset() const
+ {
+ return this->d;
+ }
+
+ /// \brief Equal operator
+ /// \param _p another plane
+ /// \return itself
+ public: Plane<T> &operator=(const Plane<T> &_p)
+ {
+ this->normal = _p.normal;
+ this->size = _p.size;
+ this->d = _p.d;
+
+ return *this;
+ }
+
+ /// \brief Plane normal
+ private: Vector3<T> normal;
+
+ /// \brief Plane size
+ private: Vector2<T> size;
+
+ /// \brief Plane offset
+ private: T d;
+ };
+
+ typedef Plane<int> Planei;
+ typedef Plane<double> Planed;
+ typedef Plane<float> Planef;
+ }
+}
+
+#endif
diff --git a/include/ignition/math/Pose3.hh b/include/ignition/math/Pose3.hh
new file mode 100644
index 0000000..31981ff
--- /dev/null
+++ b/include/ignition/math/Pose3.hh
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_POSE_HH_
+#define IGNITION_MATH_POSE_HH_
+
+#include <ignition/math/Quaternion.hh>
+#include <ignition/math/Vector3.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class Pose3 Pose3.hh ignition/math/Pose3.hh
+ /// \brief Encapsulates a position and rotation in three space
+ template<typename T>
+ class Pose3
+ {
+ /// \brief math::Pose3<T>(0, 0, 0, 0, 0, 0)
+ public: static const Pose3<T> Zero;
+
+ /// \brief Default constructors
+ public: Pose3() : p(0, 0, 0), q(1, 0, 0, 0)
+ {
+ }
+
+ /// \brief Constructor
+ /// \param[in] _pos A position
+ /// \param[in] _rot A rotation
+ public: Pose3(const Vector3<T> &_pos, const Quaternion<T> &_rot)
+ : p(_pos), q(_rot)
+ {
+ }
+
+ /// \brief Constructor
+ /// \param[in] _x x position in meters.
+ /// \param[in] _y y position in meters.
+ /// \param[in] _z z position in meters.
+ /// \param[in] _roll Roll (rotation about X-axis) in radians.
+ /// \param[in] _pitch Pitch (rotation about y-axis) in radians.
+ /// \param[in] _yaw Yaw (rotation about z-axis) in radians.
+ public: Pose3(T _x, T _y, T _z, T _roll, T _pitch, T _yaw)
+ : p(_x, _y, _z), q(_roll, _pitch, _yaw)
+ {
+ }
+
+ /// \brief Constructor
+ /// \param[in] _x x position in meters.
+ /// \param[in] _y y position in meters.
+ /// \param[in] _z z position in meters.
+ /// \param[in] _qw Quaternion w value.
+ /// \param[in] _qx Quaternion x value.
+ /// \param[in] _qy Quaternion y value.
+ /// \param[in] _qz Quaternion z value.
+ public: Pose3(T _x, T _y, T _z, T _qw, T _qx, T _qy, T _qz)
+ : p(_x, _y, _z), q(_qw, _qx, _qy, _qz)
+ {
+ }
+
+ /// \brief Copy constructor
+ /// \param[in] _pose Pose3<T> to copy
+ public: Pose3(const Pose3<T> &_pose)
+ : p(_pose.p), q(_pose.q)
+ {
+ }
+
+ /// \brief Destructor
+ public: virtual ~Pose3()
+ {
+ }
+
+ /// \brief Set the pose from a Vector3 and a Quaternion<T>
+ /// \param[in] _pos The position.
+ /// \param[in] _rot The rotation.
+ public: void Set(const Vector3<T> &_pos, const Quaternion<T> &_rot)
+ {
+ this->p = _pos;
+ this->q = _rot;
+ }
+
+ /// \brief Set the pose from pos and rpy vectors
+ /// \param[in] _pos The position.
+ /// \param[in] _rpy The rotation expressed as Euler angles.
+ public: void Set(const Vector3<T> &_pos, const Vector3<T> &_rpy)
+ {
+ this->p = _pos;
+ this->q.Euler(_rpy);
+ }
+
+ /// \brief Set the pose from a six tuple.
+ /// \param[in] _x x position in meters.
+ /// \param[in] _y y position in meters.
+ /// \param[in] _z z position in meters.
+ /// \param[in] _roll Roll (rotation about X-axis) in radians.
+ /// \param[in] _pitch Pitch (rotation about y-axis) in radians.
+ /// \param[in] _yaw Pitch (rotation about z-axis) in radians.
+ public: void Set(T _x, T _y, T _z, T _roll, T _pitch, T _yaw)
+ {
+ this->p.Set(_x, _y, _z);
+ this->q.Euler(math::Vector3<T>(_roll, _pitch, _yaw));
+ }
+
+ /// \brief See if a pose is finite (e.g., not nan)
+ public: bool IsFinite() const
+ {
+ return this->p.IsFinite() && this->q.IsFinite();
+ }
+
+ /// \brief Fix any nan values
+ public: inline void Correct()
+ {
+ this->p.Correct();
+ this->q.Correct();
+ }
+
+ /// \brief Get the inverse of this pose
+ /// \return the inverse pose
+ public: Pose3<T> Inverse() const
+ {
+ Quaternion<T> inv = this->q.Inverse();
+ return Pose3<T>(inv * (this->p*-1), inv);
+ }
+
+ /// \brief Addition operator
+ /// A is the transform from O to P specified in frame O
+ /// B is the transform from P to Q specified in frame P
+ /// then, B + A is the transform from O to Q specified in frame O
+ /// \param[in] _pose Pose3<T> to add to this pose
+ /// \return The resulting pose
+ public: Pose3<T> operator+(const Pose3<T> &_pose) const
+ {
+ Pose3<T> result;
+
+ result.p = this->CoordPositionAdd(_pose);
+ result.q = this->CoordRotationAdd(_pose.q);
+
+ return result;
+ }
+
+ /// \brief Add-Equals operator
+ /// \param[in] _pose Pose3<T> to add to this pose
+ /// \return The resulting pose
+ public: const Pose3<T> &operator+=(const Pose3<T> &_pose)
+ {
+ this->p = this->CoordPositionAdd(_pose);
+ this->q = this->CoordRotationAdd(_pose.q);
+
+ return *this;
+ }
+
+ /// \brief Negation operator
+ /// A is the transform from O to P in frame O
+ /// then -A is transform from P to O specified in frame P
+ /// \return The resulting pose
+ public: inline Pose3<T> operator-() const
+ {
+ return Pose3<T>() - *this;
+ }
+
+ /// \brief Subtraction operator
+ /// A is the transform from O to P in frame O
+ /// B is the transform from O to Q in frame O
+ /// B - A is the transform from P to Q in frame P
+ /// \param[in] _pose Pose3<T> to subtract from this one
+ /// \return The resulting pose
+ public: inline Pose3<T> operator-(const Pose3<T> &_pose) const
+ {
+ return Pose3<T>(this->CoordPositionSub(_pose),
+ this->CoordRotationSub(_pose.q));
+ }
+
+ /// \brief Subtraction operator
+ /// \param[in] _pose Pose3<T> to subtract from this one
+ /// \return The resulting pose
+ public: const Pose3<T> &operator-=(const Pose3<T> &_pose)
+ {
+ this->p = this->CoordPositionSub(_pose);
+ this->q = this->CoordRotationSub(_pose.q);
+
+ return *this;
+ }
+
+ /// \brief Equality operator
+ /// \param[in] _pose Pose3<T> for comparison
+ /// \return True if equal
+ public: bool operator==(const Pose3<T> &_pose) const
+ {
+ return this->p == _pose.p && this->q == _pose.q;
+ }
+
+ /// \brief Inequality operator
+ /// \param[in] _pose Pose3<T> for comparison
+ /// \return True if not equal
+ public: bool operator!=(const Pose3<T> &_pose) const
+ {
+ return this->p != _pose.p || this->q != _pose.q;
+ }
+
+ /// \brief Multiplication operator
+ /// \param[in] _pose the other pose
+ /// \return itself
+ public: Pose3<T> operator*(const Pose3<T> &_pose) const
+ {
+ return Pose3<T>(this->CoordPositionAdd(_pose), _pose.q * this->q);
+ }
+
+ /// \brief Equal operator
+ /// \param[in] _pose Pose3<T> to copy
+ public: Pose3<T> &operator=(const Pose3<T> &_pose)
+ {
+ this->p = _pose.p;
+ this->q = _pose.q;
+ return *this;
+ }
+
+ /// \brief Add one point to a vector: result = this + pos
+ /// \param[in] _pos Position to add to this pose
+ /// \return the resulting position
+ public: Vector3<T> CoordPositionAdd(const Vector3<T> &_pos) const
+ {
+ Quaternion<T> tmp(0.0, _pos.X(), _pos.Y(), _pos.Z());
+
+ // result = pose.q + pose.q * this->p * pose.q!
+ tmp = this->q * (tmp * this->q.Inverse());
+
+ return Vector3<T>(this->p.X() + tmp.X(),
+ this->p.Y() + tmp.Y(),
+ this->p.Z() + tmp.Z());
+ }
+
+ /// \brief Add one point to another: result = this + pose
+ /// \param[in] _pose The Pose3<T> to add
+ /// \return The resulting position
+ public: Vector3<T> CoordPositionAdd(const Pose3<T> &_pose) const
+ {
+ Quaternion<T> tmp(static_cast<T>(0),
+ this->p.X(), this->p.Y(), this->p.Z());
+
+ // result = _pose.q + _pose.q * this->p * _pose.q!
+ tmp = _pose.q * (tmp * _pose.q.Inverse());
+
+ return Vector3<T>(_pose.p.X() + tmp.X(),
+ _pose.p.Y() + tmp.Y(),
+ _pose.p.Z() + tmp.Z());
+ }
+
+ /// \brief Subtract one position from another: result = this - pose
+ /// \param[in] _pose Pose3<T> to subtract
+ /// \return The resulting position
+ public: inline Vector3<T> CoordPositionSub(const Pose3<T> &_pose) const
+ {
+ Quaternion<T> tmp(0,
+ this->p.X() - _pose.p.X(),
+ this->p.Y() - _pose.p.Y(),
+ this->p.Z() - _pose.p.Z());
+
+ tmp = _pose.q.Inverse() * (tmp * _pose.q);
+ return Vector3<T>(tmp.X(), tmp.Y(), tmp.Z());
+ }
+
+ /// \brief Add one rotation to another: result = this->q + rot
+ /// \param[in] _rot Rotation to add
+ /// \return The resulting rotation
+ public: Quaternion<T> CoordRotationAdd(const Quaternion<T> &_rot) const
+ {
+ return Quaternion<T>(_rot * this->q);
+ }
+
+ /// \brief Subtract one rotation from another: result = this->q - rot
+ /// \param[in] _rot The rotation to subtract
+ /// \return The resulting rotation
+ public: inline Quaternion<T> CoordRotationSub(
+ const Quaternion<T> &_rot) const
+ {
+ Quaternion<T> result(_rot.Inverse() * this->q);
+ result.Normalize();
+ return result;
+ }
+
+ /// \brief Find the inverse of a pose; i.e., if b = this + a, given b and
+ /// this, find a
+ /// \param[in] _b the other pose
+ public: Pose3<T> CoordPoseSolve(const Pose3<T> &_b) const
+ {
+ Quaternion<T> qt;
+ Pose3<T> a;
+
+ a.q = this->q.Inverse() * _b.q;
+ qt = a.q * Quaternion<T>(0, this->p.X(), this->p.Y(), this->p.Z());
+ qt = qt * a.q.Inverse();
+ a.p = _b.p - Vector3<T>(qt.X(), qt.Y(), qt.Z());
+
+ return a;
+ }
+
+ /// \brief Reset the pose
+ public: void Reset()
+ {
+ // set the position to zero
+ this->p.Set();
+ this->q = Quaterniond::Identity;
+ }
+
+ /// \brief Rotate vector part of a pose about the origin
+ /// \param[in] _rot rotation
+ /// \return the rotated pose
+ public: Pose3<T> RotatePositionAboutOrigin(const Quaternion<T> &_q) const
+ {
+ Pose3<T> a = *this;
+ a.p.X((1.0 - 2.0*_q.Y()*_q.Y() - 2.0*_q.Z()*_q.Z()) * this->p.X()
+ +(2.0*(_q.X()*_q.Y()+_q.W()*_q.Z())) * this->p.Y()
+ +(2.0*(_q.X()*_q.Z()-_q.W()*_q.Y())) * this->p.Z());
+ a.p.Y((2.0*(_q.X()*_q.Y()-_q.W()*_q.Z())) * this->p.X()
+ +(1.0 - 2.0*_q.X()*_q.X() - 2.0*_q.Z()*_q.Z()) * this->p.Y()
+ +(2.0*(_q.Y()*_q.Z()+_q.W()*_q.X())) * this->p.Z());
+ a.p.Z((2.0*(_q.X()*_q.Z()+_q.W()*_q.Y())) * this->p.X()
+ +(2.0*(_q.Y()*_q.Z()-_q.W()*_q.X())) * this->p.Y()
+ +(1.0 - 2.0*_q.X()*_q.X() - 2.0*_q.Y()*_q.Y()) * this->p.Z());
+ return a;
+ }
+
+ /// \brief Round all values to _precision decimal places
+ /// \param[in] _precision
+ public: void Round(int _precision)
+ {
+ this->q.Round(_precision);
+ this->p.Round(_precision);
+ }
+
+ /// \brief Get the position.
+ /// \return Origin of the pose.
+ public: inline const Vector3<T> &Pos() const
+ {
+ return this->p;
+ }
+
+ /// \brief Get a mutable reference to the position.
+ /// \return Origin of the pose.
+ public: inline Vector3<T> &Pos()
+ {
+ return this->p;
+ }
+
+ /// \brief Get the rotation.
+ /// \return Quaternion representation of the rotation.
+ public: inline const Quaternion<T> &Rot() const
+ {
+ return this->q;
+ }
+
+ /// \brief Get a mutuable reference to the rotation.
+ /// \return Quaternion representation of the rotation.
+ public: inline Quaternion<T> &Rot()
+ {
+ return this->q;
+ }
+
+ /// \brief Stream insertion operator
+ /// \param[in] _out output stream
+ /// \param[in] _pose pose to output
+ /// \return the stream
+ public: friend std::ostream &operator<<(
+ std::ostream &_out, const ignition::math::Pose3<T> &_pose)
+ {
+ _out << _pose.Pos() << " " << _pose.Rot();
+ return _out;
+ }
+
+ /// \brief Stream extraction operator
+ /// \param[in] _in the input stream
+ /// \param[in] _pose the pose
+ /// \return the stream
+ public: friend std::istream &operator>>(
+ std::istream &_in, ignition::math::Pose3<T> &_pose)
+ {
+ // Skip white spaces
+ _in.setf(std::ios_base::skipws);
+ Vector3<T> pos;
+ Quaternion<T> rot;
+ _in >> pos >> rot;
+ _pose.Set(pos, rot);
+ return _in;
+ }
+
+ /// \brief The position
+ private: Vector3<T> p;
+
+ /// \brief The rotation
+ private: Quaternion<T> q;
+ };
+ template<typename T> const Pose3<T> Pose3<T>::Zero(0, 0, 0, 0, 0, 0);
+
+ typedef Pose3<int> Pose3i;
+ typedef Pose3<double> Pose3d;
+ typedef Pose3<float> Pose3f;
+ }
+}
+#endif
diff --git a/include/ignition/math/Quaternion.hh b/include/ignition/math/Quaternion.hh
new file mode 100644
index 0000000..a810561
--- /dev/null
+++ b/include/ignition/math/Quaternion.hh
@@ -0,0 +1,1083 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_QUATERNION_HH_
+#define IGNITION_MATH_QUATERNION_HH_
+
+#include <ignition/math/Helpers.hh>
+#include <ignition/math/Angle.hh>
+#include <ignition/math/Vector3.hh>
+#include <ignition/math/Matrix3.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ template <typename T> class Matrix3;
+
+ /// \class Quaternion Quaternion.hh ignition/math/Quaternion.hh
+ /// \brief A quaternion class
+ template<typename T>
+ class Quaternion
+ {
+ /// \brief math::Quaternion(1, 0, 0, 0)
+ public: static const Quaternion Identity;
+
+ /// \brief math::Quaternion(0, 0, 0, 0)
+ public: static const Quaternion Zero;
+
+ /// \brief Default Constructor
+ public: Quaternion()
+ : qw(1), qx(0), qy(0), qz(0)
+ {
+ // quaternion not normalized, because that breaks
+ // Pose::CoordPositionAdd(...)
+ }
+
+ /// \brief Constructor
+ /// \param[in] _w W param
+ /// \param[in] _x X param
+ /// \param[in] _y Y param
+ /// \param[in] _z Z param
+ public: Quaternion(const T &_w, const T &_x, const T &_y, const T &_z)
+ : qw(_w), qx(_x), qy(_y), qz(_z)
+ {}
+
+ /// \brief Constructor from Euler angles in radians
+ /// \param[in] _roll roll
+ /// \param[in] _pitch pitch
+ /// \param[in] _yaw yaw
+ public: Quaternion(const T &_roll, const T &_pitch, const T &_yaw)
+ {
+ this->Euler(Vector3<T>(_roll, _pitch, _yaw));
+ }
+
+ /// \brief Constructor from axis angle
+ /// \param[in] _axis the rotation axis
+ /// \param[in] _angle the rotation angle in radians
+ public: Quaternion(const Vector3<T> &_axis, const T &_angle)
+ {
+ this->Axis(_axis, _angle);
+ }
+
+ /// \brief Constructor
+ /// \param[in] _rpy euler angles
+ public: explicit Quaternion(const Vector3<T> &_rpy)
+ {
+ this->Euler(_rpy);
+ }
+
+ /// \brief Construct from rotation matrix.
+ /// \param[in] _mat rotation matrix (must be orthogonal, the function
+ /// doesn't check it)
+ public: explicit Quaternion(const Matrix3<T> &_mat)
+ {
+ this->Matrix(_mat);
+ }
+
+ /// \brief Copy constructor
+ /// \param[in] _qt Quaternion<T> to copy
+ public: Quaternion(const Quaternion<T> &_qt)
+ {
+ this->qw = _qt.qw;
+ this->qx = _qt.qx;
+ this->qy = _qt.qy;
+ this->qz = _qt.qz;
+ }
+
+ /// \brief Destructor
+ public: ~Quaternion() {}
+
+ /// \brief Equal operator
+ /// \param[in] _qt Quaternion<T> to copy
+ public: Quaternion<T> &operator=(const Quaternion<T> &_qt)
+ {
+ this->qw = _qt.qw;
+ this->qx = _qt.qx;
+ this->qy = _qt.qy;
+ this->qz = _qt.qz;
+
+ return *this;
+ }
+
+ /// \brief Invert the quaternion
+ public: void Invert()
+ {
+ this->Normalize();
+ // this->qw = this->qw;
+ this->qx = -this->qx;
+ this->qy = -this->qy;
+ this->qz = -this->qz;
+ }
+
+ /// \brief Get the inverse of this quaternion
+ /// \return Inverse quaternion
+ public: inline Quaternion<T> Inverse() const
+ {
+ T s = 0;
+ Quaternion<T> q(this->qw, this->qx, this->qy, this->qz);
+
+ // use s to test if quaternion is valid
+ s = q.qw * q.qw + q.qx * q.qx + q.qy * q.qy + q.qz * q.qz;
+
+ if (equal<T>(s, static_cast<T>(0)))
+ {
+ q.qw = 1.0;
+ q.qx = 0.0;
+ q.qy = 0.0;
+ q.qz = 0.0;
+ }
+ else
+ {
+ // deal with non-normalized quaternion
+ // div by s so q * qinv = identity
+ q.qw = q.qw / s;
+ q.qx = -q.qx / s;
+ q.qy = -q.qy / s;
+ q.qz = -q.qz / s;
+ }
+ return q;
+ }
+
+ /// \brief Return the logarithm
+ /// \return the log
+ public: Quaternion<T> Log() const
+ {
+ // If q = cos(A)+sin(A)*(x*i+y*j+z*k) where (x, y, z) is unit length,
+ // then log(q) = A*(x*i+y*j+z*k). If sin(A) is near zero, use log(q) =
+ // sin(A)*(x*i+y*j+z*k) since sin(A)/A has limit 1.
+
+ Quaternion<T> result;
+ result.qw = 0.0;
+
+ if (std::abs(this->qw) < 1.0)
+ {
+ T fAngle = acos(this->qw);
+ T fSin = sin(fAngle);
+ if (std::abs(fSin) >= 1e-3)
+ {
+ T fCoeff = fAngle/fSin;
+ result.qx = fCoeff*this->qx;
+ result.qy = fCoeff*this->qy;
+ result.qz = fCoeff*this->qz;
+ return result;
+ }
+ }
+
+ result.qx = this->qx;
+ result.qy = this->qy;
+ result.qz = this->qz;
+
+ return result;
+ }
+
+ /// \brief Return the exponent
+ /// \return the exp
+ public: Quaternion<T> Exp() const
+ {
+ // If q = A*(x*i+y*j+z*k) where (x, y, z) is unit length, then
+ // exp(q) = cos(A)+sin(A)*(x*i+y*j+z*k). If sin(A) is near zero,
+ // use exp(q) = cos(A)+A*(x*i+y*j+z*k) since A/sin(A) has limit 1.
+
+ T fAngle = sqrt(this->qx*this->qx+
+ this->qy*this->qy+this->qz*this->qz);
+ T fSin = sin(fAngle);
+
+ Quaternion<T> result;
+ result.qw = cos(fAngle);
+
+ if (std::abs(fSin) >= 1e-3)
+ {
+ T fCoeff = fSin/fAngle;
+ result.qx = fCoeff*this->qx;
+ result.qy = fCoeff*this->qy;
+ result.qz = fCoeff*this->qz;
+ }
+ else
+ {
+ result.qx = this->qx;
+ result.qy = this->qy;
+ result.qz = this->qz;
+ }
+
+ return result;
+ }
+
+ /// \brief Normalize the quaternion
+ public: void Normalize()
+ {
+ T s = 0;
+
+ s = T(sqrt(this->qw * this->qw + this->qx * this->qx +
+ this->qy * this->qy + this->qz * this->qz));
+
+ if (equal<T>(s, static_cast<T>(0)))
+ {
+ this->qw = T(1.0);
+ this->qx = T(0.0);
+ this->qy = T(0.0);
+ this->qz = T(0.0);
+ }
+ else
+ {
+ this->qw /= s;
+ this->qx /= s;
+ this->qy /= s;
+ this->qz /= s;
+ }
+ }
+
+ /// \brief Set the quaternion from an axis and angle
+ /// \param[in] _x X axis
+ /// \param[in] _y Y axis
+ /// \param[in] _z Z axis
+ /// \param[in] _a Angle in radians
+ public: void Axis(T _ax, T _ay, T _az, T _aa)
+ {
+ T l;
+
+ l = _ax * _ax + _ay * _ay + _az * _az;
+
+ if (equal<T>(l, static_cast<T>(0)))
+ {
+ this->qw = 1;
+ this->qx = 0;
+ this->qy = 0;
+ this->qz = 0;
+ }
+ else
+ {
+ _aa *= 0.5;
+ l = sin(_aa) / sqrt(l);
+ this->qw = cos(_aa);
+ this->qx = _ax * l;
+ this->qy = _ay * l;
+ this->qz = _az * l;
+ }
+
+ this->Normalize();
+ }
+
+ /// \brief Set the quaternion from an axis and angle
+ /// \param[in] _axis Axis
+ /// \param[in] _a Angle in radians
+ public: void Axis(const Vector3<T> &_axis, T _a)
+ {
+ this->Axis(_axis.X(), _axis.Y(), _axis.Z(), _a);
+ }
+
+ /// \brief Set this quaternion from 4 floating numbers
+ /// \param[in] _u u
+ /// \param[in] _x x
+ /// \param[in] _y y
+ /// \param[in] _z z
+ public: void Set(T _w, T _x, T _y, T _z)
+ {
+ this->qw = _w;
+ this->qx = _x;
+ this->qy = _y;
+ this->qz = _z;
+ }
+
+ /// \brief Set the quaternion from Euler angles. The order of operations
+ /// is roll, pitch, yaw around a fixed body frame axis
+ /// (the original frame of the object before rotation is applied).
+ /// Roll is a rotation about x, pitch is about y, yaw is about z.
+ /// \param[in] _vec Euler angle
+ public: void Euler(const Vector3<T> &_vec)
+ {
+ this->Euler(_vec.X(), _vec.Y(), _vec.Z());
+ }
+
+ /// \brief Set the quaternion from Euler angles.
+ /// \param[in] _roll Roll angle (radians).
+ /// \param[in] _pitch Pitch angle (radians).
+ /// \param[in] _yaw Yaw angle (radians).
+ public: void Euler(T _roll, T _pitch, T _yaw)
+ {
+ T phi, the, psi;
+
+ phi = _roll / T(2.0);
+ the = _pitch / T(2.0);
+ psi = _yaw / T(2.0);
+
+ this->qw = T(cos(phi) * cos(the) * cos(psi) +
+ sin(phi) * sin(the) * sin(psi));
+ this->qx = T(sin(phi) * cos(the) * cos(psi) -
+ cos(phi) * sin(the) * sin(psi));
+ this->qy = T(cos(phi) * sin(the) * cos(psi) +
+ sin(phi) * cos(the) * sin(psi));
+ this->qz = T(cos(phi) * cos(the) * sin(psi) -
+ sin(phi) * sin(the) * cos(psi));
+
+ this->Normalize();
+ }
+
+ /// \brief Return the rotation in Euler angles
+ /// \return This quaternion as an Euler vector
+ public: Vector3<T> Euler() const
+ {
+ Vector3<T> vec;
+
+ T tol = static_cast<T>(1e-15);
+
+ Quaternion<T> copy = *this;
+ T squ;
+ T sqx;
+ T sqy;
+ T sqz;
+
+ copy.Normalize();
+
+ squ = copy.qw * copy.qw;
+ sqx = copy.qx * copy.qx;
+ sqy = copy.qy * copy.qy;
+ sqz = copy.qz * copy.qz;
+
+ // Pitch
+ T sarg = -2 * (copy.qx*copy.qz - copy.qw * copy.qy);
+ if (sarg <= T(-1.0))
+ {
+ vec.Y(T(-0.5*IGN_PI));
+ }
+ else if (sarg >= T(1.0))
+ {
+ vec.Y(T(0.5*IGN_PI));
+ }
+ else
+ {
+ vec.Y(T(asin(sarg)));
+ }
+
+ // If the pitch angle is PI/2 or -PI/2, we can only compute
+ // the sum roll + yaw. However, any combination that gives
+ // the right sum will produce the correct orientation, so we
+ // set yaw = 0 and compute roll.
+ // pitch angle is PI/2
+ if (std::abs(sarg - 1) < tol)
+ {
+ vec.Z(0);
+ vec.X(T(atan2(2 * (copy.qx*copy.qy - copy.qz*copy.qw),
+ squ - sqx + sqy - sqz)));
+ }
+ // pitch angle is -PI/2
+ else if (std::abs(sarg + 1) < tol)
+ {
+ vec.Z(0);
+ vec.X(T(atan2(-2 * (copy.qx*copy.qy - copy.qz*copy.qw),
+ squ - sqx + sqy - sqz)));
+ }
+ else
+ {
+ // Roll
+ vec.X(T(atan2(2 * (copy.qy*copy.qz + copy.qw*copy.qx),
+ squ - sqx - sqy + sqz)));
+
+ // Yaw
+ vec.Z(T(atan2(2 * (copy.qx*copy.qy + copy.qw*copy.qz),
+ squ + sqx - sqy - sqz)));
+ }
+
+ return vec;
+ }
+
+ /// \brief Convert euler angles to quatern.
+ /// \param[in] _vec The vector of angles to convert.
+ /// \return The converted quaternion.
+ public: static Quaternion<T> EulerToQuaternion(const Vector3<T> &_vec)
+ {
+ Quaternion<T> result;
+ result.Euler(_vec);
+ return result;
+ }
+
+ /// \brief Convert euler angles to quatern.
+ /// \param[in] _x rotation along x
+ /// \param[in] _y rotation along y
+ /// \param[in] _z rotation along z
+ /// \return The converted quaternion.
+ public: static Quaternion<T> EulerToQuaternion(T _x, T _y, T _z)
+ {
+ return EulerToQuaternion(Vector3<T>(_x, _y, _z));
+ }
+
+ /// \brief Get the Euler roll angle in radians
+ /// \return the roll component
+ public: T Roll() const
+ {
+ return this->Euler().X();
+ }
+
+ /// \brief Get the Euler pitch angle in radians
+ /// \return the pitch component
+ public: T Pitch() const
+ {
+ return this->Euler().Y();
+ }
+
+ /// \brief Get the Euler yaw angle in radians
+ /// \return the yaw component
+ public: T Yaw() const
+ {
+ return this->Euler().Z();
+ }
+
+ /// \brief Return rotation as axis and angle
+ /// \param[in] _axis rotation axis
+ /// \param[in] _angle ccw angle in radians
+ public: void ToAxis(Vector3<T> &_axis, T &_angle) const
+ {
+ T len = this->qx*this->qx + this->qy*this->qy + this->qz*this->qz;
+ if (equal<T>(len, static_cast<T>(0)))
+ {
+ _angle = 0.0;
+ _axis.Set(1, 0, 0);
+ }
+ else
+ {
+ _angle = 2.0 * acos(this->qw);
+ T invLen = 1.0 / sqrt(len);
+ _axis.Set(this->qx*invLen, this->qy*invLen, this->qz*invLen);
+ }
+ }
+
+ /// \brief Set from a rotation matrix.
+ /// \param[in] _mat rotation matrix (must be orthogonal, the function
+ /// doesn't check it)
+ ///
+ /// Implementation inspired by
+ /// http://www.euclideanspace.com/maths/geometry/rotations/
+ /// conversions/matrixToQuaternion/
+ void Matrix(const Matrix3<T> &_mat)
+ {
+ const T trace = _mat(0, 0) + _mat(1, 1) + _mat(2, 2);
+ if (trace > 0.0000001)
+ {
+ qw = sqrt(1 + trace) / 2;
+ const T s = 1.0 / (4 * qw);
+ qx = (_mat(2, 1) - _mat(1, 2)) * s;
+ qy = (_mat(0, 2) - _mat(2, 0)) * s;
+ qz = (_mat(1, 0) - _mat(0, 1)) * s;
+ }
+ else if (_mat(0, 0) > _mat(1, 1) && _mat(0, 0) > _mat(2, 2))
+ {
+ qx = sqrt(1.0 + _mat(0, 0) - _mat(1, 1) - _mat(2, 2)) / 2;
+ const T s = 1.0 / (4 * qx);
+ qw = (_mat(2, 1) - _mat(1, 2)) * s;
+ qy = (_mat(1, 0) + _mat(0, 1)) * s;
+ qz = (_mat(0, 2) + _mat(2, 0)) * s;
+ }
+ else if (_mat(1, 1) > _mat(2, 2))
+ {
+ qy = sqrt(1.0 - _mat(0, 0) + _mat(1, 1) - _mat(2, 2)) / 2;
+ const T s = 1.0 / (4 * qy);
+ qw = (_mat(0, 2) - _mat(2, 0)) * s;
+ qx = (_mat(0, 1) + _mat(1, 0)) * s;
+ qz = (_mat(1, 2) + _mat(2, 1)) * s;
+ }
+ else
+ {
+ qz = sqrt(1.0 - _mat(0, 0) - _mat(1, 1) + _mat(2, 2)) / 2;
+ const T s = 1.0 / (4 * qz);
+ qw = (_mat(1, 0) - _mat(0, 1)) * s;
+ qx = (_mat(0, 2) + _mat(2, 0)) * s;
+ qy = (_mat(1, 2) + _mat(2, 1)) * s;
+ }
+ }
+
+ /// \brief Set this quaternion to represent rotation from
+ /// vector _v1 to vector _v2, so that
+ /// _v2.Normalize() == this * _v1.Normalize() holds.
+ ///
+ /// \param[in] _v1 The first vector
+ /// \param[in] _v2 The second vector
+ ///
+ /// Implementation inspired by
+ /// http://stackoverflow.com/a/11741520/1076564
+ public: void From2Axes(const Vector3<T> &_v1, const Vector3<T> &_v2)
+ {
+ // generally, we utilize the fact that a quat (w, x, y, z) represents
+ // rotation of angle 2*w about axis (x, y, z)
+ //
+ // so we want to take get a vector half-way between no rotation and the
+ // double rotation, which is
+ // [ (1, (0, 0, 0)) + (_v1 dot _v2, _v1 x _v2) ] / 2
+ // if _v1 and _v2 are unit quaternions
+ //
+ // since we normalize the result anyway, we can omit the division,
+ // getting the result:
+ // [ (1, (0, 0, 0)) + (_v1 dot _v2, _v1 x _v2) ].Normalized()
+ //
+ // if _v1 and _v2 are not normalized, the magnitude (1 + _v1 dot _v2)
+ // is multiplied by k = norm(_v1)*norm(_v2)
+
+ const T kCosTheta = _v1.Dot(_v2);
+ const T k = sqrt(_v1.SquaredLength() * _v2.SquaredLength());
+
+ if (fabs(kCosTheta/k + 1) < 1e-6)
+ {
+ // the vectors are opposite
+ // any vector orthogonal to _v1
+ Vector3<T> other;
+ {
+ const Vector3<T> _v1Abs(_v1.Abs());
+ if (_v1Abs.X() < _v1Abs.Y())
+ {
+ if (_v1Abs.X() < _v1Abs.Z())
+ {
+ other.Set(1, 0, 0);
+ }
+ else
+ {
+ other.Set(0, 0, 1);
+ }
+ }
+ else
+ {
+ if (_v1Abs.Y() < _v1Abs.Z())
+ {
+ other.Set(0, 1, 0);
+ }
+ else
+ {
+ other.Set(0, 0, 1);
+ }
+ }
+ }
+
+ const Vector3<T> axis(_v1.Cross(other).Normalize());
+
+ qw = 0;
+ qx = axis.X();
+ qy = axis.Y();
+ qz = axis.Z();
+ }
+ else
+ {
+ // the vectors are in general position
+ const Vector3<T> axis(_v1.Cross(_v2));
+ qw = kCosTheta + k;
+ qx = axis.X();
+ qy = axis.Y();
+ qz = axis.Z();
+ this->Normalize();
+ }
+ }
+
+ /// \brief Scale a Quaternion<T>ion
+ /// \param[in] _scale Amount to scale this rotation
+ public: void Scale(T _scale)
+ {
+ Quaternion<T> b;
+ Vector3<T> axis;
+ T angle;
+
+ // Convert to axis-and-angle
+ this->ToAxis(axis, angle);
+ angle *= _scale;
+
+ this->Axis(axis.X(), axis.Y(), axis.Z(), angle);
+ }
+
+ /// \brief Addition operator
+ /// \param[in] _qt quaternion for addition
+ /// \return this quaternion + _qt
+ public: Quaternion<T> operator+(const Quaternion<T> &_qt) const
+ {
+ Quaternion<T> result(this->qw + _qt.qw, this->qx + _qt.qx,
+ this->qy + _qt.qy, this->qz + _qt.qz);
+ return result;
+ }
+
+ /// \brief Addition operator
+ /// \param[in] _qt quaternion for addition
+ /// \return this quaternion + qt
+ public: Quaternion<T> operator+=(const Quaternion<T> &_qt)
+ {
+ *this = *this + _qt;
+
+ return *this;
+ }
+
+ /// \brief Subtraction operator
+ /// \param[in] _qt quaternion to subtract
+ /// \return this quaternion - _qt
+ public: Quaternion<T> operator-(const Quaternion<T> &_qt) const
+ {
+ Quaternion<T> result(this->qw - _qt.qw, this->qx - _qt.qx,
+ this->qy - _qt.qy, this->qz - _qt.qz);
+ return result;
+ }
+
+ /// \brief Subtraction operator
+ /// \param[in] _qt Quaternion<T> for subtraction
+ /// \return This quaternion - qt
+ public: Quaternion<T> operator-=(const Quaternion<T> &_qt)
+ {
+ *this = *this - _qt;
+ return *this;
+ }
+
+ /// \brief Multiplication operator
+ /// \param[in] _q Quaternion<T> for multiplication
+ /// \return This quaternion multiplied by the parameter
+ public: inline Quaternion<T> operator*(const Quaternion<T> &_q) const
+ {
+ return Quaternion<T>(
+ this->qw*_q.qw-this->qx*_q.qx-this->qy*_q.qy-this->qz*_q.qz,
+ this->qw*_q.qx+this->qx*_q.qw+this->qy*_q.qz-this->qz*_q.qy,
+ this->qw*_q.qy-this->qx*_q.qz+this->qy*_q.qw+this->qz*_q.qx,
+ this->qw*_q.qz+this->qx*_q.qy-this->qy*_q.qx+this->qz*_q.qw);
+ }
+
+ /// \brief Multiplication operator by a scalar.
+ /// \param[in] _f factor
+ /// \return quaternion multiplied by the scalar
+ public: Quaternion<T> operator*(const T &_f) const
+ {
+ return Quaternion<T>(this->qw*_f, this->qx*_f,
+ this->qy*_f, this->qz*_f);
+ }
+
+ /// \brief Multiplication operator
+ /// \param[in] _qt Quaternion<T> for multiplication
+ /// \return This quaternion multiplied by the parameter
+ public: Quaternion<T> operator*=(const Quaternion<T> &qt)
+ {
+ *this = *this * qt;
+ return *this;
+ }
+
+ /// \brief Vector3 multiplication operator
+ /// \param[in] _v vector to multiply
+ /// \return The result of the vector multiplication
+ public: Vector3<T> operator*(const Vector3<T> &_v) const
+ {
+ Vector3<T> uv, uuv;
+ Vector3<T> qvec(this->qx, this->qy, this->qz);
+ uv = qvec.Cross(_v);
+ uuv = qvec.Cross(uv);
+ uv *= (2.0f * this->qw);
+ uuv *= 2.0f;
+
+ return _v + uv + uuv;
+ }
+
+ /// \brief Equal to operator
+ /// \param[in] _qt Quaternion<T> for comparison
+ /// \return True if equal
+ public: bool operator==(const Quaternion<T> &_qt) const
+ {
+ return equal(this->qx, _qt.qx, static_cast<T>(0.001)) &&
+ equal(this->qy, _qt.qy, static_cast<T>(0.001)) &&
+ equal(this->qz, _qt.qz, static_cast<T>(0.001)) &&
+ equal(this->qw, _qt.qw, static_cast<T>(0.001));
+ }
+
+ /// \brief Not equal to operator
+ /// \param[in] _qt Quaternion<T> for comparison
+ /// \return True if not equal
+ public: bool operator!=(const Quaternion<T> &_qt) const
+ {
+ return !equal(this->qx, _qt.qx, static_cast<T>(0.001)) ||
+ !equal(this->qy, _qt.qy, static_cast<T>(0.001)) ||
+ !equal(this->qz, _qt.qz, static_cast<T>(0.001)) ||
+ !equal(this->qw, _qt.qw, static_cast<T>(0.001));
+ }
+
+ /// \brief Unary minus operator
+ /// \return negates each component of the quaternion
+ public: Quaternion<T> operator-() const
+ {
+ return Quaternion<T>(-this->qw, -this->qx, -this->qy, -this->qz);
+ }
+
+ /// \brief Rotate a vector using the quaternion
+ /// \param[in] _vec vector to rotate
+ /// \return the rotated vector
+ public: inline Vector3<T> RotateVector(const Vector3<T> &_vec) const
+ {
+ Quaternion<T> tmp(static_cast<T>(0),
+ _vec.X(), _vec.Y(), _vec.Z());
+ tmp = (*this) * (tmp * this->Inverse());
+ return Vector3<T>(tmp.qx, tmp.qy, tmp.qz);
+ }
+
+ /// \brief Do the reverse rotation of a vector by this quaternion
+ /// \param[in] _vec the vector
+ /// \return the reversed vector
+ public: Vector3<T> RotateVectorReverse(Vector3<T> _vec) const
+ {
+ Quaternion<T> tmp(0.0, _vec.X(), _vec.Y(), _vec.Z());
+
+ tmp = this->Inverse() * (tmp * (*this));
+
+ return Vector3<T>(tmp.qx, tmp.qy, tmp.qz);
+ }
+
+ /// \brief See if a quaternion is finite (e.g., not nan)
+ /// \return True if quaternion is finite
+ public: bool IsFinite() const
+ {
+ // std::isfinite works with floating point values, need to explicit
+ // cast to avoid ambiguity in vc++.
+ return std::isfinite(static_cast<double>(this->qw)) &&
+ std::isfinite(static_cast<double>(this->qx)) &&
+ std::isfinite(static_cast<double>(this->qy)) &&
+ std::isfinite(static_cast<double>(this->qz));
+ }
+
+ /// \brief Correct any nan values in this quaternion
+ public: inline void Correct()
+ {
+ // std::isfinite works with floating point values, need to explicit
+ // cast to avoid ambiguity in vc++.
+ if (!std::isfinite(static_cast<double>(this->qx)))
+ this->qx = 0;
+ if (!std::isfinite(static_cast<double>(this->qy)))
+ this->qy = 0;
+ if (!std::isfinite(static_cast<double>(this->qz)))
+ this->qz = 0;
+ if (!std::isfinite(static_cast<double>(this->qw)))
+ this->qw = 1;
+
+ if (equal(this->qw, static_cast<T>(0)) &&
+ equal(this->qx, static_cast<T>(0)) &&
+ equal(this->qy, static_cast<T>(0)) &&
+ equal(this->qz, static_cast<T>(0)))
+ {
+ this->qw = 1;
+ }
+ }
+
+ /// \brief Return the X axis
+ /// \return the X axis of the vector
+ public: Vector3<T> XAxis() const
+ {
+ T fTy = 2.0f*this->qy;
+ T fTz = 2.0f*this->qz;
+
+ T fTwy = fTy*this->qw;
+ T fTwz = fTz*this->qw;
+ T fTxy = fTy*this->qx;
+ T fTxz = fTz*this->qx;
+ T fTyy = fTy*this->qy;
+ T fTzz = fTz*this->qz;
+
+ return Vector3<T>(1.0f-(fTyy+fTzz), fTxy+fTwz, fTxz-fTwy);
+ }
+
+ /// \brief Return the Y axis
+ /// \return the Y axis of the vector
+ public: Vector3<T> YAxis() const
+ {
+ T fTx = 2.0f*this->qx;
+ T fTy = 2.0f*this->qy;
+ T fTz = 2.0f*this->qz;
+ T fTwx = fTx*this->qw;
+ T fTwz = fTz*this->qw;
+ T fTxx = fTx*this->qx;
+ T fTxy = fTy*this->qx;
+ T fTyz = fTz*this->qy;
+ T fTzz = fTz*this->qz;
+
+ return Vector3<T>(fTxy-fTwz, 1.0f-(fTxx+fTzz), fTyz+fTwx);
+ }
+
+ /// \brief Return the Z axis
+ /// \return the Z axis of the vector
+ public: Vector3<T> ZAxis() const
+ {
+ T fTx = 2.0f*this->qx;
+ T fTy = 2.0f*this->qy;
+ T fTz = 2.0f*this->qz;
+ T fTwx = fTx*this->qw;
+ T fTwy = fTy*this->qw;
+ T fTxx = fTx*this->qx;
+ T fTxz = fTz*this->qx;
+ T fTyy = fTy*this->qy;
+ T fTyz = fTz*this->qy;
+
+ return Vector3<T>(fTxz+fTwy, fTyz-fTwx, 1.0f-(fTxx+fTyy));
+ }
+
+ /// \brief Round all values to _precision decimal places
+ /// \param[in] _precision the precision
+ public: void Round(int _precision)
+ {
+ this->qx = precision(this->qx, _precision);
+ this->qy = precision(this->qy, _precision);
+ this->qz = precision(this->qz, _precision);
+ this->qw = precision(this->qw, _precision);
+ }
+
+ /// \brief Dot product
+ /// \param[in] _q the other quaternion
+ /// \return the product
+ public: T Dot(const Quaternion<T> &_q) const
+ {
+ return this->qw*_q.qw + this->qx * _q.qx +
+ this->qy*_q.qy + this->qz*_q.qz;
+ }
+
+ /// \brief Spherical quadratic interpolation
+ /// given the ends and an interpolation parameter between 0 and 1
+ /// \param[in] _ft the interpolation parameter
+ /// \param[in] _rkP the beginning quaternion
+ /// \param[in] _rkA first intermediate quaternion
+ /// \param[in] _rkB second intermediate quaternion
+ /// \param[in] _rkQ the end quaternion
+ /// \param[in] _shortestPath when true, the rotation may be inverted to
+ /// get to minimize rotation
+ /// \return The result of the quadratic interpolation
+ public: static Quaternion<T> Squad(T _fT,
+ const Quaternion<T> &_rkP, const Quaternion<T> &_rkA,
+ const Quaternion<T> &_rkB, const Quaternion<T> &_rkQ,
+ bool _shortestPath = false)
+ {
+ T fSlerpT = 2.0f*_fT*(1.0f-_fT);
+ Quaternion<T> kSlerpP = Slerp(_fT, _rkP, _rkQ, _shortestPath);
+ Quaternion<T> kSlerpQ = Slerp(_fT, _rkA, _rkB);
+ return Slerp(fSlerpT, kSlerpP, kSlerpQ);
+ }
+
+ /// \brief Spherical linear interpolation between 2 quaternions,
+ /// given the ends and an interpolation parameter between 0 and 1
+ /// \param[in] _ft the interpolation parameter
+ /// \param[in] _rkP the beginning quaternion
+ /// \param[in] _rkQ the end quaternion
+ /// \param[in] _shortestPath when true, the rotation may be inverted to
+ /// get to minimize rotation
+ /// \return The result of the linear interpolation
+ public: static Quaternion<T> Slerp(T _fT,
+ const Quaternion<T> &_rkP, const Quaternion<T> &_rkQ,
+ bool _shortestPath = false)
+ {
+ T fCos = _rkP.Dot(_rkQ);
+ Quaternion<T> rkT;
+
+ // Do we need to invert rotation?
+ if (fCos < 0.0f && _shortestPath)
+ {
+ fCos = -fCos;
+ rkT = -_rkQ;
+ }
+ else
+ {
+ rkT = _rkQ;
+ }
+
+ if (std::abs(fCos) < 1 - 1e-03)
+ {
+ // Standard case (slerp)
+ T fSin = sqrt(1 - (fCos*fCos));
+ T fAngle = atan2(fSin, fCos);
+ // FIXME: should check if (std::abs(fSin) >= 1e-3)
+ T fInvSin = 1.0f / fSin;
+ T fCoeff0 = sin((1.0f - _fT) * fAngle) * fInvSin;
+ T fCoeff1 = sin(_fT * fAngle) * fInvSin;
+ return _rkP * fCoeff0 + rkT * fCoeff1;
+ }
+ else
+ {
+ // There are two situations:
+ // 1. "rkP" and "rkQ" are very close (fCos ~= +1),
+ // so we can do a linear interpolation safely.
+ // 2. "rkP" and "rkQ" are almost inverse of each
+ // other (fCos ~= -1), there
+ // are an infinite number of possibilities interpolation.
+ // but we haven't have method to fix this case, so just use
+ // linear interpolation here.
+ Quaternion<T> t = _rkP * (1.0f - _fT) + rkT * _fT;
+ // taking the complement requires renormalisation
+ t.Normalize();
+ return t;
+ }
+ }
+
+ /// \brief Integrate quaternion for constant angular velocity vector
+ /// along specified interval `_deltaT`.
+ /// Implementation based on:
+ /// http://physicsforgames.blogspot.com/2010/02/quaternions.html
+ /// \param[in] _angularVelocity Angular velocity vector, specified in
+ /// same reference frame as base of this quaternion.
+ /// \param[in] _deltaT Time interval in seconds to integrate over.
+ /// \return Quaternion at integrated configuration.
+ public: Quaternion<T> Integrate(const Vector3<T> &_angularVelocity,
+ const T _deltaT) const
+ {
+ Quaternion<T> deltaQ;
+ Vector3<T> theta = _angularVelocity * _deltaT * 0.5;
+ T thetaMagSq = theta.SquaredLength();
+ T s;
+ if (thetaMagSq * thetaMagSq / 24.0 < MIN_D)
+ {
+ deltaQ.W() = 1.0 - thetaMagSq / 2.0;
+ s = 1.0 - thetaMagSq / 6.0;
+ }
+ else
+ {
+ double thetaMag = sqrt(thetaMagSq);
+ deltaQ.W() = cos(thetaMag);
+ s = sin(thetaMag) / thetaMag;
+ }
+ deltaQ.X() = theta.X() * s;
+ deltaQ.Y() = theta.Y() * s;
+ deltaQ.Z() = theta.Z() * s;
+ return deltaQ * (*this);
+ }
+
+ /// \brief Get the w component.
+ /// \return The w quaternion component.
+ public: inline const T &W() const
+ {
+ return this->qw;
+ }
+
+ /// \brief Get the x component.
+ /// \return The x quaternion component.
+ public: inline const T &X() const
+ {
+ return this->qx;
+ }
+
+ /// \brief Get the y component.
+ /// \return The y quaternion component.
+ public: inline const T &Y() const
+ {
+ return this->qy;
+ }
+
+ /// \brief Get the z component.
+ /// \return The z quaternion component.
+ public: inline const T &Z() const
+ {
+ return this->qz;
+ }
+
+
+ /// \brief Get a mutable w component.
+ /// \return The w quaternion component.
+ public: inline T &W()
+ {
+ return this->qw;
+ }
+
+ /// \brief Get a mutable x component.
+ /// \return The x quaternion component.
+ public: inline T &X()
+ {
+ return this->qx;
+ }
+
+ /// \brief Get a mutable y component.
+ /// \return The y quaternion component.
+ public: inline T &Y()
+ {
+ return this->qy;
+ }
+
+ /// \brief Get a mutable z component.
+ /// \return The z quaternion component.
+ public: inline T &Z()
+ {
+ return this->qz;
+ }
+
+ /// \brief Set the x component.
+ /// \param[in] _v The new value for the x quaternion component.
+ public: inline void X(T _v)
+ {
+ this->qx = _v;
+ }
+
+ /// \brief Set the y component.
+ /// \param[in] _v The new value for the y quaternion component.
+ public: inline void Y(T _v)
+ {
+ this->qy = _v;
+ }
+
+ /// \brief Set the z component.
+ /// \param[in] _v The new value for the z quaternion component.
+ public: inline void Z(T _v)
+ {
+ this->qz = _v;
+ }
+
+ /// \brief Set the w component.
+ /// \param[in] _v The new value for the w quaternion component.
+ public: inline void W(T _v)
+ {
+ this->qw = _v;
+ }
+
+ /// \brief Stream insertion operator
+ /// \param[in] _out output stream
+ /// \param[in] _q quaternion to output
+ /// \return the stream
+ public: friend std::ostream &operator<<(std::ostream &_out,
+ const ignition::math::Quaternion<T> &_q)
+ {
+ Vector3<T> v(_q.Euler());
+ _out << precision(v.X(), 6) << " " << precision(v.Y(), 6) << " "
+ << precision(v.Z(), 6);
+ return _out;
+ }
+
+ /// \brief Stream extraction operator
+ /// \param[in] _in input stream
+ /// \param[in] _q Quaternion<T> to read values into
+ /// \return The istream
+ public: friend std::istream &operator>>(std::istream &_in,
+ ignition::math::Quaternion<T> &_q)
+ {
+ Angle roll, pitch, yaw;
+
+ // Skip white spaces
+ _in.setf(std::ios_base::skipws);
+ _in >> roll >> pitch >> yaw;
+
+ _q.Euler(Vector3<T>(*roll, *pitch, *yaw));
+
+ return _in;
+ }
+
+ /// \brief w value of the quaternion
+ private: T qw;
+
+ /// \brief x value of the quaternion
+ private: T qx;
+
+ /// \brief y value of the quaternion
+ private: T qy;
+
+ /// \brief z value of the quaternion
+ private: T qz;
+ };
+
+ template<typename T> const Quaternion<T>
+ Quaternion<T>::Identity(1, 0, 0, 0);
+
+ template<typename T> const Quaternion<T>
+ Quaternion<T>::Zero(0, 0, 0, 0);
+
+ typedef Quaternion<double> Quaterniond;
+ typedef Quaternion<float> Quaternionf;
+ typedef Quaternion<int> Quaternioni;
+ }
+}
+#endif
diff --git a/include/ignition/math/Rand.hh b/include/ignition/math/Rand.hh
new file mode 100644
index 0000000..6695d9d
--- /dev/null
+++ b/include/ignition/math/Rand.hh
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_RAND_HH_
+#define IGNITION_MATH_RAND_HH_
+
+#include <random>
+#include <cmath>
+#include <cstdint>
+#include <ignition/math/Helpers.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \def GeneratorType
+ /// \brief std::mt19937
+ typedef std::mt19937 GeneratorType;
+ /// \def UniformRealDist
+ /// \brief std::uniform_real_distribution<double>
+ typedef std::uniform_real_distribution<double> UniformRealDist;
+ /// \def NormalRealDist
+ /// \brief std::normal_distribution<double>
+ typedef std::normal_distribution<double> NormalRealDist;
+ /// \def UniformIntDist
+ /// \brief std::uniform_int<int>
+ typedef std::uniform_int_distribution<int32_t> UniformIntDist;
+
+ /// \class Rand Rand.hh ignition/math/Rand.hh
+ /// \brief Random number generator class
+ class IGNITION_MATH_VISIBLE Rand
+ {
+ /// \brief Set the seed value.
+ /// \param[in] _seed The seed used to initialize the randon number
+ /// generator.
+ public: static void Seed(unsigned int _seed);
+
+ /// \brief Get the seed value.
+ /// \return The seed value used to initialize the random number
+ /// generator.
+ public: static unsigned int Seed();
+
+ /// \brief Get a double from a uniform distribution
+ /// \param[in] _min Minimum bound for the random number
+ /// \param[in] _max Maximum bound for the random number
+ public: static double DblUniform(double _min = 0, double _max = 1);
+
+ /// \brief Get a double from a normal distribution
+ /// \param[in] _mean Mean value for the distribution
+ /// \param[in] _sigma Sigma value for the distribution
+ public: static double DblNormal(double _mean = 0, double _sigma = 1);
+
+ /// \brief Get an integer from a uniform distribution
+ /// \param[in] _min Minimum bound for the random number
+ /// \param[in] _max Maximum bound for the random number
+ public: static int32_t IntUniform(int _min, int _max);
+
+ /// \brief Get an integer from a normal distribution
+ /// \param[in] _mean Mean value for the distribution
+ /// \param[in] _sigma Sigma value for the distribution
+ public: static int32_t IntNormal(int _mean, int _sigma);
+
+ /// \brief Get a mutable reference to the seed (create the static
+ /// member if it hasn't been created yet).
+ private: static uint32_t &SeedMutable();
+
+ /// \brief Get a mutable reference to the random generator (create the
+ /// static member if it hasn't been created yet).
+ private: static GeneratorType &RandGenerator();
+ };
+ }
+}
+#endif
diff --git a/include/ignition/math/RotationSpline.hh b/include/ignition/math/RotationSpline.hh
new file mode 100644
index 0000000..0f3a986
--- /dev/null
+++ b/include/ignition/math/RotationSpline.hh
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_ROTATIONSPLINE_HH_
+#define IGNITION_MATH_ROTATIONSPLINE_HH_
+
+#include <ignition/math/Quaternion.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ // Forward declare private data
+ class RotationSplinePrivate;
+
+ /// \class RotationSpline RotationSpline.hh ignition/math/RotationSpline.hh
+ /// \brief Spline for rotations
+ class IGNITION_MATH_VISIBLE RotationSpline
+ {
+ /// \brief Constructor. Sets the autoCalc to true
+ public: RotationSpline();
+
+ /// \brief Destructor. Nothing is done
+ public: ~RotationSpline();
+
+ /// \brief Adds a control point to the end of the spline.
+ /// \param[in] _p control point
+ public: void AddPoint(const Quaterniond &_p);
+
+ /// \brief Gets the detail of one of the control points of the spline.
+ /// \param[in] _index the index of the control point. _index is
+ /// clamped to [0, PointCount()-1].
+ /// \remarks This point must already exist in the spline.
+ /// \return The quaternion at the specified point.
+ /// If there are no points, then a Quaterniond with a value of
+ /// [INF, INF, INF, INF] is returned.
+ public: const Quaterniond &Point(const unsigned int _index) const;
+
+ /// \brief Gets the number of control points in the spline.
+ /// \return the count
+ public: unsigned int PointCount() const;
+
+ /// \brief Clears all the points in the spline.
+ public: void Clear();
+
+ /// \brief Updates a single point in the spline.
+ /// \remarks This point must already exist in the spline.
+ /// \param[in] _index index
+ /// \param[in] _value the new control point value
+ /// \return True on success, false if _index is larger or equal than
+ /// PointCount().
+ public: bool UpdatePoint(const unsigned int _index,
+ const Quaterniond &_value);
+
+ /// \brief Returns an interpolated point based on a parametric
+ /// value over the whole series.
+ /// \remarks Given a t value between 0 and 1 representing the
+ /// parametric distance along the whole length of the spline,
+ /// this method returns an interpolated point.
+ /// \param[in] _t Parametric value.
+ /// \param[in] _useShortestPath Defines if rotation should take the
+ /// shortest possible path
+ /// \return The rotation, or [INF, INF, INF, INF] on error. Use
+ /// Quateriond::IsFinite() to check for an error
+ public: Quaterniond Interpolate(double _t,
+ const bool _useShortestPath = true);
+
+ /// \brief Interpolates a single segment of the spline
+ /// given a parametric value.
+ /// \param[in] _fromIndex The point index to treat as t = 0.
+ /// _fromIndex + 1 is deemed to be t = 1
+ /// \param[in] _t Parametric value
+ /// \param[in] _useShortestPath Defines if rotation should take the
+ /// shortest possible path
+ /// \return the rotation, or [INF, INF, INF, INF] on error. Use
+ /// Quateriond::IsFinite() to check for an error
+ public: Quaterniond Interpolate(const unsigned int _fromIndex,
+ const double _t, const bool _useShortestPath = true);
+
+ /// \brief Tells the spline whether it should automatically calculate
+ /// tangents on demand as points are added.
+ /// \remarks The spline calculates tangents at each point automatically
+ /// based on the input points. Normally it does this every
+ /// time a point changes. However, if you have a lot of points
+ /// to add in one go, you probably don't want to incur this
+ /// overhead and would prefer to defer the calculation until
+ /// you are finished setting all the points. You can do this
+ /// by calling this method with a parameter of 'false'. Just
+ /// remember to manually call the recalcTangents method when
+ /// you are done.
+ /// \param[in] _autoCalc If true, tangents are calculated for you
+ /// whenever a point changes. If false, you must call reclacTangents to
+ /// recalculate them when it best suits.
+ public: void AutoCalculate(bool _autoCalc);
+
+ /// \brief Recalculates the tangents associated with this spline.
+ /// \remarks If you tell the spline not to update on demand by calling
+ /// setAutoCalculate(false) then you must call this after
+ /// completing your updates to the spline points.
+ public: void RecalcTangents();
+
+ /// \brief Private data pointer
+ private: RotationSplinePrivate *dataPtr;
+ };
+ }
+}
+
+#endif
diff --git a/include/ignition/math/SemanticVersion.hh b/include/ignition/math/SemanticVersion.hh
new file mode 100644
index 0000000..8f28723
--- /dev/null
+++ b/include/ignition/math/SemanticVersion.hh
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+
+#ifndef IGNITION_MATH_SEMANTICVERSION_HH_
+#define IGNITION_MATH_SEMANTICVERSION_HH_
+
+#include <memory>
+#include <string>
+#include <ignition/math/Helpers.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ // Forward declare private data class
+ class SemanticVersionPrivate;
+
+ /// \class SemanticVersion SemanticVersion.hh
+ /// ignition/math/SemanticVersion.hh
+ /// \brief Version comparison class based on Semantic Versioning 2.0.0
+ /// http://semver.org/
+ /// Compares versions and converts versions from string.
+ class IGNITION_MATH_VISIBLE SemanticVersion
+ {
+ /// \brief Default constructor. Use the Parse function to populate
+ /// an instance with version information.
+ public: SemanticVersion();
+
+ /// \brief Constructor
+ /// \param[in] _v the string version. ex: "0.3.2"
+ public: explicit SemanticVersion(const std::string &_v);
+
+ /// \brief Copy constructor
+ /// \param[in] _copy the other version
+ public: SemanticVersion(const SemanticVersion &_copy);
+
+ /// \brief Assignment operator
+ /// \param[in] _other The version to assign from.
+ /// \return The reference to this instance
+ public: SemanticVersion &operator=(const SemanticVersion &_other);
+
+ /// \brief Constructor
+ /// \param[in] _major The major number
+ /// \param[in] _minor The minor number
+ /// \param[in] _patch The patch number
+ /// \param[in] _prerelease The prerelease string
+ /// \param[in] _build The build metadata string
+ public: SemanticVersion(const unsigned int _major,
+ const unsigned int _minor = 0,
+ const unsigned int _patch = 0,
+ const std::string &_prerelease = "",
+ const std::string &_build = "");
+
+ /// \brief Destructor
+ public: ~SemanticVersion();
+
+ /// \brief Parse a version string and set the major, minor, patch
+ /// numbers, and prerelease and build strings.
+ /// \param[in] _versionStr The version string, such as "1.2.3-pr+123"
+ /// \retur True on success.
+ public: bool Parse(const std::string &_versionStr);
+
+ /// \brief Returns the version as a string
+ /// \return The semantic version string
+ public: std::string Version() const;
+
+ /// \brief Get the major number
+ /// \return The major number
+ public: unsigned int Major() const;
+
+ /// \brief Get the minor number
+ /// \return The minor number
+ public: unsigned int Minor() const;
+
+ /// \brief Get the patch number
+ /// \return The patch number
+ public: unsigned int Patch() const;
+
+ /// \brief Get the prerelease string.
+ /// \return Prelrease string, empty if a prerelease string was not
+ /// specified.
+ public: std::string Prerelease() const;
+
+ /// \brief Get the build metadata string. Build meta data is not used
+ /// when determining precedence.
+ /// \return Build metadata string, empty if a build metadata string was
+ /// not specified.
+ public: std::string Build() const;
+
+ /// \brief Less than comparison operator
+ /// \param[in] _other The other version to compare to
+ /// \return True if _other version is newer
+ public: bool operator<(const SemanticVersion &_other) const;
+
+ /// \brief Less than or equal comparison operator
+ /// \param[in] _other The other version to compare to
+ /// \return True if _other version is newer or equal
+ public: bool operator<=(const SemanticVersion &_other) const;
+
+ /// \brief Greater than comparison operator
+ /// \param[in] _other The other version to compare to
+ /// \return True if _other version is older
+ public: bool operator>(const SemanticVersion &_other) const;
+
+ /// \brief Greater than or equal comparison operator
+ /// \param[in] _other The other version to compare to
+ /// \return True if _other version is older or the same
+ public: bool operator>=(const SemanticVersion &_other) const;
+
+ /// \brief Equality comparison operator
+ /// \param[in] _other The other version to compare to
+ /// \return True if _other version is the same
+ public: bool operator==(const SemanticVersion &_other) const;
+
+ /// \brief Inequality comparison operator
+ /// \param[in] _other The other version to compare to
+ /// \return True if _other version is different
+ public: bool operator!=(const SemanticVersion &_other) const;
+
+ /// \brief Stream insertion operator
+ /// \param _out output stream
+ /// \param _v Semantic version to output
+ /// \return the stream
+ public: friend std::ostream &operator<<(std::ostream &_out,
+ const SemanticVersion &_v)
+ {
+ _out << _v.Version();
+ return _out;
+ }
+
+#ifdef _WIN32
+// Disable warning C4251 which is triggered by
+// std::unique_ptr
+#pragma warning(push)
+#pragma warning(disable: 4251)
+#endif
+ /// \brief Pointer to private data
+ private: std::unique_ptr<SemanticVersionPrivate> dataPtr;
+#ifdef _WIN32
+#pragma warning(pop)
+#endif
+ };
+ }
+}
+#endif
diff --git a/include/ignition/math/SignalStats.hh b/include/ignition/math/SignalStats.hh
new file mode 100644
index 0000000..eb5350d
--- /dev/null
+++ b/include/ignition/math/SignalStats.hh
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_SIGNALSTATS_HH_
+#define IGNITION_MATH_SIGNALSTATS_HH_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <ignition/math/Helpers.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \brief Forward declare private data class.
+ class SignalStatisticPrivate;
+
+ /// \class SignalStatistic SignalStats.hh ignition/math/SignalStats.hh
+ /// \brief Statistical properties of a discrete time scalar signal.
+ class IGNITION_MATH_VISIBLE SignalStatistic
+ {
+ /// \brief Constructor
+ public: SignalStatistic();
+
+ /// \brief Destructor
+ public: virtual ~SignalStatistic();
+
+ /// \brief Copy constructor
+ /// \param[in] _ss SignalStatistic to copy
+ public: SignalStatistic(const SignalStatistic &_ss);
+
+ /// \brief Get the current value of the statistical measure.
+ /// \return Current value of the statistical measure.
+ public: virtual double Value() const = 0;
+
+ /// \brief Get a short version of the name of this statistical measure.
+ /// \return Short name of the statistical measure.
+ public: virtual std::string ShortName() const = 0;
+
+ /// \brief Get number of data points in measurement.
+ /// \return Number of data points in measurement.
+ public: virtual size_t Count() const;
+
+ /// \brief Add a new sample to the statistical measure.
+ /// \param[in] _data New signal data point.
+ public: virtual void InsertData(const double _data) = 0;
+
+ /// \brief Forget all previous data.
+ public: virtual void Reset();
+
+#ifdef _WIN32
+// Disable warning C4251 which is triggered by
+// std::unique_ptr
+#pragma warning(push)
+#pragma warning(disable: 4251)
+#endif
+ /// \brief Pointer to private data.
+ protected: std::unique_ptr<SignalStatisticPrivate> dataPtr;
+#ifdef _WIN32
+#pragma warning(pop)
+#endif
+ };
+ /// \}
+
+ /// \class SignalMaximum SignalStats.hh ignition/math/SignalStats.hh
+ /// \brief Computing the maximum value of a discretely sampled signal.
+ class IGNITION_MATH_VISIBLE SignalMaximum : public SignalStatistic
+ {
+ // Documentation inherited.
+ public: virtual double Value() const;
+
+ /// \brief Get a short version of the name of this statistical measure.
+ /// \return "max"
+ public: virtual std::string ShortName() const;
+
+ // Documentation inherited.
+ public: virtual void InsertData(const double _data);
+ };
+ /// \}
+
+ /// \class SignalMean SignalStats.hh ignition/math/SignalStats.hh
+ /// \brief Computing the mean value of a discretely sampled signal.
+ class IGNITION_MATH_VISIBLE SignalMean : public SignalStatistic
+ {
+ // Documentation inherited.
+ public: virtual double Value() const;
+
+ /// \brief Get a short version of the name of this statistical measure.
+ /// \return "mean"
+ public: virtual std::string ShortName() const;
+
+ // Documentation inherited.
+ public: virtual void InsertData(const double _data);
+ };
+ /// \}
+
+ /// \class SignalMinimum SignalStats.hh ignition/math/SignalStats.hh
+ /// \brief Computing the minimum value of a discretely sampled signal.
+ class IGNITION_MATH_VISIBLE SignalMinimum : public SignalStatistic
+ {
+ // Documentation inherited.
+ public: virtual double Value() const;
+
+ /// \brief Get a short version of the name of this statistical measure.
+ /// \return "min"
+ public: virtual std::string ShortName() const;
+
+ // Documentation inherited.
+ public: virtual void InsertData(const double _data);
+ };
+ /// \}
+
+ /// \class SignalRootMeanSquare SignalStats.hh ignition/math/SignalStats.hh
+ /// \brief Computing the square root of the mean squared value
+ /// of a discretely sampled signal.
+ class IGNITION_MATH_VISIBLE SignalRootMeanSquare : public SignalStatistic
+ {
+ // Documentation inherited.
+ public: virtual double Value() const;
+
+ /// \brief Get a short version of the name of this statistical measure.
+ /// \return "rms"
+ public: virtual std::string ShortName() const;
+
+ // Documentation inherited.
+ public: virtual void InsertData(const double _data);
+ };
+ /// \}
+
+ /// \class SignalMaxAbsoluteValue SignalStats.hh
+ /// ignition/math/SignalStats.hh
+ /// \brief Computing the maximum of the absolute value
+ /// of a discretely sampled signal.
+ /// Also known as the maximum norm, infinity norm, or supremum norm.
+ class IGNITION_MATH_VISIBLE SignalMaxAbsoluteValue : public SignalStatistic
+ {
+ // Documentation inherited.
+ public: virtual double Value() const;
+
+ /// \brief Get a short version of the name of this statistical measure.
+ /// \return "maxAbs"
+ public: virtual std::string ShortName() const;
+
+ // Documentation inherited.
+ public: virtual void InsertData(const double _data);
+ };
+ /// \}
+
+ /// \class SignalVariance SignalStats.hh ignition/math/SignalStats.hh
+ /// \brief Computing the incremental variance
+ /// of a discretely sampled signal.
+ class IGNITION_MATH_VISIBLE SignalVariance : public SignalStatistic
+ {
+ // Documentation inherited.
+ public: virtual double Value() const;
+
+ /// \brief Get a short version of the name of this statistical measure.
+ /// \return "var"
+ public: virtual std::string ShortName() const;
+
+ // Documentation inherited.
+ public: virtual void InsertData(const double _data);
+ };
+ /// \}
+
+ /// \brief Forward declare private data class.
+ class SignalStatsPrivate;
+
+ /// \class SignalStats SignalStats.hh ignition/math/SignalStats.hh
+ /// \brief Collection of statistics for a scalar signal.
+ class IGNITION_MATH_VISIBLE SignalStats
+ {
+ /// \brief Constructor
+ public: SignalStats();
+
+ /// \brief Destructor
+ public: ~SignalStats();
+
+ /// \brief Copy constructor
+ /// \param[in] _ss SignalStats to copy
+ public: SignalStats(const SignalStats &_ss);
+
+ /// \brief Get number of data points in first statistic.
+ /// Technically you can have different numbers of data points
+ /// in each statistic if you call InsertStatistic after InsertData,
+ /// but this is not a recommended use case.
+ /// \return Number of data points in first statistic.
+ public: size_t Count() const;
+
+ /// \brief Get the current values of each statistical measure,
+ /// stored in a map using the short name as the key.
+ /// \return Map with short name of each statistic as key
+ /// and value of statistic as the value.
+ public: std::map<std::string, double> Map() const;
+
+ /// \brief Add a new sample to the statistical measures.
+ /// \param[in] _data New signal data point.
+ public: void InsertData(const double _data);
+
+ /// \brief Add a new type of statistic.
+ /// \param[in] _name Short name of new statistic.
+ /// Valid values include:
+ /// "maxAbs"
+ /// "mean"
+ /// "rms"
+ /// \return True if statistic was successfully added,
+ /// false if name was not recognized or had already
+ /// been inserted.
+ public: bool InsertStatistic(const std::string &_name);
+
+ /// \brief Add multiple statistics.
+ /// \param[in] _names Comma-separated list of new statistics.
+ /// For example, all statistics could be added with:
+ /// "maxAbs,mean,rms"
+ /// \return True if all statistics were successfully added,
+ /// false if any names were not recognized or had already
+ /// been inserted.
+ public: bool InsertStatistics(const std::string &_names);
+
+ /// \brief Forget all previous data.
+ public: void Reset();
+
+ /// \brief Assignment operator
+ /// \param[in] _v A SignalStats to copy
+ /// \return this
+ public: SignalStats &operator=(const SignalStats &_s);
+
+#ifdef _WIN32
+// Disable warning C4251 which is triggered by
+// std::unique_ptr
+#pragma warning(push)
+#pragma warning(disable: 4251)
+#endif
+ /// \brief Pointer to private data.
+ private: std::unique_ptr<SignalStatsPrivate> dataPtr;
+#ifdef _WIN32
+#pragma warning(pop)
+#endif
+ };
+ /// \}
+ }
+}
+#endif
+
diff --git a/include/ignition/math/SphericalCoordinates.hh b/include/ignition/math/SphericalCoordinates.hh
new file mode 100644
index 0000000..d138be5
--- /dev/null
+++ b/include/ignition/math/SphericalCoordinates.hh
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_SPHERICALCOORDINATES_HH_
+#define IGNITION_MATH_SPHERICALCOORDINATES_HH_
+
+#include <memory>
+#include <string>
+
+#include <ignition/math/Angle.hh>
+#include <ignition/math/Vector3.hh>
+#include <ignition/math/Helpers.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ class SphericalCoordinatesPrivate;
+
+ /// \class SphericalCoordinates SphericalCoordinates.hh commmon/common.hh
+ /// \brief Convert spherical coordinates for planetary surfaces.
+ class IGNITION_MATH_VISIBLE SphericalCoordinates
+ {
+ /// \enum SurfaceType
+ /// \brief Unique identifiers for planetary surface models.
+ public: enum SurfaceType
+ {
+ /// \brief Model of reference ellipsoid for earth, based on
+ /// WGS 84 standard. see wikipedia: World_Geodetic_System
+ EARTH_WGS84 = 1
+ };
+
+ /// \enum CoordinateType
+ /// \brief Unique identifiers for coordinate types.
+ public: enum CoordinateType
+ {
+ /// \brief Latitude, Longitude and Altitude by SurfaceType
+ SPHERICAL = 1,
+
+ /// \brief Earth centered, earth fixed Cartesian
+ ECEF = 2,
+
+ /// \brief Local tangent plane (East, North, Up)
+ GLOBAL = 3,
+
+ /// \brief Heading-adjusted tangent plane (X, Y, Z)
+ LOCAL = 4
+ };
+
+ /// \brief Constructor.
+ public: SphericalCoordinates();
+
+ /// \brief Constructor with surface type input.
+ /// \param[in] _type SurfaceType specification.
+ public: explicit SphericalCoordinates(const SurfaceType _type);
+
+ /// \brief Constructor with surface type, angle, and elevation inputs.
+ /// \param[in] _type SurfaceType specification.
+ /// \param[in] _latitude Reference latitude.
+ /// \param[in] _longitude Reference longitude.
+ /// \param[in] _elevation Reference elevation.
+ /// \param[in] _heading Heading offset.
+ public: SphericalCoordinates(const SurfaceType _type,
+ const ignition::math::Angle &_latitude,
+ const ignition::math::Angle &_longitude,
+ const double _elevation,
+ const ignition::math::Angle &_heading);
+
+ /// \brief Copy constructor.
+ /// \param[in] _sc Spherical coordinates to copy.
+ public: SphericalCoordinates(const SphericalCoordinates &_sc);
+
+ /// \brief Destructor.
+ public: ~SphericalCoordinates();
+
+ /// \brief Convert a Cartesian position vector to geodetic coordinates.
+ /// \param[in] _xyz Cartesian position vector in the world frame.
+ /// \return Cooordinates: geodetic latitude (deg), longitude (deg),
+ /// altitude above sea level (m).
+ public: ignition::math::Vector3d SphericalFromLocalPosition(
+ const ignition::math::Vector3d &_xyz) const;
+
+ /// \brief Convert a Cartesian velocity vector in the local frame
+ /// to a global Cartesian frame with components East, North, Up.
+ /// \param[in] _xyz Cartesian velocity vector in the world frame.
+ /// \return Rotated vector with components (x,y,z): (East, North, Up).
+ public: ignition::math::Vector3d GlobalFromLocalVelocity(
+ const ignition::math::Vector3d &_xyz) const;
+
+ /// \brief Convert a string to a SurfaceType.
+ /// Allowed values: ["EARTH_WGS84"].
+ /// \param[in] _str String to convert.
+ /// \return Conversion to SurfaceType.
+ public: static SurfaceType Convert(const std::string &_str);
+
+ /// \brief Get the distance between two points expressed in geographic
+ /// latitude and longitude. It assumes that both points are at sea level.
+ /// Example: _latA = 38.0016667 and _lonA = -123.0016667) represents
+ /// the point with latitude 38d 0'6.00"N and longitude 123d 0'6.00"W.
+ /// \param[in] _latA Latitude of point A.
+ /// \param[in] _longA Longitude of point A.
+ /// \param[in] _latB Latitude of point B.
+ /// \param[in] _longB Longitude of point B.
+ /// \return Distance in meters.
+ public: static double Distance(const ignition::math::Angle &_latA,
+ const ignition::math::Angle &_lonA,
+ const ignition::math::Angle &_latB,
+ const ignition::math::Angle &_lonB);
+
+ /// \brief Get SurfaceType currently in use.
+ /// \return Current SurfaceType value.
+ public: SurfaceType Surface() const;
+
+ /// \brief Get reference geodetic latitude.
+ /// \return Reference geodetic latitude.
+ public: ignition::math::Angle LatitudeReference() const;
+
+ /// \brief Get reference longitude.
+ /// \return Reference longitude.
+ public: ignition::math::Angle LongitudeReference() const;
+
+ /// \brief Get reference elevation in meters.
+ /// \return Reference elevation.
+ public: double ElevationReference() const;
+
+ /// \brief Get heading offset for the reference frame, expressed as
+ /// angle from East to x-axis, or equivalently
+ /// from North to y-axis.
+ /// \return Heading offset of reference frame.
+ public: ignition::math::Angle HeadingOffset() const;
+
+ /// \brief Set SurfaceType for planetary surface model.
+ /// \param[in] _type SurfaceType value.
+ public: void SetSurface(const SurfaceType &_type);
+
+ /// \brief Set reference geodetic latitude.
+ /// \param[in] _angle Reference geodetic latitude.
+ public: void SetLatitudeReference(const ignition::math::Angle &_angle);
+
+ /// \brief Set reference longitude.
+ /// \param[in] _angle Reference longitude.
+ public: void SetLongitudeReference(const ignition::math::Angle &_angle);
+
+ /// \brief Set reference elevation above sea level in meters.
+ /// \param[in] _elevation Reference elevation.
+ public: void SetElevationReference(const double _elevation);
+
+ /// \brief Set heading angle offset for the frame.
+ /// \param[in] _angle Heading offset for the frame.
+ public: void SetHeadingOffset(const ignition::math::Angle &_angle);
+
+ /// \brief Convert a geodetic position vector to Cartesian coordinates.
+ /// \param[in] _xyz Geodetic position in the planetary frame of reference
+ /// \return Cartesian position vector in the world frame
+ public: ignition::math::Vector3d LocalFromSphericalPosition(
+ const ignition::math::Vector3d &_xyz) const;
+
+ /// \brief Convert a Cartesian velocity vector with components East,
+ /// North, Up to a local cartesian frame vector XYZ.
+ /// \param[in] Vector with components (x,y,z): (East, North, Up).
+ /// \return Cartesian vector in the world frame.
+ public: ignition::math::Vector3d LocalFromGlobalVelocity(
+ const ignition::math::Vector3d &_xyz) const;
+
+ /// \brief Update coordinate transformation matrix with reference location
+ public: void UpdateTransformationMatrix();
+
+ /// \brief Convert between positions in SPHERICAL/ECEF/LOCAL/GLOBAL frame
+ /// \param[in] _pos Position vector in frame defined by parameter _in
+ /// \param[in] _in CoordinateType for input
+ /// \param[in] _out CoordinateType for output
+ /// \return Transformed coordinate using cached orgin
+ public: ignition::math::Vector3d
+ PositionTransform(const ignition::math::Vector3d &_pos,
+ const CoordinateType &_in, const CoordinateType &_out) const;
+
+ /// \brief Convert between velocity in SPHERICAL/ECEF/LOCAL/GLOBAL frame
+ /// \param[in] _vel Velocity vector in frame defined by parameter _in
+ /// \param[in] _in CoordinateType for input
+ /// \param[in] _out CoordinateType for output
+ /// \return Transformed velocity vector
+ public: ignition::math::Vector3d VelocityTransform(
+ const ignition::math::Vector3d &_vel,
+ const CoordinateType &_in, const CoordinateType &_out) const;
+
+ /// \brief Equality operator, result = this == _sc
+ /// \param[in] _sc Spherical coordinates to check for equality
+ /// \return true if this == _sc
+ public: bool operator==(const SphericalCoordinates &_sc) const;
+
+ /// \brief Inequality
+ /// \param[in] _sc Spherical coordinates to check for inequality
+ /// \return true if this != _sc
+ public: bool operator!=(const SphericalCoordinates &_sc) const;
+
+ /// \brief Assignment operator
+ /// \param[in] _sc The spherical coordinates to copy from.
+ /// \return this
+ public: SphericalCoordinates &operator=(
+ const SphericalCoordinates &_sc);
+
+
+#ifdef _WIN32
+// Disable warning C4251 which is triggered by
+// std::unique_ptr
+#pragma warning(push)
+#pragma warning(disable: 4251)
+#endif
+ /// \internal
+ /// \brief Pointer to the private data
+ private: std::unique_ptr<SphericalCoordinatesPrivate> dataPtr;
+#ifdef _WIN32
+#pragma warning(pop)
+#endif
+ };
+ /// \}
+ }
+}
+#endif
diff --git a/include/ignition/math/Spline.hh b/include/ignition/math/Spline.hh
new file mode 100644
index 0000000..ef16ca7
--- /dev/null
+++ b/include/ignition/math/Spline.hh
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+// Note: Originally cribbed from Ogre3d. Modified to implement Cardinal
+// spline and catmull-rom spline
+#ifndef IGNITION_MATH_SPLINE_HH_
+#define IGNITION_MATH_SPLINE_HH_
+
+#include <ignition/math/Helpers.hh>
+#include <ignition/math/Vector3.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ // Forward declare private classes
+ class ControlPoint;
+ class SplinePrivate;
+
+ /// \class Spline Spline.hh ignition/math/Spline.hh
+ /// \brief Splines
+ class IGNITION_MATH_VISIBLE Spline
+ {
+ /// \brief constructor
+ public: Spline();
+
+ /// \brief destructor
+ public: ~Spline();
+
+ /// \brief Sets the tension parameter.
+ /// \remarks A value of 0 results in a Catmull-Rom
+ /// spline.
+ /// \param[in] _t Tension value between 0.0 and 1.0
+ public: void Tension(double _t);
+
+ /// \brief Gets the tension value.
+ /// \return the value of the tension, which is between 0.0 and 1.0.
+ public: double Tension() const;
+
+ /// \brief Gets spline arc length.
+ /// \return arc length or INF on error.
+ public: double ArcLength() const;
+
+ /// \brief Gets spline arc length up to
+ /// a given parameter value \p _t.
+ /// \param[in] _t parameter value (range 0 to 1).
+ /// \return arc length up to \p _t or INF on error.
+ public: double ArcLength(const double _t) const;
+
+ /// \brief Gets a spline segment arc length.
+ /// \param[in] _index of the spline segment.
+ /// \param[in] _t parameter value (range 0 to 1).
+ /// \return arc length of a given segment up to
+ /// \p _t or INF on error.
+ public: double ArcLength(const unsigned int _index,
+ const double _t) const;
+
+ /// \brief Adds a single control point to the
+ /// end of the spline.
+ /// \param[in] _p control point value to add.
+ public: void AddPoint(const Vector3d &_p);
+
+ /// \brief Adds a single control point to the end
+ /// of the spline with fixed tangent.
+ /// \param[in] _p control point value to add.
+ /// \param[in] _t tangent at \p _p.
+ public: void AddPoint(const Vector3d &_p, const Vector3d &_t);
+
+ /// \brief Adds a single control point to the end
+ /// of the spline.
+ /// \param[in] _cp control point to add.
+ /// \param[in] _fixed whether this control point
+ /// should not be subject to tangent recomputation.
+ private: void AddPoint(const ControlPoint &_cp, const bool _fixed);
+
+ /// \brief Gets the value for one of the control points
+ /// of the spline.
+ /// \param[in] _index the control point index.
+ /// \return the control point value, or [INF, INF, INF]
+ /// on error. Use Vector3d::IsFinite() to check for an error.
+ public: Vector3d Point(const unsigned int _index) const;
+
+ /// \brief Gets the tangent value for one of the control points
+ /// of the spline.
+ /// \param[in] _index the control point index.
+ /// \return the control point tangent, or [INF, INF, INF]
+ /// on error. Use Vector3d::IsFinite() to check for an error.
+ public: Vector3d Tangent(const unsigned int _index) const;
+
+ /// \brief Gets the mth derivative for one of the control points
+ /// of the spline.
+ /// \param[in] _index the control point index.
+ /// \param[in] _mth derivative order.
+ /// \return the control point mth derivative, or [INF, INF, INF]
+ /// on error. Use Vector3d::IsFinite() to check for an error.
+ public: Vector3d MthDerivative(const unsigned int _index,
+ const unsigned int _mth) const;
+
+ /// \brief Gets the number of control points in the spline.
+ /// \return the count
+ public: size_t PointCount() const;
+
+ /// \brief Clears all the points in the spline.
+ public: void Clear();
+
+ /// \brief Updates a single control point value in the spline,
+ /// keeping its tangent.
+ /// \param[in] _index the control point index.
+ /// \param[in] _p the new control point value.
+ /// \return True on success.
+ public: bool UpdatePoint(const unsigned int _index,
+ const Vector3d &_p);
+
+ /// \brief Updates a single control point in the spline, along
+ /// with its tangent.
+ /// \param[in] _index the control point index.
+ /// \param[in] _p the new control point value.
+ /// \param[in] _t the new control point tangent.
+ /// \return True on success.
+ public: bool UpdatePoint(const unsigned int _index,
+ const Vector3d &_p,
+ const Vector3d &_t);
+
+ /// \brief Updates a single control point in the spline.
+ /// \param[in] _index the control point index
+ /// \param[in] _cp the new control point
+ /// \param[in] _fixed whether the new control point should not be
+ /// subject to tangent recomputation
+ /// \return True on success.
+ private: bool UpdatePoint(const unsigned int _index,
+ const ControlPoint &_cp,
+ const bool _fixed);
+
+ /// \brief Interpolates a point on the spline
+ /// at parameter value \p _t.
+ /// \remarks Parameter value is normalized over the
+ /// whole spline arc length. Arc length is assumed
+ /// to be linear with the parameter.
+ /// \param[in] _t parameter value (range 0 to 1).
+ /// \return the interpolated point, or
+ /// [INF, INF, INF] on error. Use
+ /// Vector3d::IsFinite() to check for an error.
+ public: Vector3d Interpolate(const double _t) const;
+
+ /// \brief Interpolates a point on a segment of the spline
+ /// at parameter value \p _t.
+ /// \remarks Parameter value is normalized over the
+ /// segment arc length. Arc length is assumed
+ /// to be linear with the parameter.
+ /// \param[in] _fromIndex The point index to treat as t = 0.
+ /// fromIndex + 1 is deemed to be t = 1.
+ /// \param[in] _t parameter value (range 0 to 1).
+ /// \return the interpolated point, or [INF, INF, INF] on
+ /// error. Use Vector3d::IsFinite() to check for an error.
+ public: Vector3d Interpolate(const unsigned int _fromIndex,
+ const double _t) const;
+
+ /// \brief Interpolates a tangent on the spline at
+ /// parameter value \p _t.
+ /// \remarks Parameter value is normalized over the
+ /// whole spline arc length. Arc length is assumed
+ /// to be linear with the parameter.
+ /// \param[in] _t parameter value (range 0 to 1).
+ /// \return the interpolated point, or [INF, INF, INF]
+ /// on error. Use Vector3d::IsFinte() to check for an error.
+ public: Vector3d InterpolateTangent(const double _t) const;
+
+ /// \brief Interpolates the tangent on a segment of the spline
+ /// at parameter value \p _t.
+ /// \remarks Parameter value is normalized over the
+ /// segment arc length. Arc length is assumed
+ /// to be linear with the parameter.
+ /// \param[in] _fromIndex the point index to treat as t = 0.
+ /// fromIndex + 1 is deemed to be t = 1.
+ /// \param[in] _t parameter value (range 0 to 1).
+ /// \return the interpolated point, or [INF, INF, INF] on
+ /// error. Use Vector3d::IsFinte() to check for an error.
+ public: Vector3d InterpolateTangent(const unsigned int _fromIndex,
+ const double _t) const;
+
+ /// \brief Interpolates the mth derivative of the spline at
+ /// parameter value \p _t.
+ /// \param[in] _mth order of curve derivative to interpolate.
+ /// \param[in] _t parameter value (range 0 to 1).
+ /// \return the interpolated mth derivative, or [INF, INF, INF]
+ /// on error. Use Vector3d::IsFinite() to check for an error.
+ public: Vector3d InterpolateMthDerivative(const unsigned int _mth,
+ const double _1) const;
+
+ /// \brief Interpolates the mth derivative of a segment of the spline
+ /// at parameter value \p _t.
+ /// \remarks Parameter value is normalized over the segment
+ /// arc length. Arc length is assumed to be linear with the parameter.
+ /// \param[in] _fromIndex point index to treat as t = 0, fromIndex + 1
+ /// is deemed to be t = 1.
+ /// \param[in] _mth order of curve derivative to interpolate.
+ /// \param[in] _t parameter value (range 0 to 1).
+ /// \return the interpolated mth derivative, or [INF, INF, INF] on
+ /// error. Use Vector3d::IsFinite() to check for an error.
+ public: Vector3d InterpolateMthDerivative(const unsigned int _fromIndex,
+ const unsigned int _mth,
+ const double _s) const;
+
+ /// \brief Tells the spline whether it should automatically
+ /// calculate tangents on demand as points are added.
+ /// \remarks The spline calculates tangents at each point
+ /// automatically based on the input points. Normally it
+ /// does this every time a point changes. However, if you
+ /// have a lot of points to add in one go, you probably
+ /// don't want to incur this overhead and would prefer to
+ /// defer the calculation until you are finished setting all
+ /// the points. You can do this by calling this method with a
+ /// parameter of 'false'. Just remember to manually call the
+ /// recalcTangents method when you are done.
+ /// \param[in] _autoCalc If true, tangents are calculated for you whenever
+ /// a point changes. If false, you must call RecalcTangents to
+ /// recalculate them when it best suits.
+ public: void AutoCalculate(bool _autoCalc);
+
+ /// \brief Recalculates the tangents associated with this spline.
+ /// \remarks If you tell the spline not to update on demand by
+ /// calling setAutoCalculate(false) then you must call this
+ /// after completing your updates to the spline points.
+ public: void RecalcTangents();
+
+ /// \brief Rebuilds spline segments.
+ private: void Rebuild();
+
+ /// \internal
+ /// \brief Maps \p _t parameter value over the whole spline
+ /// to the right segment (starting at point \p _index) with
+ /// the proper parameter value fraction \p _fraction.
+ /// \remarks Arc length is assumed to be linear with the parameter.
+ /// \param[in] _t parameter value over the whole spline (range 0 to 1).
+ /// \param[out] _index point index at which the segment starts.
+ /// \param[out] _fraction parameter value fraction for the given segment.
+ /// \return True on success.
+ private: bool MapToSegment(const double _t,
+ unsigned int &_index,
+ double &_fraction) const;
+
+ /// \internal
+ /// \brief Private data pointer
+ private: SplinePrivate *dataPtr;
+ };
+ }
+}
+#endif
diff --git a/include/ignition/math/Temperature.hh b/include/ignition/math/Temperature.hh
new file mode 100644
index 0000000..ffb1c80
--- /dev/null
+++ b/include/ignition/math/Temperature.hh
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2016 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_TEMPERATURE_HH_
+#define IGNITION_MATH_TEMPERATURE_HH_
+
+#include <iostream>
+#include <memory>
+
+#include "ignition/math/Helpers.hh"
+
+namespace ignition
+{
+ namespace math
+ {
+ // Forward declare private data class.
+ class TemperaturePrivate;
+
+ /// \brief A class that stores temperature information, and allows
+ /// conversion between different units.
+ ///
+ /// This class is mostly for convenience. It can be used to easily
+ /// convert between temperature units and encapsulate temperature values.
+ ///
+ /// The default unit is Kelvin. Most functions that accept a double
+ /// value will assume the double is Kelvin. The exceptions are a few of
+ /// the conversion functions, such as CelsiusToFahrenheit. Similarly,
+ /// most doubles that are returned will be in Kelvin.
+ ///
+ /// ## Example usage ##
+ ///
+ /// ### Convert from Kelvin to Celsius ###
+ ///
+ /// double celsius = ignition::math::Temperature::KelvinToCelsius(2.5);
+ ///
+ /// ### Create and use a Temperature object ###
+ ///
+ /// ignition::math::Temperature temp(123.5);
+ /// std::cout << "Temperature in Kelvin = " << temp << std::endl;
+ /// std::cout << "Temperature in Celsius = "
+ /// << temp.Celsius() << std::endl;
+ ///
+ /// temp += 100.0;
+ /// std::cout << "Temperature + 100.0 = " << temp << "K" << std::endl;
+ ///
+ /// ignition::math::Temperature newTemp(temp);
+ /// newTemp += temp + 23.5;
+ /// std::cout << "Copied the temp object and added 23.5K. newTemp = "
+ /// << newTemp.Fahrenheit() << "F" << std::endl;
+ ///
+ class IGNITION_MATH_VISIBLE Temperature
+ {
+ /// \brief Default constructor
+ public: Temperature();
+
+ /// \brief Kelvin value constructor. This is a conversion constructor
+ /// \param[in] _temp Temperature in Kelvin
+ // cppcheck-suppress noExplicitConstructor
+ public: Temperature(const double _temp);
+
+ /// \brief Copy constructor
+ /// \param[in] _temp Temperature object to copy.
+ public: Temperature(const Temperature &_temp);
+
+ /// \brief Destructor
+ public: virtual ~Temperature();
+
+ /// \brief Convert Kelvin to Celsius
+ /// \param[in] _temp Temperature in Kelvin
+ /// \return Temperature in Celsius
+ public: static double KelvinToCelsius(const double _temp);
+
+ /// \brief Convert Kelvin to Fahrenheit
+ /// \param[in] _temp Temperature in Kelvin
+ /// \return Temperature in Fahrenheit
+ public: static double KelvinToFahrenheit(const double _temp);
+
+ /// \brief Convert Celsius to Fahrenheit
+ /// \param[in] _temp Temperature in Celsius
+ /// \return Temperature in Fahrenheit
+ public: static double CelsiusToFahrenheit(const double _temp);
+
+ /// \brief Convert Celsius to Kelvin
+ /// \param[in] _temp Temperature in Celsius
+ /// \return Temperature in Kelvin
+ public: static double CelsiusToKelvin(const double _temp);
+
+ /// \brief Convert Fahrenheit to Celsius
+ /// \param[in] _temp Temperature in Fahrenheit
+ /// \return Temperature in Celsius
+ public: static double FahrenheitToCelsius(const double _temp);
+
+ /// \brief Convert Fahrenheit to Kelvin
+ /// \param[in] _temp Temperature in Fahrenheit
+ /// \return Temperature in Kelvin
+ public: static double FahrenheitToKelvin(const double _temp);
+
+ /// \brief Set the temperature from a Kelvin value
+ /// \param[in] _temp Temperature in Kelvin
+ public: void SetKelvin(const double _temp);
+
+ /// \brief Set the temperature from a Celsius value
+ /// \param[in] _temp Temperature in Celsius
+ public: void SetCelsius(const double _temp);
+
+ /// \brief Set the temperature from a Fahrenheit value
+ /// \param[in] _temp Temperature in Fahrenheit
+ public: void SetFahrenheit(const double _temp);
+
+ /// \brief Get the temperature in Kelvin
+ /// \return Temperature in Kelvin
+ public: double Kelvin() const;
+
+ /// \brief Get the temperature in Celsius
+ /// \return Temperature in Celsius
+ public: double Celsius() const;
+
+ /// \brief Get the temperature in Fahrenheit
+ /// \return Temperature in Fahrenheit
+ public: double Fahrenheit() const;
+
+ /// \brief Accessor operator
+ /// \return Temperature in Kelvin
+ /// \sa Kelvin()
+ public: double operator()() const;
+
+ /// \brief Assignment operator
+ /// \param[in] _temp Temperature in Kelvin
+ /// \return Reference to this instance
+ public: Temperature &operator=(const double _temp);
+
+ /// \brief Assignment operator
+ /// \param[in] _temp Temperature object
+ /// \return Reference to this instance
+ public: Temperature &operator=(const Temperature &_temp);
+
+ /// \brief Addition operator
+ /// \param[in] _temp Temperature in Kelvin
+ /// \return Resulting temperature
+ public: Temperature operator+(const double _temp);
+
+ /// \brief Addition operator
+ /// \param[in] _temp Temperature object
+ /// \return Resulting temperature
+ public: Temperature operator+(const Temperature &_temp);
+
+ /// \brief Addition operator for double type.
+ /// \param[in] _t Temperature in kelvin
+ /// \param[in] _temp Temperature object
+ /// \return Resulting temperature
+ public: friend Temperature operator+(double _t, const Temperature &_temp)
+ {
+ return _t + _temp.Kelvin();
+ }
+
+ /// \brief Addition assignment operator
+ /// \param[in] _temp Temperature in Kelvin
+ /// \return Reference to this instance
+ public: const Temperature &operator+=(const double _temp);
+
+ /// \brief Addition assignment operator
+ /// \param[in] _temp Temperature object
+ /// \return Reference to this instance
+ public: const Temperature &operator+=(const Temperature &_temp);
+
+ /// \brief Subtraction operator
+ /// \param[in] _temp Temperature in Kelvin
+ /// \return Resulting temperature
+ public: Temperature operator-(const double _temp);
+
+ /// \brief Subtraction operator
+ /// \param[in] _temp Temperature object
+ /// \return Resulting temperature
+ public: Temperature operator-(const Temperature &_temp);
+
+ /// \brief Subtraction operator for double type.
+ /// \param[in] _t Temperature in kelvin
+ /// \param[in] _temp Temperature object
+ /// \return Resulting temperature
+ public: friend Temperature operator-(double _t, const Temperature &_temp)
+ {
+ return _t - _temp.Kelvin();
+ }
+
+ /// \brief Subtraction assignment operator
+ /// \param[in] _temp Temperature in Kelvin
+ /// \return Reference to this instance
+ public: const Temperature &operator-=(const double _temp);
+
+ /// \brief Subtraction assignment operator
+ /// \param[in] _temp Temperature object
+ /// \return Reference to this instance
+ public: const Temperature &operator-=(const Temperature &_temp);
+
+ /// \brief Multiplication operator
+ /// \param[in] _temp Temperature in Kelvin
+ /// \return Resulting temperature
+ public: Temperature operator*(const double _temp);
+
+ /// \brief Multiplication operator
+ /// \param[in] _temp Temperature object
+ /// \return Resulting temperature
+ public: Temperature operator*(const Temperature &_temp);
+
+ /// \brief Multiplication operator for double type.
+ /// \param[in] _t Temperature in kelvin
+ /// \param[in] _temp Temperature object
+ /// \return Resulting temperature
+ public: friend Temperature operator*(double _t, const Temperature &_temp)
+ {
+ return _t * _temp.Kelvin();
+ }
+
+ /// \brief Multiplication assignment operator
+ /// \param[in] _temp Temperature in Kelvin
+ /// \return Reference to this instance
+ public: const Temperature &operator*=(const double _temp);
+
+ /// \brief Multiplication assignment operator
+ /// \param[in] _temp Temperature object
+ /// \return Reference to this instance
+ public: const Temperature &operator*=(const Temperature &_temp);
+
+ /// \brief Division operator
+ /// \param[in] _temp Temperature in Kelvin
+ /// \return Resulting temperature
+ public: Temperature operator/(const double _temp);
+
+ /// \brief Division operator
+ /// \param[in] _temp Temperature object
+ /// \return Resulting temperature
+ public: Temperature operator/(const Temperature &_temp);
+
+ /// \brief Division operator for double type.
+ /// \param[in] _t Temperature in kelvin
+ /// \param[in] _temp Temperature object
+ /// \return Resulting temperature
+ public: friend Temperature operator/(double _t, const Temperature &_temp)
+ {
+ return _t / _temp.Kelvin();
+ }
+
+ /// \brief Division assignment operator
+ /// \param[in] _temp Temperature in Kelvin
+ /// \return Reference to this instance
+ public: const Temperature &operator/=(const double _temp);
+
+ /// \brief Division assignment operator
+ /// \param[in] _temp Temperature object
+ /// \return Reference to this instance
+ public: const Temperature &operator/=(const Temperature &_temp);
+
+ /// \brief Equal to operator
+ /// \param[in] _temp The temperature to compare
+ /// \return true if the temperatures are the same, false otherwise
+ public: bool operator==(const Temperature &_temp) const;
+
+ /// \brief Equal to operator, where the value of _temp is assumed to
+ /// be in Kelvin
+ /// \param[in] _temp The temperature (in Kelvin) to compare
+ /// \return true if the temperatures are the same, false otherwise
+ public: bool operator==(const double _temp) const;
+
+ /// \brief Inequality to operator
+ /// \param[in] _temp The temperature to compare
+ /// \return false if the temperatures are the same, true otherwise
+ public: bool operator!=(const Temperature &_temp) const;
+
+ /// \brief Inequality to operator, where the value of _temp is assumed to
+ /// be in Kelvin
+ /// \param[in] _temp The temperature (in Kelvin) to compare
+ /// \return false if the temperatures are the same, true otherwise
+ public: bool operator!=(const double _temp) const;
+
+ /// \brief Less than to operator
+ /// \param[in] _temp The temperature to compare
+ /// \return True if this is less than _temp.
+ public: bool operator<(const Temperature &_temp) const;
+
+ /// \brief Less than operator, where the value of _temp is assumed to
+ /// be in Kelvin
+ /// \param[in] _temp The temperature (in Kelvin) to compare
+ /// \return True if this is less than _temp.
+ public: bool operator<(const double _temp) const;
+
+ /// \brief Less than or equal to operator
+ /// \param[in] _temp The temperature to compare
+ /// \return True if this is less than or equal _temp.
+ public: bool operator<=(const Temperature &_temp) const;
+
+ /// \brief Less than or equal operator,
+ /// where the value of _temp is assumed to be in Kelvin
+ /// \param[in] _temp The temperature (in Kelvin) to compare
+ /// \return True if this is less than or equal to _temp.
+ public: bool operator<=(const double _temp) const;
+
+ /// \brief Greater than operator
+ /// \param[in] _temp The temperature to compare
+ /// \return True if this is greater than _temp.
+ public: bool operator>(const Temperature &_temp) const;
+
+ /// \brief Greater than operator, where the value of _temp is assumed to
+ /// be in Kelvin
+ /// \param[in] _temp The temperature (in Kelvin) to compare
+ /// \return True if this is greater than _temp.
+ public: bool operator>(const double _temp) const;
+
+ /// \brief Greater than or equal to operator
+ /// \param[in] _temp The temperature to compare
+ /// \return True if this is greater than or equal to _temp.
+ public: bool operator>=(const Temperature &_temp) const;
+
+ /// \brief Greater than equal operator,
+ /// where the value of _temp is assumed to be in Kelvin
+ /// \param[in] _temp The temperature (in Kelvin) to compare
+ /// \return True if this is greater than or equal to _temp.
+ public: bool operator>=(const double _temp) const;
+
+ /// \brief Stream insertion operator
+ /// \param[in] _out the output stream
+ /// \param[in] _temp Temperature to write to the stream
+ /// \return the output stream
+ public: friend std::ostream &operator<<(std::ostream &_out,
+ const ignition::math::Temperature &_temp)
+ {
+ _out << _temp.Kelvin();
+ return _out;
+ }
+
+ /// \brief Stream extraction operator
+ /// \param[in] _in the input stream
+ /// \param[in] _temp Temperature to read from to the stream. Assumes
+ /// temperature value is in Kelvin.
+ /// \return the input stream
+ public: friend std::istream &operator>>(std::istream &_in,
+ ignition::math::Temperature &_temp)
+ {
+ // Skip white spaces
+ _in.setf(std::ios_base::skipws);
+
+ double kelvin;
+ _in >> kelvin;
+
+ _temp.SetKelvin(kelvin);
+ return _in;
+ }
+
+#ifdef _WIN32
+// Disable warning C4251 which is triggered by
+// std::unique_ptr
+#pragma warning(push)
+#pragma warning(disable: 4251)
+#endif
+ /// \brief Private data pointer.
+ private: std::unique_ptr<TemperaturePrivate> dataPtr;
+#ifdef _WIN32
+#pragma warning(pop)
+#endif
+ };
+ }
+}
+#endif
diff --git a/include/ignition/math/Triangle.hh b/include/ignition/math/Triangle.hh
new file mode 100644
index 0000000..ce27979
--- /dev/null
+++ b/include/ignition/math/Triangle.hh
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2014 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_TRIANGLE_HH_
+#define IGNITION_MATH_TRIANGLE_HH_
+
+#include <set>
+#include <ignition/math/Helpers.hh>
+#include <ignition/math/Line2.hh>
+#include <ignition/math/Vector2.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class Triangle Triangle.hh ignition/math/Triangle.hh
+ /// \brief Triangle class and related functions.
+ template<typename T>
+ class Triangle
+ {
+ /// \brief Default constructor
+ public: Triangle() = default;
+
+ /// \brief Constructor
+ /// \param[in] _pt1 First point that defines the triangle.
+ /// \param[in] _pt2 Second point that defines the triangle.
+ /// \param[in] _pt3 Third point that defines the triangle.
+ public: Triangle(const math::Vector2<T> &_pt1,
+ const math::Vector2<T> &_pt2,
+ const math::Vector2<T> &_pt3)
+ {
+ this->Set(_pt1, _pt2, _pt3);
+ }
+
+ /// \brief Set one vertex of the triangle.
+ /// \param[in] _index Index of the point to set, where
+ /// 0 == first vertex, 1 == second vertex, and 2 == third vertex.
+ /// The index is clamped to the range [0, 2].
+ /// \param[in] _pt Value of the point to set.
+ public: void Set(const unsigned int _index, const math::Vector2<T> &_pt)
+ {
+ this->pts[clamp(_index, 0u, 2u)] = _pt;
+ }
+
+ /// \brief Set all vertices of the triangle.
+ /// \param[in] _pt1 First point that defines the triangle.
+ /// \param[in] _pt2 Second point that defines the triangle.
+ /// \param[in] _pt3 Third point that defines the triangle.
+ public: void Set(const math::Vector2<T> &_pt1,
+ const math::Vector2<T> &_pt2,
+ const math::Vector2<T> &_pt3)
+ {
+ this->pts[0] = _pt1;
+ this->pts[1] = _pt2;
+ this->pts[2] = _pt3;
+ }
+
+ /// \brief Get whether this triangle is valid, based on triangle
+ /// inequality: the sum of the lengths of any two sides must be greater
+ /// than the length of the remaining side.
+ /// \return True if the triangle inequality holds
+ public: bool Valid() const
+ {
+ T a = this->Side(0).Length();
+ T b = this->Side(1).Length();
+ T c = this->Side(2).Length();
+ return (a+b) > c && (b+c) > a && (c+a) > b;
+ }
+
+ /// \brief Get a line segment for one side of the triangle.
+ /// \param[in] _index Index of the side to retreive, where
+ /// 0 == Line2(pt1, pt2),
+ /// 1 == Line2(pt2, pt3),
+ /// 2 == Line2(pt3, pt1)
+ /// The index is clamped to the range [0, 2]
+ /// \return Line segment of the requested side.
+ public: Line2<T> Side(const unsigned int _index) const
+ {
+ if (_index == 0)
+ return Line2<T>(this->pts[0], this->pts[1]);
+ else if (_index == 1)
+ return Line2<T>(this->pts[1], this->pts[2]);
+ else
+ return Line2<T>(this->pts[2], this->pts[0]);
+ }
+
+ /// \brief Check if this triangle completely contains the given line
+ /// segment.
+ /// \param[in] _line Line to check.
+ /// \return True if the line's start and end points are both inside
+ /// this triangle.
+ public: bool Contains(const Line2<T> &_line) const
+ {
+ return this->Contains(_line[0]) && this->Contains(_line[1]);
+ }
+
+ /// \brief Get whether this triangle contains the given point.
+ /// \param[in] _pt Point to check.
+ /// \return True if the point is inside or on the triangle.
+ public: bool Contains(const math::Vector2<T> &_pt) const
+ {
+ // Compute vectors
+ math::Vector2<T> v0 = this->pts[2] -this->pts[0];
+ math::Vector2<T> v1 = this->pts[1] -this->pts[0];
+ math::Vector2<T> v2 = _pt - this->pts[0];
+
+ // Compute dot products
+ double dot00 = v0.Dot(v0);
+ double dot01 = v0.Dot(v1);
+ double dot02 = v0.Dot(v2);
+ double dot11 = v1.Dot(v1);
+ double dot12 = v1.Dot(v2);
+
+ // Compute barycentric coordinates
+ double invDenom = 1.0 / (dot00 * dot11 - dot01 * dot01);
+ double u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+ double v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+ // Check if point is in triangle
+ return (u >= 0) && (v >= 0) && (u + v <= 1);
+ }
+
+ /// \brief Get whether the given line intersects this triangle.
+ /// \param[in] _line Line to check.
+ /// \param[out] _ipt1 Return value of the first intersection point,
+ /// only valid if the return value of the function is true.
+ /// \param[out] _ipt2 Return value of the second intersection point,
+ /// only valid if the return value of the function is true.
+ /// \return True if the given line intersects this triangle.
+ public: bool Intersects(const Line2<T> &_line,
+ math::Vector2<T> &_ipt1,
+ math::Vector2<T> &_ipt2) const
+ {
+ if (this->Contains(_line))
+ {
+ _ipt1 = _line[0];
+ _ipt2 = _line[1];
+ return true;
+ }
+
+ Line2<T> line1(this->pts[0], this->pts[1]);
+ Line2<T> line2(this->pts[1], this->pts[2]);
+ Line2<T> line3(this->pts[2], this->pts[0]);
+
+ math::Vector2<T> pt;
+ std::set<math::Vector2<T> > points;
+
+ if (line1.Intersect(_line, pt))
+ points.insert(pt);
+
+ if (line2.Intersect(_line, pt))
+ points.insert(pt);
+
+ if (line3.Intersect(_line, pt))
+ points.insert(pt);
+
+ if (points.empty())
+ {
+ return false;
+ }
+ else if (points.size() == 1)
+ {
+ auto iter = points.begin();
+
+ _ipt1 = *iter;
+ if (this->Contains(_line[0]))
+ _ipt2 = _line[0];
+ else
+ {
+ _ipt2 = _line[1];
+ }
+ }
+ else
+ {
+ auto iter = points.begin();
+ _ipt1 = *(iter++);
+ _ipt2 = *iter;
+ }
+
+ return true;
+ }
+
+ /// \brief Get the length of the triangle's perimeter.
+ /// \return Sum of the triangle's line segments.
+ public: T Perimeter() const
+ {
+ return this->Side(0).Length() + this->Side(1).Length() +
+ this->Side(2).Length();
+ }
+
+ /// \brief Get the area of this triangle.
+ /// \return Triangle's area.
+ public: double Area() const
+ {
+ double s = this->Perimeter() / 2.0;
+ T a = this->Side(0).Length();
+ T b = this->Side(1).Length();
+ T c = this->Side(2).Length();
+
+ // Heron's formula
+ // http://en.wikipedia.org/wiki/Heron%27s_formula
+ return sqrt(s * (s-a) * (s-b) * (s-c));
+ }
+
+ /// \brief Get one of points that define the triangle.
+ /// \param[in] _index The index, where 0 == first vertex,
+ /// 1 == second vertex, and 2 == third vertex.
+ /// The index is clamped to the range [0, 2]
+ /// \return The point specified by _index.
+ public: math::Vector2<T> operator[](const size_t _index) const
+ {
+ return this->pts[clamp(_index, IGN_ZERO_SIZE_T, IGN_TWO_SIZE_T)];
+ }
+
+ /// The points of the triangle
+ private: math::Vector2<T> pts[3];
+ };
+
+ /// Integer specialization of the Triangle class.
+ typedef Triangle<int> Trianglei;
+
+ /// Double specialization of the Triangle class.
+ typedef Triangle<double> Triangled;
+
+ /// Float specialization of the Triangle class.
+ typedef Triangle<float> Trianglef;
+ }
+}
+#endif
diff --git a/include/ignition/math/Triangle3.hh b/include/ignition/math/Triangle3.hh
new file mode 100644
index 0000000..12a02af
--- /dev/null
+++ b/include/ignition/math/Triangle3.hh
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2016 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_TRIANGLE3_HH_
+#define IGNITION_MATH_TRIANGLE3_HH_
+
+#include <ignition/math/Helpers.hh>
+#include <ignition/math/Line3.hh>
+#include <ignition/math/Plane.hh>
+#include <ignition/math/Vector3.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class Triangle3 Triangle3.hh ignition/math/Triangle3.hh
+ /// \brief A 3-dimensional triangle and related functions.
+ template<typename T>
+ class Triangle3
+ {
+ /// \brief Default constructor
+ public: Triangle3() = default;
+
+ /// \brief Constructor.
+ ///
+ /// Keep in mind that the triangle normal
+ /// is determined by the order of these vertices. Search
+ /// the internet for "triangle winding" for more information.
+ /// \param[in] _pt1 First point that defines the triangle.
+ /// \param[in] _pt2 Second point that defines the triangle.
+ /// \param[in] _pt3 Third point that defines the triangle.
+ public: Triangle3(const Vector3<T> &_pt1,
+ const Vector3<T> &_pt2,
+ const Vector3<T> &_pt3)
+ {
+ this->Set(_pt1, _pt2, _pt3);
+ }
+
+ /// \brief Set one vertex of the triangle.
+ ///
+ /// Keep in mind that the triangle normal
+ /// is determined by the order of these vertices. Search
+ /// the internet for "triangle winding" for more information.
+ ///
+ /// \param[in] _index Index of the point to set. _index is clamped
+ /// to the range [0,2].
+ /// \param[in] _pt Value of the point to set.
+ public: void Set(const unsigned int _index, const Vector3<T> &_pt)
+ {
+ this->pts[clamp(_index, 0u, 2u)] = _pt;
+ }
+
+ /// \brief Set all vertices of the triangle.
+ ///
+ /// Keep in mind that the triangle normal
+ /// is determined by the order of these vertices. Search
+ /// the internet for "triangle winding" for more information.
+ ///
+ /// \param[in] _pt1 First point that defines the triangle.
+ /// \param[in] _pt2 Second point that defines the triangle.
+ /// \param[in] _pt3 Third point that defines the triangle.
+ public: void Set(const Vector3<T> &_pt1,
+ const Vector3<T> &_pt2,
+ const Vector3<T> &_pt3)
+ {
+ this->pts[0] = _pt1;
+ this->pts[1] = _pt2;
+ this->pts[2] = _pt3;
+ }
+
+ /// \brief Get whether this triangle is valid, based on triangle
+ /// inequality: the sum of the lengths of any two sides must be greater
+ /// than the length of the remaining side.
+ /// \return True if the triangle inequality holds
+ public: bool Valid() const
+ {
+ T a = this->Side(0).Length();
+ T b = this->Side(1).Length();
+ T c = this->Side(2).Length();
+ return (a+b) > c && (b+c) > a && (c+a) > b;
+ }
+
+ /// \brief Get a line segment for one side of the triangle.
+ /// \param[in] _index Index of the side to retrieve, where
+ /// 0 == Line3(pt1, pt2),
+ /// 1 == Line3(pt2, pt3),
+ /// 2 == Line3(pt3, pt1).
+ /// _index is clamped to the range [0,2].
+ /// \return Line segment of the requested side.
+ public: Line3<T> Side(const unsigned int _index) const
+ {
+ if (_index == 0)
+ return Line3<T>(this->pts[0], this->pts[1]);
+ else if (_index == 1)
+ return Line3<T>(this->pts[1], this->pts[2]);
+ else
+ return Line3<T>(this->pts[2], this->pts[0]);
+ }
+
+ /// \brief Check if this triangle completely contains the given line
+ /// segment.
+ /// \param[in] _line Line to check.
+ /// \return True if the line's start and end points are both inside
+ /// this triangle.
+ public: bool Contains(const Line3<T> &_line) const
+ {
+ return this->Contains(_line[0]) && this->Contains(_line[1]);
+ }
+
+ /// \brief Get whether this triangle contains the given point.
+ /// \param[in] _pt Point to check.
+ /// \return True if the point is inside or on the triangle.
+ public: bool Contains(const Vector3<T> &_pt) const
+ {
+ // Make sure the point is on the same plane as the triangle
+ if (Planed(this->Normal()).Side(_pt) == Planed::NO_SIDE)
+ {
+ Vector3d v0 = this->pts[2] - this->pts[0];
+ Vector3d v1 = this->pts[1] - this->pts[0];
+ Vector3d v2 = _pt - this->pts[0];
+
+ double dot00 = v0.Dot(v0);
+ double dot01 = v0.Dot(v1);
+ double dot02 = v0.Dot(v2);
+ double dot11 = v1.Dot(v1);
+ double dot12 = v1.Dot(v2);
+
+ // Compute barycentric coordinates
+ double invDenom = 1.0 / (dot00 * dot11 - dot01 * dot01);
+ double u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+ double v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+ // Check if point is in triangle
+ return (u >= 0) && (v >= 0) && (u + v <= 1);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /// \brief Get the triangle's normal vector.
+ /// \return The normal vector for the triangle.
+ public: Vector3d Normal() const
+ {
+ return Vector3d::Normal(this->pts[0], this->pts[1], this->pts[2]);
+ }
+
+ /// \brief Get whether the given line intersects an edge of this triangle.
+ ///
+ /// The returned intersection point is one of:
+ ///
+ /// * If the line is coplanar with the triangle:
+ /// * The point on the closest edge of the triangle that the line
+ /// intersects.
+ /// OR
+ /// * The first point on the line, if the line is completely contained
+ /// * If the line is not coplanar, the point on the triangle that the
+ /// line intersects.
+ ///
+ /// \param[in] _line Line to check.
+ /// \param[out] _ipt1 Return value of the first intersection point,
+ /// only valid if the return value of the function is true.
+ /// \return True if the given line intersects this triangle.
+ public: bool Intersects(const Line3<T> &_line, Vector3<T> &_ipt1) const
+ {
+ // Triangle normal
+ Vector3d norm = this->Normal();
+
+ // Ray direction to intersect with triangle
+ Vector3d dir = (_line[1] - _line[0]).Normalize();
+
+ double denom = norm.Dot(dir);
+
+ // Handle the case when the line is not co-planar with the triangle
+ if (!math::equal(denom, 0.0))
+ {
+ // Distance from line start to triangle intersection
+ double intersection =
+ -norm.Dot(_line[0] - this->pts[0]) / denom;
+
+ // Make sure the ray intersects the triangle
+ if (intersection < 1.0 || intersection > _line.Length())
+ return false;
+
+ // Return point of intersection
+ _ipt1 = _line[0] + (dir * intersection);
+
+ return true;
+ }
+ // Line co-planar with triangle
+ else
+ {
+ // If the line is completely inside the triangle
+ if (this->Contains(_line))
+ {
+ _ipt1 = _line[0];
+ return true;
+ }
+ // If the line intersects the first side
+ else if (_line.Intersect(this->Side(0), _ipt1))
+ {
+ return true;
+ }
+ // If the line intersects the second side
+ else if (_line.Intersect(this->Side(1), _ipt1))
+ {
+ return true;
+ }
+ // If the line intersects the third side
+ else if (_line.Intersect(this->Side(2), _ipt1))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /// \brief Get the length of the triangle's perimeter.
+ /// \return Sum of the triangle's line segments.
+ public: T Perimeter() const
+ {
+ return this->Side(0).Length() + this->Side(1).Length() +
+ this->Side(2).Length();
+ }
+
+ /// \brief Get the area of this triangle.
+ /// \return Triangle's area.
+ public: double Area() const
+ {
+ double s = this->Perimeter() / 2.0;
+ T a = this->Side(0).Length();
+ T b = this->Side(1).Length();
+ T c = this->Side(2).Length();
+
+ // Heron's formula
+ // http://en.wikipedia.org/wiki/Heron%27s_formula
+ return sqrt(s * (s-a) * (s-b) * (s-c));
+ }
+
+ /// \brief Get one of points that define the triangle.
+ /// \param[in] _index: 0, 1, or 2. _index is clamped to the range
+ /// [0,2].
+ /// \return The triangle point at _index.
+ public: Vector3<T> operator[](const size_t _index) const
+ {
+ return this->pts[clamp(_index, IGN_ZERO_SIZE_T, IGN_TWO_SIZE_T)];
+ }
+
+ /// The points of the triangle
+ private: Vector3<T> pts[3];
+ };
+
+ /// \brief Integer specialization of the Triangle class.
+ typedef Triangle3<int> Triangle3i;
+
+ /// \brief Double specialization of the Triangle class.
+ typedef Triangle3<double> Triangle3d;
+
+ /// \brief Float specialization of the Triangle class.
+ typedef Triangle3<float> Triangle3f;
+ }
+}
+#endif
diff --git a/include/ignition/math/Vector2.hh b/include/ignition/math/Vector2.hh
new file mode 100644
index 0000000..96a27aa
--- /dev/null
+++ b/include/ignition/math/Vector2.hh
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_VECTOR2_HH_
+#define IGNITION_MATH_VECTOR2_HH_
+
+#include <ignition/math/Helpers.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class Vector2 Vector2.hh ignition/math/Vector2.hh
+ /// \brief Two dimensional (x, y) vector.
+ template<typename T>
+ class Vector2
+ {
+ /// \brief math::Vector2(0, 0)
+ public: static const Vector2<T> Zero;
+
+ /// \brief math::Vector2(1, 1)
+ public: static const Vector2<T> One;
+
+ /// \brief Default Constructor
+ public: Vector2()
+ {
+ this->data[0] = 0;
+ this->data[1] = 0;
+ }
+
+ /// \brief Constructor
+ /// \param[in] _x value along x
+ /// \param[in] _y value along y
+ public: Vector2(const T &_x, const T &_y)
+ {
+ this->data[0] = _x;
+ this->data[1] = _y;
+ }
+
+ /// \brief Copy constructor
+ /// \param[in] _v the value
+ public: Vector2(const Vector2<T> &_v)
+ {
+ this->data[0] = _v[0];
+ this->data[1] = _v[1];
+ }
+
+ /// \brief Destructor
+ public: virtual ~Vector2() {}
+
+ /// \brief Calc distance to the given point
+ /// \param[in] _pt The point to measure to
+ /// \return the distance
+ public: double Distance(const Vector2 &_pt) const
+ {
+ return sqrt((this->data[0]-_pt[0])*(this->data[0]-_pt[0]) +
+ (this->data[1]-_pt[1])*(this->data[1]-_pt[1]));
+ }
+
+ /// \brief Returns the length (magnitude) of the vector
+ /// \return The length
+ public: T Length() const
+ {
+ return sqrt(this->SquaredLength());
+ }
+
+ /// \brief Returns the square of the length (magnitude) of the vector
+ /// \return The squared length
+ public: T SquaredLength() const
+ {
+ return std::pow(this->data[0], 2)
+ + std::pow(this->data[1], 2);
+ }
+
+ /// \brief Normalize the vector length
+ public: void Normalize()
+ {
+ double d = this->Length();
+
+ if (!equal<T>(d, static_cast<T>(0.0)))
+ {
+ this->data[0] /= d;
+ this->data[1] /= d;
+ }
+ }
+
+ /// \brief Set the contents of the vector
+ /// \param[in] _x value along x
+ /// \param[in] _y value along y
+ public: void Set(T _x, T _y)
+ {
+ this->data[0] = _x;
+ this->data[1] = _y;
+ }
+
+ /// \brief Get the dot product of this vector and _v
+ /// \param[in] _v the vector
+ /// \return The dot product
+ public: T Dot(const Vector2<T> &_v) const
+ {
+ return (this->data[0] * _v[0]) + (this->data[1] * _v[1]);
+ }
+
+ /// \brief Assignment operator
+ /// \param[in] _v a value for x and y element
+ /// \return this
+ public: Vector2 &operator=(const Vector2 &_v)
+ {
+ this->data[0] = _v[0];
+ this->data[1] = _v[1];
+
+ return *this;
+ }
+
+ /// \brief Assignment operator
+ /// \param[in] _v the value for x and y element
+ /// \return this
+ public: const Vector2 &operator=(T _v)
+ {
+ this->data[0] = _v;
+ this->data[1] = _v;
+
+ return *this;
+ }
+
+ /// \brief Addition operator
+ /// \param[in] _v vector to add
+ /// \return sum vector
+ public: Vector2 operator+(const Vector2 &_v) const
+ {
+ return Vector2(this->data[0] + _v[0], this->data[1] + _v[1]);
+ }
+
+ /// \brief Addition assignment operator
+ /// \param[in] _v the vector to add
+ // \return this
+ public: const Vector2 &operator+=(const Vector2 &_v)
+ {
+ this->data[0] += _v[0];
+ this->data[1] += _v[1];
+
+ return *this;
+ }
+
+ /// \brief Addition operators
+ /// \param[in] _s the scalar addend
+ /// \return sum vector
+ public: inline Vector2<T> operator+(const T _s) const
+ {
+ return Vector2<T>(this->data[0] + _s,
+ this->data[1] + _s);
+ }
+
+ /// \brief Addition operators
+ /// \param[in] _s the scalar addend
+ /// \param[in] _v input vector
+ /// \return sum vector
+ public: friend inline Vector2<T> operator+(const T _s,
+ const Vector2<T> &_v)
+ {
+ return _v + _s;
+ }
+
+ /// \brief Addition assignment operator
+ /// \param[in] _s scalar addend
+ /// \return this
+ public: const Vector2<T> &operator+=(const T _s)
+ {
+ this->data[0] += _s;
+ this->data[1] += _s;
+
+ return *this;
+ }
+
+ /// \brief Negation operator
+ /// \return negative of this vector
+ public: inline Vector2 operator-() const
+ {
+ return Vector2(-this->data[0], -this->data[1]);
+ }
+
+ /// \brief Subtraction operator
+ /// \param[in] _v the vector to substract
+ /// \return the subtracted vector
+ public: Vector2 operator-(const Vector2 &_v) const
+ {
+ return Vector2(this->data[0] - _v[0], this->data[1] - _v[1]);
+ }
+
+ /// \brief Subtraction assignment operator
+ /// \param[in] _v the vector to substract
+ /// \return this
+ public: const Vector2 &operator-=(const Vector2 &_v)
+ {
+ this->data[0] -= _v[0];
+ this->data[1] -= _v[1];
+
+ return *this;
+ }
+
+ /// \brief Subtraction operators
+ /// \param[in] _s the scalar subtrahend
+ /// \return difference vector
+ public: inline Vector2<T> operator-(const T _s) const
+ {
+ return Vector2<T>(this->data[0] - _s,
+ this->data[1] - _s);
+ }
+
+ /// \brief Subtraction operators
+ /// \param[in] _s the scalar minuend
+ /// \param[in] _v vector subtrahend
+ /// \return difference vector
+ public: friend inline Vector2<T> operator-(const T _s,
+ const Vector2<T> &_v)
+ {
+ return {_s - _v.X(), _s - _v.Y()};
+ }
+
+ /// \brief Subtraction assignment operator
+ /// \param[in] _s scalar subtrahend
+ /// \return this
+ public: const Vector2<T> &operator-=(T _s)
+ {
+ this->data[0] -= _s;
+ this->data[1] -= _s;
+
+ return *this;
+ }
+
+ /// \brief Division operator
+ /// \remarks this is an element wise division
+ /// \param[in] _v a vector
+ /// \result a result
+ public: const Vector2 operator/(const Vector2 &_v) const
+ {
+ return Vector2(this->data[0] / _v[0], this->data[1] / _v[1]);
+ }
+
+ /// \brief Division operator
+ /// \remarks this is an element wise division
+ /// \param[in] _v a vector
+ /// \return this
+ public: const Vector2 &operator/=(const Vector2 &_v)
+ {
+ this->data[0] /= _v[0];
+ this->data[1] /= _v[1];
+
+ return *this;
+ }
+
+ /// \brief Division operator
+ /// \param[in] _v the value
+ /// \return a vector
+ public: const Vector2 operator/(T _v) const
+ {
+ return Vector2(this->data[0] / _v, this->data[1] / _v);
+ }
+
+ /// \brief Division operator
+ /// \param[in] _v the divisor
+ /// \return a vector
+ public: const Vector2 &operator/=(T _v)
+ {
+ this->data[0] /= _v;
+ this->data[1] /= _v;
+
+ return *this;
+ }
+
+ /// \brief Multiplication operators
+ /// \param[in] _v the vector
+ /// \return the result
+ public: const Vector2 operator*(const Vector2 &_v) const
+ {
+ return Vector2(this->data[0] * _v[0], this->data[1] * _v[1]);
+ }
+
+ /// \brief Multiplication assignment operator
+ /// \remarks this is an element wise multiplication
+ /// \param[in] _v the vector
+ /// \return this
+ public: const Vector2 &operator*=(const Vector2 &_v)
+ {
+ this->data[0] *= _v[0];
+ this->data[1] *= _v[1];
+
+ return *this;
+ }
+
+ /// \brief Multiplication operators
+ /// \param[in] _v the scaling factor
+ /// \return a scaled vector
+ public: const Vector2 operator*(T _v) const
+ {
+ return Vector2(this->data[0] * _v, this->data[1] * _v);
+ }
+
+ /// \brief Scalar left multiplication operators
+ /// \param[in] _s the scaling factor
+ /// \param[in] _v the vector to scale
+ /// \return a scaled vector
+ public: friend inline const Vector2 operator*(const T _s,
+ const Vector2 &_v)
+ {
+ return Vector2(_v * _s);
+ }
+
+ /// \brief Multiplication assignment operator
+ /// \param[in] _v the scaling factor
+ /// \return a scaled vector
+ public: const Vector2 &operator*=(T _v)
+ {
+ this->data[0] *= _v;
+ this->data[1] *= _v;
+
+ return *this;
+ }
+
+ /// \brief Equality test with tolerance.
+ /// \param[in] _v the vector to compare to
+ /// \param[in] _tol equality tolerance.
+ /// \return true if the elements of the vectors are equal within
+ /// the tolerence specified by _tol.
+ public: bool Equal(const Vector2 &_v, const T &_tol) const
+ {
+ return equal<T>(this->data[0], _v[0], _tol)
+ && equal<T>(this->data[1], _v[1], _tol);
+ }
+
+ /// \brief Equal to operator
+ /// \param[in] _v the vector to compare to
+ /// \return true if the elements of the 2 vectors are equal within
+ /// a tolerence (1e-6)
+ public: bool operator==(const Vector2 &_v) const
+ {
+ return this->Equal(_v, static_cast<T>(1e-6));
+ }
+
+ /// \brief Not equal to operator
+ /// \return true if elements are of diffent values (tolerence 1e-6)
+ public: bool operator!=(const Vector2 &_v) const
+ {
+ return !(*this == _v);
+ }
+
+ /// \brief See if a point is finite (e.g., not nan)
+ /// \return true if finite, false otherwise
+ public: bool IsFinite() const
+ {
+ // std::isfinite works with floating point values,
+ // need to explicit cast to avoid ambiguity in vc++.
+ return std::isfinite(static_cast<double>(this->data[0])) &&
+ std::isfinite(static_cast<double>(this->data[1]));
+ }
+
+ /// \brief Array subscript operator
+ /// \param[in] _index The index, where 0 == x and 1 == y.
+ /// The index is clamped to the range [0,1].
+ public: T &operator[](const std::size_t _index)
+ {
+ return this->data[clamp(_index, IGN_ZERO_SIZE_T, IGN_ONE_SIZE_T)];
+ }
+
+ /// \brief Const-qualified array subscript operator
+ /// \param[in] _index The index, where 0 == x and 1 == y.
+ /// The index is clamped to the range [0,1].
+ public: T operator[](const std::size_t _index) const
+ {
+ return this->data[clamp(_index, IGN_ZERO_SIZE_T, IGN_ONE_SIZE_T)];
+ }
+
+ /// \brief Return the x value.
+ /// \return Value of the X component.
+ public: inline T X() const
+ {
+ return this->data[0];
+ }
+
+ /// \brief Return the y value.
+ /// \return Value of the Y component.
+ public: inline T Y() const
+ {
+ return this->data[1];
+ }
+
+ /// \brief Return a mutable x value.
+ /// \return Value of the X component.
+ public: inline T &X()
+ {
+ return this->data[0];
+ }
+
+ /// \brief Return a mutable y value.
+ /// \return Value of the Y component.
+ public: inline T &Y()
+ {
+ return this->data[1];
+ }
+
+ /// \brief Set the x value.
+ /// \param[in] _v Value for the x component.
+ public: inline void X(const T &_v)
+ {
+ this->data[0] = _v;
+ }
+
+ /// \brief Set the y value.
+ /// \param[in] _v Value for the y component.
+ public: inline void Y(const T &_v)
+ {
+ this->data[1] = _v;
+ }
+
+ /// \brief Stream extraction operator
+ /// \param[in] _out output stream
+ /// \param[in] _pt Vector2 to output
+ /// \return The stream
+ public: friend std::ostream
+ &operator<<(std::ostream &_out, const Vector2<T> &_pt)
+ {
+ _out << _pt[0] << " " << _pt[1];
+ return _out;
+ }
+
+ /// \brief Less than operator.
+ /// \param[in] _pt Vector to compare.
+ /// \return True if this vector's first or second value is less than
+ /// the given vector's first or second value.
+ public: bool operator<(const Vector2<T> &_pt) const
+ {
+ return this->data[0] < _pt[0] || this->data[1] < _pt[1];
+ }
+
+ /// \brief Stream extraction operator
+ /// \param[in] _in input stream
+ /// \param[in] _pt Vector2 to read values into
+ /// \return The stream
+ public: friend std::istream
+ &operator>>(std::istream &_in, Vector2<T> &_pt)
+ {
+ T x, y;
+ // Skip white spaces
+ _in.setf(std::ios_base::skipws);
+ _in >> x >> y;
+ _pt.Set(x, y);
+ return _in;
+ }
+
+ /// \brief The x and y values.
+ private: T data[2];
+ };
+
+ template<typename T>
+ const Vector2<T> Vector2<T>::Zero(0, 0);
+
+ template<typename T>
+ const Vector2<T> Vector2<T>::One(1, 1);
+
+ typedef Vector2<int> Vector2i;
+ typedef Vector2<double> Vector2d;
+ typedef Vector2<float> Vector2f;
+ }
+}
+#endif
diff --git a/include/ignition/math/Vector3.hh b/include/ignition/math/Vector3.hh
new file mode 100644
index 0000000..698bce0
--- /dev/null
+++ b/include/ignition/math/Vector3.hh
@@ -0,0 +1,757 @@
+/*
+ * Copyright (C) 2014 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_VECTOR3_HH_
+#define IGNITION_MATH_VECTOR3_HH_
+
+#include <iostream>
+#include <fstream>
+#include <cmath>
+#include <algorithm>
+
+#include <ignition/math/Helpers.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class Vector3 Vector3.hh ignition/math/Vector3.hh
+ /// \brief The Vector3 class represents the generic vector containing 3
+ /// elements. Since it's commonly used to keep coordinate system
+ /// related information, its elements are labeled by x, y, z.
+ template<typename T>
+ class Vector3
+ {
+ /// \brief math::Vector3(0, 0, 0)
+ public: static const Vector3 Zero;
+
+ /// \brief math::Vector3(1, 1, 1)
+ public: static const Vector3 One;
+
+ /// \brief math::Vector3(1, 0, 0)
+ public: static const Vector3 UnitX;
+
+ /// \brief math::Vector3(0, 1, 0)
+ public: static const Vector3 UnitY;
+
+ /// \brief math::Vector3(0, 0, 1)
+ public: static const Vector3 UnitZ;
+
+ /// \brief Constructor
+ public: Vector3()
+ {
+ this->data[0] = 0;
+ this->data[1] = 0;
+ this->data[2] = 0;
+ }
+
+ /// \brief Constructor
+ /// \param[in] _x value along x
+ /// \param[in] _y value along y
+ /// \param[in] _z value along z
+ public: Vector3(const T &_x, const T &_y, const T &_z)
+ {
+ this->data[0] = _x;
+ this->data[1] = _y;
+ this->data[2] = _z;
+ }
+
+ /// \brief Copy constructor
+ /// \param[in] _v a vector
+ public: Vector3(const Vector3<T> &_v)
+ {
+ this->data[0] = _v[0];
+ this->data[1] = _v[1];
+ this->data[2] = _v[2];
+ }
+
+ /// \brief Destructor
+ public: virtual ~Vector3() {}
+
+ /// \brief Return the sum of the values
+ /// \return the sum
+ public: T Sum() const
+ {
+ return this->data[0] + this->data[1] + this->data[2];
+ }
+
+ /// \brief Calc distance to the given point
+ /// \param[in] _pt the point
+ /// \return the distance
+ public: T Distance(const Vector3<T> &_pt) const
+ {
+ return sqrt((this->data[0]-_pt[0])*(this->data[0]-_pt[0]) +
+ (this->data[1]-_pt[1])*(this->data[1]-_pt[1]) +
+ (this->data[2]-_pt[2])*(this->data[2]-_pt[2]));
+ }
+
+ /// \brief Calc distance to the given point
+ /// \param[in] _x value along x
+ /// \param[in] _y value along y
+ /// \param[in] _z value along z
+ /// \return the distance
+ public: T Distance(T _x, T _y, T _z) const
+ {
+ return this->Distance(Vector3(_x, _y, _z));
+ }
+
+ /// \brief Returns the length (magnitude) of the vector
+ /// \return the length
+ public: T Length() const
+ {
+ return sqrt(this->SquaredLength());
+ }
+
+ /// \brief Return the square of the length (magnitude) of the vector
+ /// \return the squared length
+ public: T SquaredLength() const
+ {
+ return std::pow(this->data[0], 2)
+ + std::pow(this->data[1], 2)
+ + std::pow(this->data[2], 2);
+ }
+
+ /// \brief Normalize the vector length
+ /// \return unit length vector
+ public: Vector3 Normalize()
+ {
+ T d = this->Length();
+
+ if (!equal<T>(d, static_cast<T>(0.0)))
+ {
+ this->data[0] /= d;
+ this->data[1] /= d;
+ this->data[2] /= d;
+ }
+
+ return *this;
+ }
+
+ /// \brief Return a normalized vector
+ /// \return unit length vector
+ public: Vector3 Normalized() const
+ {
+ Vector3<T> result = *this;
+ result.Normalize();
+ return result;
+ }
+
+ /// \brief Round to near whole number, return the result.
+ /// \return the result
+ public: Vector3 Round()
+ {
+ this->data[0] = nearbyint(this->data[0]);
+ this->data[1] = nearbyint(this->data[1]);
+ this->data[2] = nearbyint(this->data[2]);
+ return *this;
+ }
+
+ /// \brief Get a rounded version of this vector
+ /// \return a rounded vector
+ public: Vector3 Rounded() const
+ {
+ Vector3<T> result = *this;
+ result.Round();
+ return result;
+ }
+
+ /// \brief Set the contents of the vector
+ /// \param[in] _x value along x
+ /// \param[in] _y value along y
+ /// \param[in] _z value aling z
+ public: inline void Set(T _x = 0, T _y = 0, T _z = 0)
+ {
+ this->data[0] = _x;
+ this->data[1] = _y;
+ this->data[2] = _z;
+ }
+
+ /// \brief Return the cross product of this vector with another vector.
+ /// \param[in] _v a vector
+ /// \return the cross product
+ public: Vector3 Cross(const Vector3<T> &_v) const
+ {
+ return Vector3(this->data[1] * _v[2] - this->data[2] * _v[1],
+ this->data[2] * _v[0] - this->data[0] * _v[2],
+ this->data[0] * _v[1] - this->data[1] * _v[0]);
+ }
+
+ /// \brief Return the dot product of this vector and another vector
+ /// \param[in] _v the vector
+ /// \return the dot product
+ public: T Dot(const Vector3<T> &_v) const
+ {
+ return this->data[0] * _v[0] +
+ this->data[1] * _v[1] +
+ this->data[2] * _v[2];
+ }
+
+ /// \brief Return the absolute dot product of this vector and
+ /// another vector. This is similar to the Dot function, except the
+ /// absolute value of each component of the vector is used.
+ ///
+ /// result = abs(x1 * x2) + abs(y1 * y2) + abs(z1 *z2)
+ ///
+ /// \param[in] _v the vector
+ /// \return The absolute dot product
+ public: T AbsDot(const Vector3<T> &_v) const
+ {
+ return std::abs(this->data[0] * _v[0]) +
+ std::abs(this->data[1] * _v[1]) +
+ std::abs(this->data[2] * _v[2]);
+ }
+
+ /// \brief Get the absolute value of the vector
+ /// \return a vector with positive elements
+ public: Vector3 Abs() const
+ {
+ return Vector3(std::abs(this->data[0]),
+ std::abs(this->data[1]),
+ std::abs(this->data[2]));
+ }
+
+ /// \brief Return a vector that is perpendicular to this one.
+ /// \return an orthogonal vector
+ public: Vector3 Perpendicular() const
+ {
+ static const T sqrZero = 1e-06 * 1e-06;
+
+ Vector3<T> perp = this->Cross(Vector3(1, 0, 0));
+
+ // Check the length of the vector
+ if (perp.SquaredLength() < sqrZero)
+ {
+ perp = this->Cross(Vector3(0, 1, 0));
+ }
+
+ return perp;
+ }
+
+ /// \brief Get a normal vector to a triangle
+ /// \param[in] _v1 first vertex of the triangle
+ /// \param[in] _v2 second vertex
+ /// \param[in] _v3 third vertex
+ /// \return the normal
+ public: static Vector3 Normal(const Vector3<T> &_v1,
+ const Vector3<T> &_v2, const Vector3<T> &_v3)
+ {
+ Vector3<T> a = _v2 - _v1;
+ Vector3<T> b = _v3 - _v1;
+ Vector3<T> n = a.Cross(b);
+ return n.Normalize();
+ }
+
+ /// \brief Get distance to a line
+ /// \param[in] _pt1 first point on the line
+ /// \param[in] _pt2 second point on the line
+ /// \return the minimum distance from this point to the line
+ public: T DistToLine(const Vector3<T> &_pt1, const Vector3 &_pt2)
+ {
+ T d = ((*this) - _pt1).Cross((*this) - _pt2).Length();
+ d = d / (_pt2 - _pt1).Length();
+ return d;
+ }
+
+ /// \brief Set this vector's components to the maximum of itself and the
+ /// passed in vector
+ /// \param[in] _v the maximum clamping vector
+ public: void Max(const Vector3<T> &_v)
+ {
+ if (_v[0] > this->data[0])
+ this->data[0] = _v[0];
+ if (_v[1] > this->data[1])
+ this->data[1] = _v[1];
+ if (_v[2] > this->data[2])
+ this->data[2] = _v[2];
+ }
+
+ /// \brief Set this vector's components to the minimum of itself and the
+ /// passed in vector
+ /// \param[in] _v the minimum clamping vector
+ public: void Min(const Vector3<T> &_v)
+ {
+ if (_v[0] < this->data[0])
+ this->data[0] = _v[0];
+ if (_v[1] < this->data[1])
+ this->data[1] = _v[1];
+ if (_v[2] < this->data[2])
+ this->data[2] = _v[2];
+ }
+
+ /// \brief Get the maximum value in the vector
+ /// \return the maximum element
+ public: T Max() const
+ {
+ return std::max(std::max(this->data[0], this->data[1]), this->data[2]);
+ }
+
+ /// \brief Get the minimum value in the vector
+ /// \return the minimum element
+ public: T Min() const
+ {
+ return std::min(std::min(this->data[0], this->data[1]), this->data[2]);
+ }
+
+ /// \brief Assignment operator
+ /// \param[in] _v a new value
+ /// \return this
+ public: Vector3 &operator=(const Vector3<T> &_v)
+ {
+ this->data[0] = _v[0];
+ this->data[1] = _v[1];
+ this->data[2] = _v[2];
+
+ return *this;
+ }
+
+ /// \brief Assignment operator
+ /// \param[in] _value assigned to all elements
+ /// \return this
+ public: Vector3 &operator=(T _v)
+ {
+ this->data[0] = _v;
+ this->data[1] = _v;
+ this->data[2] = _v;
+
+ return *this;
+ }
+
+ /// \brief Addition operator
+ /// \param[in] _v vector to add
+ /// \return the sum vector
+ public: Vector3 operator+(const Vector3<T> &_v) const
+ {
+ return Vector3(this->data[0] + _v[0],
+ this->data[1] + _v[1],
+ this->data[2] + _v[2]);
+ }
+
+ /// \brief Addition assignment operator
+ /// \param[in] _v vector to add
+ /// \return the sum vector
+ public: const Vector3 &operator+=(const Vector3<T> &_v)
+ {
+ this->data[0] += _v[0];
+ this->data[1] += _v[1];
+ this->data[2] += _v[2];
+
+ return *this;
+ }
+
+ /// \brief Addition operators
+ /// \param[in] _s the scalar addend
+ /// \return sum vector
+ public: inline Vector3<T> operator+(const T _s) const
+ {
+ return Vector3<T>(this->data[0] + _s,
+ this->data[1] + _s,
+ this->data[2] + _s);
+ }
+
+ /// \brief Addition operators
+ /// \param[in] _s the scalar addend
+ /// \param[in] _v input vector
+ /// \return sum vector
+ public: friend inline Vector3<T> operator+(const T _s,
+ const Vector3<T> &_v)
+ {
+ return {_v.X() + _s, _v.Y() + _s, _v.Z() + _s};
+ }
+
+ /// \brief Addition assignment operator
+ /// \param[in] _s scalar addend
+ /// \return this
+ public: const Vector3<T> &operator+=(const T _s)
+ {
+ this->data[0] += _s;
+ this->data[1] += _s;
+ this->data[2] += _s;
+
+ return *this;
+ }
+
+ /// \brief Negation operator
+ /// \return negative of this vector
+ public: inline Vector3 operator-() const
+ {
+ return Vector3(-this->data[0], -this->data[1], -this->data[2]);
+ }
+
+ /// \brief Subtraction operators
+ /// \param[in] _pt a vector to substract
+ /// \return a vector after the substraction
+ public: inline Vector3<T> operator-(const Vector3<T> &_pt) const
+ {
+ return Vector3(this->data[0] - _pt[0],
+ this->data[1] - _pt[1],
+ this->data[2] - _pt[2]);
+ }
+
+ /// \brief Subtraction assignment operators
+ /// \param[in] _pt subtrahend
+ /// \return a vector after the substraction
+ public: const Vector3<T> &operator-=(const Vector3<T> &_pt)
+ {
+ this->data[0] -= _pt[0];
+ this->data[1] -= _pt[1];
+ this->data[2] -= _pt[2];
+
+ return *this;
+ }
+
+ /// \brief Subtraction operators
+ /// \param[in] _s the scalar subtrahend
+ /// \return difference vector
+ public: inline Vector3<T> operator-(const T _s) const
+ {
+ return Vector3<T>(this->data[0] - _s,
+ this->data[1] - _s,
+ this->data[2] - _s);
+ }
+
+ /// \brief Subtraction operators
+ /// \param[in] _s the scalar minuend
+ /// \param[in] _v vector subtrahend
+ /// \return difference vector
+ public: friend inline Vector3<T> operator-(const T _s,
+ const Vector3<T> &_v)
+ {
+ return {_s - _v.X(), _s - _v.Y(), _s - _v.Z()};
+ }
+
+ /// \brief Subtraction assignment operator
+ /// \param[in] _s scalar subtrahend
+ /// \return this
+ public: const Vector3<T> &operator-=(const T _s)
+ {
+ this->data[0] -= _s;
+ this->data[1] -= _s;
+ this->data[2] -= _s;
+
+ return *this;
+ }
+
+ /// \brief Division operator
+ /// \remarks this is an element wise division
+ /// \param[in] _pt the vector divisor
+ /// \return a vector
+ public: const Vector3<T> operator/(const Vector3<T> &_pt) const
+ {
+ return Vector3(this->data[0] / _pt[0],
+ this->data[1] / _pt[1],
+ this->data[2] / _pt[2]);
+ }
+
+ /// \brief Division assignment operator
+ /// \remarks this is an element wise division
+ /// \param[in] _pt the vector divisor
+ /// \return a vector
+ public: const Vector3<T> &operator/=(const Vector3<T> &_pt)
+ {
+ this->data[0] /= _pt[0];
+ this->data[1] /= _pt[1];
+ this->data[2] /= _pt[2];
+
+ return *this;
+ }
+
+ /// \brief Division operator
+ /// \remarks this is an element wise division
+ /// \param[in] _v the divisor
+ /// \return a vector
+ public: const Vector3<T> operator/(T _v) const
+ {
+ return Vector3(this->data[0] / _v,
+ this->data[1] / _v,
+ this->data[2] / _v);
+ }
+
+ /// \brief Division assignment operator
+ /// \remarks this is an element wise division
+ /// \param[in] _v the divisor
+ /// \return this
+ public: const Vector3<T> &operator/=(T _v)
+ {
+ this->data[0] /= _v;
+ this->data[1] /= _v;
+ this->data[2] /= _v;
+
+ return *this;
+ }
+
+ /// \brief Multiplication operator
+ /// \remarks this is an element wise multiplication, not a cross product
+ /// \param[in] _p multiplier operator
+ /// \return a vector
+ public: Vector3<T> operator*(const Vector3<T> &_p) const
+ {
+ return Vector3(this->data[0] * _p[0],
+ this->data[1] * _p[1],
+ this->data[2] * _p[2]);
+ }
+
+ /// \brief Multiplication assignment operators
+ /// \remarks this is an element wise multiplication, not a cross product
+ /// \param[in] _v a vector
+ /// \return this
+ public: const Vector3<T> &operator*=(const Vector3<T> &_v)
+ {
+ this->data[0] *= _v[0];
+ this->data[1] *= _v[1];
+ this->data[2] *= _v[2];
+
+ return *this;
+ }
+
+ /// \brief Multiplication operators
+ /// \param[in] _s the scaling factor
+ /// \return a scaled vector
+ public: inline Vector3<T> operator*(T _s) const
+ {
+ return Vector3<T>(this->data[0] * _s,
+ this->data[1] * _s,
+ this->data[2] * _s);
+ }
+
+ /// \brief Multiplication operators
+ /// \param[in] _s the scaling factor
+ /// \param[in] _v input vector
+ /// \return a scaled vector
+ public: friend inline Vector3<T> operator*(T _s, const Vector3<T> &_v)
+ {
+ return {_v.X() * _s, _v.Y() * _s, _v.Z() * _s};
+ }
+
+ /// \brief Multiplication operator
+ /// \param[in] _v scaling factor
+ /// \return this
+ public: const Vector3<T> &operator*=(T _v)
+ {
+ this->data[0] *= _v;
+ this->data[1] *= _v;
+ this->data[2] *= _v;
+
+ return *this;
+ }
+
+ /// \brief Equality test with tolerance.
+ /// \param[in] _v the vector to compare to
+ /// \param[in] _tol equality tolerance.
+ /// \return true if the elements of the vectors are equal within
+ /// the tolerence specified by _tol.
+ public: bool Equal(const Vector3 &_v, const T &_tol) const
+ {
+ return equal<T>(this->data[0], _v[0], _tol)
+ && equal<T>(this->data[1], _v[1], _tol)
+ && equal<T>(this->data[2], _v[2], _tol);
+ }
+
+ /// \brief Equal to operator
+ /// \param[in] _v The vector to compare against
+ /// \return true if each component is equal within a
+ /// default tolerence (1e-3), false otherwise
+ public: bool operator==(const Vector3<T> &_v) const
+ {
+ return this->Equal(_v, static_cast<T>(1e-3));
+ }
+
+ /// \brief Not equal to operator
+ /// \param[in] _v The vector to compare against
+ /// \return false if each component is equal within a
+ /// default tolerence (1e-3), true otherwise
+ public: bool operator!=(const Vector3<T> &_v) const
+ {
+ return !(*this == _v);
+ }
+
+ /// \brief See if a point is finite (e.g., not nan)
+ /// \return true if is finite or false otherwise
+ public: bool IsFinite() const
+ {
+ // std::isfinite works with floating point values,
+ // need to explicit cast to avoid ambiguity in vc++.
+ return std::isfinite(static_cast<double>(this->data[0])) &&
+ std::isfinite(static_cast<double>(this->data[1])) &&
+ std::isfinite(static_cast<double>(this->data[2]));
+ }
+
+ /// \brief Corrects any nan values
+ public: inline void Correct()
+ {
+ // std::isfinite works with floating point values,
+ // need to explicit cast to avoid ambiguity in vc++.
+ if (!std::isfinite(static_cast<double>(this->data[0])))
+ this->data[0] = 0;
+ if (!std::isfinite(static_cast<double>(this->data[1])))
+ this->data[1] = 0;
+ if (!std::isfinite(static_cast<double>(this->data[2])))
+ this->data[2] = 0;
+ }
+
+ /// \brief Array subscript operator
+ /// \param[in] _index The index, where 0 == x, 1 == y, 2 == z.
+ /// The index is clamped to the range [0,2].
+ /// \return The value.
+ public: T &operator[](const std::size_t _index)
+ {
+ return this->data[clamp(_index, IGN_ZERO_SIZE_T, IGN_TWO_SIZE_T)];
+ }
+
+ /// \brief Const-qualified array subscript operator
+ /// \param[in] _index The index, where 0 == x, 1 == y, 2 == z.
+ /// The index is clamped to the range [0,2].
+ /// \return The value.
+ public: T operator[](const std::size_t _index) const
+ {
+ return this->data[clamp(_index, IGN_ZERO_SIZE_T, IGN_TWO_SIZE_T)];
+ }
+
+ /// \brief Round all values to _precision decimal places
+ /// \param[in] _precision the decimal places
+ public: void Round(int _precision)
+ {
+ this->data[0] = precision(this->data[0], _precision);
+ this->data[1] = precision(this->data[1], _precision);
+ this->data[2] = precision(this->data[2], _precision);
+ }
+
+ /// \brief Equality test
+ /// \remarks This is equivalent to the == operator
+ /// \param[in] _v the other vector
+ /// \return true if the 2 vectors have the same values, false otherwise
+ public: bool Equal(const Vector3<T> &_v) const
+ {
+ return equal<T>(this->data[0], _v[0]) &&
+ equal<T>(this->data[1], _v[1]) &&
+ equal<T>(this->data[2], _v[2]);
+ }
+
+ /// \brief Get the x value.
+ /// \return The x component of the vector
+ public: inline T X() const
+ {
+ return this->data[0];
+ }
+
+ /// \brief Get the y value.
+ /// \return The y component of the vector
+ public: inline T Y() const
+ {
+ return this->data[1];
+ }
+
+ /// \brief Get the z value.
+ /// \return The z component of the vector
+ public: inline T Z() const
+ {
+ return this->data[2];
+ }
+
+ /// \brief Get a mutable reference to the x value.
+ /// \return The x component of the vector
+ public: inline T &X()
+ {
+ return this->data[0];
+ }
+
+ /// \brief Get a mutable reference to the y value.
+ /// \return The y component of the vector
+ public: inline T &Y()
+ {
+ return this->data[1];
+ }
+
+ /// \brief Get a mutable reference to the z value.
+ /// \return The z component of the vector
+ public: inline T &Z()
+ {
+ return this->data[2];
+ }
+
+ /// \brief Set the x value.
+ /// \param[in] _v Value for the x component.
+ public: inline void X(const T &_v)
+ {
+ this->data[0] = _v;
+ }
+
+ /// \brief Set the y value.
+ /// \param[in] _v Value for the y component.
+ public: inline void Y(const T &_v)
+ {
+ this->data[1] = _v;
+ }
+
+ /// \brief Set the z value.
+ /// \param[in] _v Value for the z component.
+ public: inline void Z(const T &_v)
+ {
+ this->data[2] = _v;
+ }
+
+ /// \brief Less than operator.
+ /// \param[in] _pt Vector to compare.
+ /// \return True if this vector's X(), Y(), or Z() value is less
+ /// than the given vector's corresponding values.
+ public: bool operator<(const Vector3<T> &_pt) const
+ {
+ return this->data[0] < _pt[0] || this->data[1] < _pt[1] ||
+ this->data[2] < _pt[2];
+ }
+
+ /// \brief Stream insertion operator
+ /// \param _out output stream
+ /// \param _pt Vector3 to output
+ /// \return the stream
+ public: friend std::ostream &operator<<(
+ std::ostream &_out, const ignition::math::Vector3<T> &_pt)
+ {
+ _out << precision(_pt[0], 6) << " " << precision(_pt[1], 6) << " "
+ << precision(_pt[2], 6);
+ return _out;
+ }
+
+ /// \brief Stream extraction operator
+ /// \param _in input stream
+ /// \param _pt vector3 to read values into
+ /// \return the stream
+ public: friend std::istream &operator>>(
+ std::istream &_in, ignition::math::Vector3<T> &_pt)
+ {
+ // Skip white spaces
+ _in.setf(std::ios_base::skipws);
+ T x, y, z;
+ _in >> x >> y >> z;
+ _pt.Set(x, y, z);
+ return _in;
+ }
+
+ /// \brief The x, y, and z values
+ private: T data[3];
+ };
+
+ template<typename T> const Vector3<T> Vector3<T>::Zero(0, 0, 0);
+ template<typename T> const Vector3<T> Vector3<T>::One(1, 1, 1);
+ template<typename T> const Vector3<T> Vector3<T>::UnitX(1, 0, 0);
+ template<typename T> const Vector3<T> Vector3<T>::UnitY(0, 1, 0);
+ template<typename T> const Vector3<T> Vector3<T>::UnitZ(0, 0, 1);
+
+ typedef Vector3<int> Vector3i;
+ typedef Vector3<double> Vector3d;
+ typedef Vector3<float> Vector3f;
+ }
+}
+#endif
diff --git a/include/ignition/math/Vector3Stats.hh b/include/ignition/math/Vector3Stats.hh
new file mode 100644
index 0000000..6fc323d
--- /dev/null
+++ b/include/ignition/math/Vector3Stats.hh
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_VECTOR3STATS_HH_
+#define IGNITION_MATH_VECTOR3STATS_HH_
+
+#include <string>
+#include <ignition/math/Helpers.hh>
+#include <ignition/math/SignalStats.hh>
+#include <ignition/math/Vector3.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \brief Forward declare private data class.
+ class Vector3StatsPrivate;
+
+ /// \class Vector3Stats Vector3Stats.hh ignition/math/Vector3Stats.hh
+ /// \brief Collection of statistics for a Vector3 signal.
+ class IGNITION_MATH_VISIBLE Vector3Stats
+ {
+ /// \brief Constructor
+ public: Vector3Stats();
+
+ /// \brief Destructor
+ public: ~Vector3Stats();
+
+ /// \brief Add a new sample to the statistical measures.
+ /// \param[in] _data New signal data point.
+ public: void InsertData(const Vector3d &_data);
+
+ /// \brief Add a new type of statistic.
+ /// \param[in] _name Short name of new statistic.
+ /// Valid values include:
+ /// "maxAbs"
+ /// "mean"
+ /// "rms"
+ /// \return True if statistic was successfully added,
+ /// false if name was not recognized or had already
+ /// been inserted.
+ public: bool InsertStatistic(const std::string &_name);
+
+ /// \brief Add multiple statistics.
+ /// \param[in] _names Comma-separated list of new statistics.
+ /// For example, all statistics could be added with:
+ /// "maxAbs,mean,rms"
+ /// \return True if all statistics were successfully added,
+ /// false if any names were not recognized or had already
+ /// been inserted.
+ public: bool InsertStatistics(const std::string &_names);
+
+ /// \brief Forget all previous data.
+ public: void Reset();
+
+ /// \brief Get statistics for x component of signal.
+ /// \return Statistics for x component of signal.
+ public: const SignalStats &X() const;
+
+ /// \brief Get statistics for y component of signal.
+ /// \return Statistics for y component of signal.
+ public: const SignalStats &Y() const;
+
+ /// \brief Get statistics for z component of signal.
+ /// \return Statistics for z component of signal.
+ public: const SignalStats &Z() const;
+
+ /// \brief Get statistics for magnitude component of signal.
+ /// \return Statistics for magnitude component of signal.
+ public: const SignalStats &Mag() const;
+
+ /// \brief Get mutable reference to statistics for x component of signal.
+ /// \return Statistics for x component of signal.
+ public: SignalStats &X();
+
+ /// \brief Get mutable reference to statistics for y component of signal.
+ /// \return Statistics for y component of signal.
+ public: SignalStats &Y();
+
+ /// \brief Get mutable reference to statistics for z component of signal.
+ /// \return Statistics for z component of signal.
+ public: SignalStats &Z();
+
+ /// \brief Get mutable reference to statistics for magnitude of signal.
+ /// \return Statistics for magnitude of signal.
+ public: SignalStats &Mag();
+
+ /// \brief Pointer to private data.
+ protected: Vector3StatsPrivate *dataPtr;
+ };
+ }
+}
+#endif
+
diff --git a/include/ignition/math/Vector4.hh b/include/ignition/math/Vector4.hh
new file mode 100644
index 0000000..de3ffa5
--- /dev/null
+++ b/include/ignition/math/Vector4.hh
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_VECTOR4_HH_
+#define IGNITION_MATH_VECTOR4_HH_
+
+#include <ignition/math/Matrix4.hh>
+#include <ignition/math/Helpers.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \class Vector4 Vector4.hh ignition/math/Vector4.hh
+ /// \brief T Generic x, y, z, w vector
+ template<typename T>
+ class Vector4
+ {
+ /// \brief math::Vector4(0, 0, 0, 0)
+ public: static const Vector4<T> Zero;
+
+ /// \brief math::Vector4(1, 1, 1, 1)
+ public: static const Vector4<T> One;
+
+ /// \brief Constructor
+ public: Vector4()
+ {
+ this->data[0] = this->data[1] = this->data[2] = this->data[3] = 0;
+ }
+
+ /// \brief Constructor with component values
+ /// \param[in] _x value along x axis
+ /// \param[in] _y value along y axis
+ /// \param[in] _z value along z axis
+ /// \param[in] _w value along w axis
+ public: Vector4(const T &_x, const T &_y, const T &_z, const T &_w)
+ {
+ this->data[0] = _x;
+ this->data[1] = _y;
+ this->data[2] = _z;
+ this->data[3] = _w;
+ }
+
+ /// \brief Copy constructor
+ /// \param[in] _v vector
+ public: Vector4(const Vector4<T> &_v)
+ {
+ this->data[0] = _v[0];
+ this->data[1] = _v[1];
+ this->data[2] = _v[2];
+ this->data[3] = _v[3];
+ }
+
+ /// \brief Destructor
+ public: virtual ~Vector4() {}
+
+ /// \brief Calc distance to the given point
+ /// \param[in] _pt the point
+ /// \return the distance
+ public: T Distance(const Vector4<T> &_pt) const
+ {
+ return sqrt((this->data[0]-_pt[0])*(this->data[0]-_pt[0]) +
+ (this->data[1]-_pt[1])*(this->data[1]-_pt[1]) +
+ (this->data[2]-_pt[2])*(this->data[2]-_pt[2]) +
+ (this->data[3]-_pt[3])*(this->data[3]-_pt[3]));
+ }
+
+ /// \brief Returns the length (magnitude) of the vector
+ /// \return The length
+ public: T Length() const
+ {
+ return sqrt(this->SquaredLength());
+ }
+
+ /// \brief Return the square of the length (magnitude) of the vector
+ /// \return the length
+ public: T SquaredLength() const
+ {
+ return std::pow(this->data[0], 2)
+ + std::pow(this->data[1], 2)
+ + std::pow(this->data[2], 2)
+ + std::pow(this->data[3], 2);
+ }
+
+ /// \brief Normalize the vector length
+ public: void Normalize()
+ {
+ T d = this->Length();
+
+ if (!equal<T>(d, static_cast<T>(0.0)))
+ {
+ this->data[0] /= d;
+ this->data[1] /= d;
+ this->data[2] /= d;
+ this->data[3] /= d;
+ }
+ }
+
+ /// \brief Set the contents of the vector
+ /// \param[in] _x value along x axis
+ /// \param[in] _y value along y axis
+ /// \param[in] _z value along z axis
+ /// \param[in] _w value along w axis
+ public: void Set(T _x = 0, T _y = 0, T _z = 0, T _w = 0)
+ {
+ this->data[0] = _x;
+ this->data[1] = _y;
+ this->data[2] = _z;
+ this->data[3] = _w;
+ }
+
+ /// \brief Assignment operator
+ /// \param[in] _v the vector
+ /// \return a reference to this vector
+ public: Vector4<T> &operator=(const Vector4<T> &_v)
+ {
+ this->data[0] = _v[0];
+ this->data[1] = _v[1];
+ this->data[2] = _v[2];
+ this->data[3] = _v[3];
+
+ return *this;
+ }
+
+ /// \brief Assignment operator
+ /// \param[in] _value
+ public: Vector4<T> &operator=(T _value)
+ {
+ this->data[0] = _value;
+ this->data[1] = _value;
+ this->data[2] = _value;
+ this->data[3] = _value;
+
+ return *this;
+ }
+
+ /// \brief Addition operator
+ /// \param[in] _v the vector to add
+ /// \result a sum vector
+ public: Vector4<T> operator+(const Vector4<T> &_v) const
+ {
+ return Vector4<T>(this->data[0] + _v[0],
+ this->data[1] + _v[1],
+ this->data[2] + _v[2],
+ this->data[3] + _v[3]);
+ }
+
+ /// \brief Addition operator
+ /// \param[in] _v the vector to add
+ /// \return this vector
+ public: const Vector4<T> &operator+=(const Vector4<T> &_v)
+ {
+ this->data[0] += _v[0];
+ this->data[1] += _v[1];
+ this->data[2] += _v[2];
+ this->data[3] += _v[3];
+
+ return *this;
+ }
+
+ /// \brief Addition operators
+ /// \param[in] _s the scalar addend
+ /// \return sum vector
+ public: inline Vector4<T> operator+(const T _s) const
+ {
+ return Vector4<T>(this->data[0] + _s,
+ this->data[1] + _s,
+ this->data[2] + _s,
+ this->data[3] + _s);
+ }
+
+ /// \brief Addition operators
+ /// \param[in] _s the scalar addend
+ /// \param[in] _v input vector
+ /// \return sum vector
+ public: friend inline Vector4<T> operator+(const T _s,
+ const Vector4<T> &_v)
+ {
+ return _v + _s;
+ }
+
+ /// \brief Addition assignment operator
+ /// \param[in] _s scalar addend
+ /// \return this
+ public: const Vector4<T> &operator+=(const T _s)
+ {
+ this->data[0] += _s;
+ this->data[1] += _s;
+ this->data[2] += _s;
+ this->data[3] += _s;
+
+ return *this;
+ }
+
+ /// \brief Negation operator
+ /// \return negative of this vector
+ public: inline Vector4 operator-() const
+ {
+ return Vector4(-this->data[0], -this->data[1],
+ -this->data[2], -this->data[3]);
+ }
+
+ /// \brief Subtraction operator
+ /// \param[in] _v the vector to substract
+ /// \return a vector
+ public: Vector4<T> operator-(const Vector4<T> &_v) const
+ {
+ return Vector4<T>(this->data[0] - _v[0],
+ this->data[1] - _v[1],
+ this->data[2] - _v[2],
+ this->data[3] - _v[3]);
+ }
+
+ /// \brief Subtraction assigment operators
+ /// \param[in] _v the vector to substract
+ /// \return this vector
+ public: const Vector4<T> &operator-=(const Vector4<T> &_v)
+ {
+ this->data[0] -= _v[0];
+ this->data[1] -= _v[1];
+ this->data[2] -= _v[2];
+ this->data[3] -= _v[3];
+
+ return *this;
+ }
+
+ /// \brief Subtraction operators
+ /// \param[in] _s the scalar subtrahend
+ /// \return difference vector
+ public: inline Vector4<T> operator-(const T _s) const
+ {
+ return Vector4<T>(this->data[0] - _s,
+ this->data[1] - _s,
+ this->data[2] - _s,
+ this->data[3] - _s);
+ }
+
+ /// \brief Subtraction operators
+ /// \param[in] _s the scalar minuend
+ /// \param[in] _v vector subtrahend
+ /// \return difference vector
+ public: friend inline Vector4<T> operator-(const T _s,
+ const Vector4<T> &_v)
+ {
+ return Vector4<T>(_s - _v.X(), _s - _v.Y(), _s - _v.Z(), _s - _v.W());
+ }
+
+ /// \brief Subtraction assignment operator
+ /// \param[in] _s scalar subtrahend
+ /// \return this
+ public: const Vector4<T> &operator-=(const T _s)
+ {
+ this->data[0] -= _s;
+ this->data[1] -= _s;
+ this->data[2] -= _s;
+ this->data[3] -= _s;
+
+ return *this;
+ }
+
+ /// \brief Division assignment operator
+ /// \remarks Performs element wise division,
+ /// which has limited use.
+ /// \param[in] _v the vector to perform element wise division with
+ /// \return a result vector
+ public: const Vector4<T> operator/(const Vector4<T> &_v) const
+ {
+ return Vector4<T>(this->data[0] / _v[0],
+ this->data[1] / _v[1],
+ this->data[2] / _v[2],
+ this->data[3] / _v[3]);
+ }
+
+ /// \brief Division assignment operator
+ /// \remarks Performs element wise division,
+ /// which has limited use.
+ /// \param[in] _v the vector to perform element wise division with
+ /// \return this
+ public: const Vector4<T> &operator/=(const Vector4<T> &_v)
+ {
+ this->data[0] /= _v[0];
+ this->data[1] /= _v[1];
+ this->data[2] /= _v[2];
+ this->data[3] /= _v[3];
+
+ return *this;
+ }
+
+ /// \brief Division assignment operator
+ /// \remarks Performs element wise division,
+ /// which has limited use.
+ /// \param[in] _pt another vector
+ /// \return a result vector
+ public: const Vector4<T> operator/(T _v) const
+ {
+ return Vector4<T>(this->data[0] / _v, this->data[1] / _v,
+ this->data[2] / _v, this->data[3] / _v);
+ }
+
+ /// \brief Division operator
+ /// \param[in] _v scaling factor
+ /// \return a vector
+ public: const Vector4<T> &operator/=(T _v)
+ {
+ this->data[0] /= _v;
+ this->data[1] /= _v;
+ this->data[2] /= _v;
+ this->data[3] /= _v;
+
+ return *this;
+ }
+
+ /// \brief Multiplication operator.
+ /// \remarks Performs element wise multiplication,
+ /// which has limited use.
+ /// \param[in] _pt another vector
+ /// \return result vector
+ public: const Vector4<T> operator*(const Vector4<T> &_pt) const
+ {
+ return Vector4<T>(this->data[0] * _pt[0],
+ this->data[1] * _pt[1],
+ this->data[2] * _pt[2],
+ this->data[3] * _pt[3]);
+ }
+
+ /// \brief Matrix multiplication operator.
+ /// \param[in] _m matrix
+ /// \return the vector multiplied by _m
+ public: const Vector4<T> operator*(const Matrix4<T> &_m) const
+ {
+ return Vector4<T>(
+ this->data[0]*_m(0, 0) + this->data[1]*_m(1, 0) +
+ this->data[2]*_m(2, 0) + this->data[3]*_m(3, 0),
+ this->data[0]*_m(0, 1) + this->data[1]*_m(1, 1) +
+ this->data[2]*_m(2, 1) + this->data[3]*_m(3, 1),
+ this->data[0]*_m(0, 2) + this->data[1]*_m(1, 2) +
+ this->data[2]*_m(2, 2) + this->data[3]*_m(3, 2),
+ this->data[0]*_m(0, 3) + this->data[1]*_m(1, 3) +
+ this->data[2]*_m(2, 3) + this->data[3]*_m(3, 3));
+ }
+
+ /// \brief Multiplication assignment operator
+ /// \remarks Performs element wise multiplication,
+ /// which has limited use.
+ /// \param[in] _pt a vector
+ /// \return this
+ public: const Vector4<T> &operator*=(const Vector4<T> &_pt)
+ {
+ this->data[0] *= _pt[0];
+ this->data[1] *= _pt[1];
+ this->data[2] *= _pt[2];
+ this->data[3] *= _pt[3];
+
+ return *this;
+ }
+
+ /// \brief Multiplication operators
+ /// \param[in] _v scaling factor
+ /// \return a scaled vector
+ public: const Vector4<T> operator*(T _v) const
+ {
+ return Vector4<T>(this->data[0] * _v, this->data[1] * _v,
+ this->data[2] * _v, this->data[3] * _v);
+ }
+
+ /// \brief Scalar left multiplication operators
+ /// \param[in] _s the scaling factor
+ /// \param[in] _v the vector to scale
+ /// \return a scaled vector
+ public: friend inline const Vector4 operator*(const T _s,
+ const Vector4 &_v)
+ {
+ return Vector4(_v * _s);
+ }
+
+ /// \brief Multiplication assignment operator
+ /// \param[in] _v scaling factor
+ /// \return this
+ public: const Vector4<T> &operator*=(T _v)
+ {
+ this->data[0] *= _v;
+ this->data[1] *= _v;
+ this->data[2] *= _v;
+ this->data[3] *= _v;
+
+ return *this;
+ }
+
+ /// \brief Equality test with tolerance.
+ /// \param[in] _v the vector to compare to
+ /// \param[in] _tol equality tolerance.
+ /// \return true if the elements of the vectors are equal within
+ /// the tolerence specified by _tol.
+ public: bool Equal(const Vector4 &_v, const T &_tol) const
+ {
+ return equal<T>(this->data[0], _v[0], _tol)
+ && equal<T>(this->data[1], _v[1], _tol)
+ && equal<T>(this->data[2], _v[2], _tol)
+ && equal<T>(this->data[3], _v[3], _tol);
+ }
+
+ /// \brief Equal to operator
+ /// \param[in] _v the other vector
+ /// \return true if each component is equal within a
+ /// default tolerence (1e-6), false otherwise
+ public: bool operator==(const Vector4<T> &_v) const
+ {
+ return this->Equal(_v, static_cast<T>(1e-6));
+ }
+
+ /// \brief Not equal to operator
+ /// \param[in] _pt the other vector
+ /// \return false if each component is equal within a
+ /// default tolerence (1e-6), true otherwise
+ public: bool operator!=(const Vector4<T> &_pt) const
+ {
+ return !(*this == _pt);
+ }
+
+ /// \brief See if a point is finite (e.g., not nan)
+ /// \return true if finite, false otherwise
+ public: bool IsFinite() const
+ {
+ // std::isfinite works with floating point values,
+ // need to explicit cast to avoid ambiguity in vc++.
+ return std::isfinite(static_cast<double>(this->data[0])) &&
+ std::isfinite(static_cast<double>(this->data[1])) &&
+ std::isfinite(static_cast<double>(this->data[2])) &&
+ std::isfinite(static_cast<double>(this->data[3]));
+ }
+
+ /// \brief Array subscript operator
+ /// \param[in] _index The index, where 0 == x, 1 == y, 2 == z, 3 == w.
+ /// The index is clamped to the range (0,3).
+ /// \return The value.
+ public: T &operator[](const std::size_t _index)
+ {
+ return this->data[clamp(_index, IGN_ZERO_SIZE_T, IGN_THREE_SIZE_T)];
+ }
+
+ /// \brief Const-qualified array subscript operator
+ /// \param[in] _index The index, where 0 == x, 1 == y, 2 == z, 3 == w.
+ /// The index is clamped to the range (0,3).
+ /// \return The value.
+ public: T operator[](const std::size_t _index) const
+ {
+ return this->data[clamp(_index, IGN_ZERO_SIZE_T, IGN_THREE_SIZE_T)];
+ }
+
+ /// \brief Return a mutable x value.
+ /// \return The x component of the vector
+ public: T &X()
+ {
+ return this->data[0];
+ }
+
+ /// \brief Return a mutable y value.
+ /// \return The y component of the vector
+ public: T &Y()
+ {
+ return this->data[1];
+ }
+
+ /// \brief Return a mutable z value.
+ /// \return The z component of the vector
+ public: T &Z()
+ {
+ return this->data[2];
+ }
+
+ /// \brief Return a mutable w value.
+ /// \return The w component of the vector
+ public: T &W()
+ {
+ return this->data[3];
+ }
+
+ /// \brief Get the x value.
+ /// \return The x component of the vector
+ public: T X() const
+ {
+ return this->data[0];
+ }
+
+ /// \brief Get the y value.
+ /// \return The y component of the vector
+ public: T Y() const
+ {
+ return this->data[1];
+ }
+
+ /// \brief Get the z value.
+ /// \return The z component of the vector
+ public: T Z() const
+ {
+ return this->data[2];
+ }
+
+ /// \brief Get the w value.
+ /// \return The w component of the vector
+ public: T W() const
+ {
+ return this->data[3];
+ }
+
+ /// \brief Set the x value.
+ /// \param[in] _v Value for the x component.
+ public: inline void X(const T &_v)
+ {
+ this->data[0] = _v;
+ }
+
+ /// \brief Set the y value.
+ /// \param[in] _v Value for the y component.
+ public: inline void Y(const T &_v)
+ {
+ this->data[1] = _v;
+ }
+
+ /// \brief Set the z value.
+ /// \param[in] _v Value for the z component.
+ public: inline void Z(const T &_v)
+ {
+ this->data[2] = _v;
+ }
+
+ /// \brief Set the w value.
+ /// \param[in] _v Value for the w component.
+ public: inline void W(const T &_v)
+ {
+ this->data[3] = _v;
+ }
+
+ /// \brief Stream insertion operator
+ /// \param[in] _out output stream
+ /// \param[in] _pt Vector4 to output
+ /// \return The stream
+ public: friend std::ostream &operator<<(
+ std::ostream &_out, const ignition::math::Vector4<T> &_pt)
+ {
+ _out << _pt[0] << " " << _pt[1] << " " << _pt[2] << " " << _pt[3];
+ return _out;
+ }
+
+ /// \brief Stream extraction operator
+ /// \param[in] _in input stream
+ /// \param[in] _pt Vector4 to read values into
+ /// \return the stream
+ public: friend std::istream &operator>>(
+ std::istream &_in, ignition::math::Vector4<T> &_pt)
+ {
+ T x, y, z, w;
+
+ // Skip white spaces
+ _in.setf(std::ios_base::skipws);
+ _in >> x >> y >> z >> w;
+ _pt.Set(x, y, z, w);
+ return _in;
+ }
+
+ /// \brief Data values, 0==x, 1==y, 2==z, 3==w
+ private: T data[4];
+ };
+
+ template<typename T>
+ const Vector4<T> Vector4<T>::Zero(0, 0, 0, 0);
+
+ template<typename T>
+ const Vector4<T> Vector4<T>::One(1, 1, 1, 1);
+
+ typedef Vector4<int> Vector4i;
+ typedef Vector4<double> Vector4d;
+ typedef Vector4<float> Vector4f;
+ }
+}
+#endif
diff --git a/include/ignition/math/config.hh.in b/include/ignition/math/config.hh.in
new file mode 100644
index 0000000..16cf225
--- /dev/null
+++ b/include/ignition/math/config.hh.in
@@ -0,0 +1,15 @@
+/* Config.hh. Generated by CMake for @PROJECT_NAME_NO_VERSION at . */
+
+/* Version number */
+#define IGNITION_MATH_MAJOR_VERSION ${PROJECT_MAJOR_VERSION}
+#define IGNITION_MATH_MINOR_VERSION ${PROJECT_MINOR_VERSION}
+#define IGNITION_MATH_PATCH_VERSION ${PROJECT_PATCH_VERSION}
+
+#define IGNITION_MATH_VERSION "${PROJECT_VERSION}"
+#define IGNITION_MATH_VERSION_FULL "${PROJECT_VERSION_FULL}"
+
+#define IGNITION_MATH_VERSION_HEADER "Ignition math, version ${PROJECT_VERSION_FULL}\nCopyright (C) 2014 Open Source Robotics Foundation.\nReleased under the Apache 2.0 License.\n\n"
+
+#cmakedefine IGNITION_MATH_BUILD_TYPE_PROFILE 1
+#cmakedefine IGNITION_MATH_BUILD_TYPE_DEBUG 1
+#cmakedefine IGNITION_MATH_BUILD_TYPE_RELEASE 1
diff --git a/include/ignition/math/graph/Edge.hh b/include/ignition/math/graph/Edge.hh
new file mode 100644
index 0000000..afed5b6
--- /dev/null
+++ b/include/ignition/math/graph/Edge.hh
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_GRAPH_EDGE_HH_
+#define IGNITION_MATH_GRAPH_EDGE_HH_
+
+// uint64_t
+#include <cstdint>
+#include <functional>
+#include <iostream>
+#include <map>
+#include <set>
+
+#include "ignition/math/graph/Vertex.hh"
+
+namespace ignition
+{
+namespace math
+{
+namespace graph
+{
+ /// \def EdgeId.
+ /// \brief The unique Id for an edge.
+ using EdgeId = uint64_t;
+
+ /// \brief Used in the Graph constructors for uniform initialization.
+ template<typename E>
+ struct EdgeInitializer
+ {
+ /// \brief Constructor.
+ /// \param[in] _vertices The vertices of the edge.
+ /// \param[in] _data The data stored in the edge.
+ /// \param[in] _weight The weight (cost) of the edge.
+ EdgeInitializer(const VertexId_P &_vertices,
+ const E &_data = E(),
+ const double _weight = 1)
+ : vertices(_vertices),
+ data(_data),
+ weight(_weight)
+ {
+ };
+
+ /// \brief IDs of the vertices.
+ public: VertexId_P vertices;
+
+ /// \brief User data.
+ public: E data;
+
+ /// \brief The weight (cost) of the edge.
+ public: double weight = 1;
+ };
+
+ /// \brief Generic edge class. An edge has two ends and some constraint
+ /// between them. For example, a directed edge only allows traversing the
+ /// edge in one direction.
+ template<typename E>
+ class Edge
+ {
+ /// \brief Constructor.
+ /// \param[in] _vertices The vertices of the edge.
+ /// \param[in] _data The data stored in the edge.
+ /// \param[in] _weight The weight (cost) of the edge.
+ /// \param[in] _id Optional unique id.
+ public: explicit Edge(const VertexId_P &_vertices,
+ const E &_data,
+ const double _weight,
+ const EdgeId &_id = kNullId)
+ : id(_id),
+ vertices(_vertices),
+ data(_data),
+ weight(_weight)
+ {
+ }
+
+ /// \brief Get the edge Id.
+ /// \return The edge Id.
+ public: EdgeId Id() const
+ {
+ return this->id;
+ }
+
+ /// \brief Get the two vertices contained in the edge.
+ /// \return The two vertices contained in the edge.
+ public: VertexId_P Vertices() const
+ {
+ if (!this->Valid())
+ return {kNullId, kNullId};
+
+ return this->vertices;
+ }
+
+ /// \brief Get a non-mutable reference to the user data stored in the edge
+ /// \return The non-mutable reference to the user data stored in the edge.
+ public: const E &Data() const
+ {
+ return this->data;
+ }
+
+ /// \brief Get a mutable reference to the user data stored in the edge.
+ /// \return The mutable reference to the user data stored in the edge.
+ public: E &Data()
+ {
+ return this->data;
+ }
+
+ /// \brief The cost of traversing the _from end to the other end of the
+ /// edge.
+ /// \return The cost.
+ public: double Weight() const
+ {
+ return this->weight;
+ }
+
+ /// \brief Set the cost of the edge.
+ /// \param[in] _newWeight The new cost.
+ public: void SetWeight(const double _newWeight)
+ {
+ this->weight = _newWeight;
+ }
+
+ /// \brief Get the destination end that is reachable from a source end of
+ /// an edge.
+ ///
+ /// E.g.: Let's assume that we have an undirected edge (e1) with ends
+ /// (v1) and (v2): (v1)--(v2). The operation e1.From(v1) returns (v2).
+ /// The operation e1.From(v2) returns (v1).
+ ///
+ /// E.g.: Let's assume that we have a directed edge (e2) with the tail end
+ /// (v1) and the head end (v2): (v1)->(v2). The operation e2.From(v1)
+ /// returns (v2). The operation e2.From(v2) returns kNullId.
+ ///
+ /// \param[in] _from Source vertex.
+ /// \return The other vertex of the edge reachable from the "_from"
+ /// vertex or kNullId otherwise.
+ public: virtual VertexId From(const VertexId &_from) const = 0;
+
+ /// \brief Get the source end that can reach the destination end of
+ /// an edge.
+ ///
+ /// E.g.: Let's assume that we have an undirected edge (e1) with ends
+ /// (v1) and (v2): (v1)--(v2). The operation e1.To(v1) returns (v2).
+ /// The operation e1.To(v2) returns (v1).
+ ///
+ /// E.g.: Let's assume that we have a directed edge (e2) with the tail end
+ /// (v1) and the head end (v2): (v1)->(v2). The operation e2.To(v1)
+ /// returns kNullId. The operation e2.To(v2) returns v1.
+ ///
+ /// \param[in] _from Destination vertex.
+ /// \return The other vertex of the edge that can reach "_to"
+ /// vertex or kNullId otherwise.
+ public: virtual VertexId To(const VertexId &_to) const = 0;
+
+ /// \brief An edge is considered valid when its id is not kNullId.
+ /// \return Whether the edge is valid or not.
+ public: bool Valid() const
+ {
+ return this->id != kNullId;
+ }
+
+ /// \brief Unique edge Id.
+ private: EdgeId id = kNullId;
+
+ /// \brief The set of Ids of the two vertices.
+ private: VertexId_P vertices;
+
+ /// \brief User data.
+ private: E data;
+
+ /// \brief The weight (cost) of the edge. By default, the cost of an edge
+ /// is 1.0 .
+ private: double weight = 1.0;
+ };
+
+ /// \def EdgeId_S
+ /// \brief A set of edge Ids.
+ using EdgeId_S = std::set<EdgeId>;
+
+ /// \def EdgeRef_M
+ /// \brief A map of edges. The key is the edge Id. The value is a reference
+ /// to the edge.
+ template<typename EdgeType>
+ using EdgeRef_M = std::map<EdgeId, std::reference_wrapper<const EdgeType>>;
+
+ /// \brief An undirected edge represents a connection between two vertices.
+ /// The connection is bidirectional, it's possible to traverse the edge
+ /// in both directions.
+ template<typename E>
+ class UndirectedEdge : public Edge<E>
+ {
+ /// \brief An invalid undirected edge.
+ public: static UndirectedEdge<E> NullEdge;
+
+ /// \brief Constructor.
+ /// \param[in] _vertices The vertices of the edge.
+ /// \param[in] _data The data stored in the edge.
+ /// \param[in] _weight The weight (cost) of the edge.
+ /// \param[in] _id Optional unique id.
+ public: explicit UndirectedEdge(const VertexId_P &_vertices,
+ const E &_data,
+ const double _weight,
+ const EdgeId &_id = kNullId)
+ : Edge<E>(_vertices, _data, _weight, _id)
+ {
+ }
+
+ // Documentation inherited.
+ public: VertexId From(const VertexId &_from) const override
+ {
+ if (!this->Valid())
+ return kNullId;
+
+ if (this->Vertices().first != _from && this->Vertices().second != _from)
+ return kNullId;
+
+ if (this->Vertices().first == _from)
+ return this->Vertices().second;
+
+ return this->Vertices().first;
+ }
+
+ // Documentation inherited.
+ public: VertexId To(const VertexId &_to) const override
+ {
+ return this->From(_to);
+ }
+
+ /// \brief Stream insertion operator. The output uses DOT graph
+ /// description language.
+ /// \param[out] _out The output stream.
+ /// \param[in] _e Edge to write to the stream.
+ /// \ref https://en.wikipedia.org/wiki/DOT_(graph_description_language).
+ public: friend std::ostream &operator<<(std::ostream &_out,
+ const UndirectedEdge<E> &_e)
+ {
+ auto vertices = _e.Vertices();
+ _out << " " << vertices.first << " -- " << vertices.second
+ << " [label=" << _e.Weight() << "];" << std::endl;
+ return _out;
+ }
+ };
+
+ /// \brief An invalid undirected edge.
+ template<typename E>
+ UndirectedEdge<E> UndirectedEdge<E>::NullEdge(
+ VertexId_P(kNullId, kNullId), E(), 1.0, kNullId);
+
+ /// \brief A directed edge represents a connection between two vertices.
+ /// The connection is unidirectional, it's only possible to traverse the edge
+ /// in one direction (from the tail to the head).
+ template<typename E>
+ class DirectedEdge : public Edge<E>
+ {
+ /// \brief An invalid directed edge.
+ public: static DirectedEdge<E> NullEdge;
+
+ /// \brief Constructor.
+ /// \param[in] _vertices The vertices of the edge.
+ /// \param[in] _data The data stored in the edge.
+ /// \param[in] _weight The weight (cost) of the edge.
+ /// \param[in] _id Optional unique id.
+ public: explicit DirectedEdge(const VertexId_P &_vertices,
+ const E &_data,
+ const double _weight,
+ const EdgeId &_id = kNullId)
+ : Edge<E>(_vertices, _data, _weight, _id)
+ {
+ }
+
+ /// \brief Get the Id of the tail vertex in this edge.
+ /// \return An id of the tail vertex in this edge.
+ /// \sa Head()
+ public: VertexId Tail() const
+ {
+ return this->Vertices().first;
+ }
+
+ /// \brief Get the Id of the head vertex in this edge.
+ /// \return An id of the head vertex in this edge.
+ /// \sa Tail()
+ public: VertexId Head() const
+ {
+ return this->Vertices().second;
+ }
+
+ // Documentation inherited.
+ public: VertexId From(const VertexId &_from) const override
+ {
+ if (_from != this->Tail())
+ return kNullId;
+
+ return this->Head();
+ }
+
+ // Documentation inherited.
+ public: VertexId To(const VertexId &_to) const override
+ {
+ if (_to != this->Head())
+ return kNullId;
+
+ return this->Tail();
+ }
+
+ /// \brief Stream insertion operator. The output uses DOT graph
+ /// description language.
+ /// \param[out] _out The output stream.
+ /// \param[in] _e Edge to write to the stream.
+ /// \ref https://en.wikipedia.org/wiki/DOT_(graph_description_language).
+ public: friend std::ostream &operator<<(std::ostream &_out,
+ const DirectedEdge<E> &_e)
+ {
+ _out << " " << _e.Tail() << " -> " << _e.Head()
+ << " [label=" << _e.Weight() << "];" << std::endl;
+ return _out;
+ }
+ };
+
+ /// \brief An invalid directed edge.
+ template<typename E>
+ DirectedEdge<E> DirectedEdge<E>::NullEdge(
+ VertexId_P(kNullId, kNullId), E(), 1.0, kNullId);
+}
+}
+}
+#endif
diff --git a/include/ignition/math/graph/Graph.hh b/include/ignition/math/graph/Graph.hh
new file mode 100644
index 0000000..c9f3afa
--- /dev/null
+++ b/include/ignition/math/graph/Graph.hh
@@ -0,0 +1,763 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_GRAPH_GRAPH_HH_
+#define IGNITION_MATH_GRAPH_GRAPH_HH_
+
+#include <cassert>
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "ignition/math/graph/Edge.hh"
+#include "ignition/math/graph/Vertex.hh"
+
+namespace ignition
+{
+namespace math
+{
+namespace graph
+{
+ /// \brief A generic graph class.
+ /// Both vertices and edges can store user information. A vertex could be
+ /// created passing a custom Id if needed, otherwise it will be choosen
+ /// internally. The vertices also have a name that could be reused among
+ /// other vertices if needed. This class supports the use of different edge
+ /// types (e.g. directed or undirected edges).
+ ///
+ /// <b> Example directed graph</b>
+ //
+ /// \code{.cpp}
+ ///
+ /// // Create a directed graph that is capable of storing integer data in the
+ /// // vertices and double data on the edges.
+ /// ignition::math::graph::DirectedGraph<int, double> graph(
+ /// // Create the vertices, with default data and vertex ids.
+ /// {
+ /// {"vertex1"}, {"vertex2"}, {"vertex3"}
+ /// },
+ /// // Create the edges, with default data and weight values.
+ /// {
+ /// // Edge from vertex 0 to vertex 1. Each number refers to a vertex id.
+ /// // Vertex ids start from zero.
+ /// {{0, 1}}, {{1, 2}}
+ /// });
+ ///
+ /// // You can assign data to vertices.
+ /// ignition::math::graph::DirectedGraph<int, double> graph2(
+ /// // Create the vertices, with custom data and default vertex ids.
+ /// {
+ /// {"vertex1", 1}, {"vertex2", 2}, {"vertex3", 10}
+ /// },
+ /// // Create the edges, with default data and weight values.
+ /// {
+ /// // Edge from vertex 0 to vertex 1. Each number refers to a vertex id
+ /// // specified above.
+ /// {{0, 2}}, {{1, 2}}
+ /// });
+ ///
+ ///
+ /// // It's also possible to specify vertex ids.
+ /// ignition::math::graph::DirectedGraph<int, double> graph3(
+ /// // Create the vertices with custom data and vertex ids.
+ /// {
+ /// {"vertex1", 1, 2}, {"vertex2", 2, 3}, {"vertex3", 10, 4}
+ /// },
+ /// // Create the edges, with custom data and default weight values.
+ /// {
+ /// {{2, 3}, 6.3}, {{3, 4}, 4.2}
+ /// });
+ ///
+ /// // Finally, you can also assign weights to the edges.
+ /// ignition::math::graph::DirectedGraph<int, double> graph4(
+ /// // Create the vertices with custom data and vertex ids.
+ /// {
+ /// {"vertex1", 1, 2}, {"vertex2", 2, 3}, {"vertex3", 10, 4}
+ /// },
+ /// // Create the edges, with custom data and default weight values
+ /// {
+ /// {{2, 3}, 6.3, 1.1}, {{3, 4}, 4.2, 2.3}
+ /// });
+ /// \endcode
+ template<typename V, typename E, typename EdgeType>
+ class Graph
+ {
+ /// \brief Default constructor.
+ public: Graph() = default;
+
+ /// \brief Constructor.
+ /// \param[in] _vertices Collection of vertices.
+ /// \param[in] _edges Collection of edges.
+ public: Graph(const std::vector<Vertex<V>> &_vertices,
+ const std::vector<EdgeInitializer<E>> &_edges)
+ {
+ // Add all vertices.
+ for (auto const &v : _vertices)
+ {
+ if (!this->AddVertex(v.Name(), v.Data(), v.Id()).Valid())
+ {
+ std::cerr << "Invalid vertex with Id [" << v.Id() << "]. Ignoring."
+ << std::endl;
+ }
+ }
+
+ // Add all edges.
+ for (auto const &e : _edges)
+ {
+ if (!this->AddEdge(e.vertices, e.data, e.weight).Valid())
+ std::cerr << "Ignoring edge" << std::endl;
+ }
+ }
+
+ /// \brief Add a new vertex to the graph.
+ /// \param[in] _name Name of the vertex. It doesn't have to be unique.
+ /// \param[in] _data Data to be stored in the vertex.
+ /// \param[in] _id Optional Id to be used for this vertex. This Id must
+ /// be unique.
+ /// \return A reference to the new vertex.
+ public: Vertex<V> &AddVertex(const std::string &_name,
+ const V &_data,
+ const VertexId &_id = kNullId)
+ {
+ auto id = _id;
+
+ // The user didn't provide an Id, we generate it.
+ if (id == kNullId)
+ {
+ id = this->NextVertexId();
+
+ // No space for new Ids.
+ if (id == kNullId)
+ {
+ std::cerr << "[Graph::AddVertex()] The limit of vertices has been "
+ << "reached. Ignoring vertex." << std::endl;
+ return Vertex<V>::NullVertex;
+ }
+ }
+
+ // Create the vertex.
+ auto ret = this->vertices.insert(
+ std::make_pair(id, Vertex<V>(_name, _data, id)));
+
+ // The Id already exists.
+ if (!ret.second)
+ {
+ std::cerr << "[Graph::AddVertex()] Repeated vertex [" << id << "]"
+ << std::endl;
+ return Vertex<V>::NullVertex;
+ }
+
+ // Link the vertex with an empty list of edges.
+ this->adjList[id] = EdgeId_S();
+
+ // Update the map of names.
+ this->names.insert(std::make_pair(_name, id));
+
+ return ret.first->second;
+ }
+
+ /// \brief The collection of all vertices in the graph.
+ /// \return A map of vertices, where keys are Ids and values are
+ /// references to the vertices.
+ public: const VertexRef_M<V> Vertices() const
+ {
+ VertexRef_M<V> res;
+ for (auto const &v : this->vertices)
+ res.emplace(std::make_pair(v.first, std::cref(v.second)));
+
+ return std::move(res);
+ }
+
+ /// \brief The collection of all vertices in the graph with name == _name.
+ /// \return A map of vertices, where keys are Ids and values are
+ /// references to the vertices.
+ public: const VertexRef_M<V> Vertices(const std::string &_name) const
+ {
+ VertexRef_M<V> res;
+ for (auto const &vertex : this->vertices)
+ {
+ if (vertex.second.Name() == _name)
+ res.emplace(std::make_pair(vertex.first, std::cref(vertex.second)));
+ }
+
+ return std::move(res);
+ }
+
+ /// \brief Add a new edge to the graph.
+ /// \param[in] _vertices The set of Ids of the two vertices.
+ /// \param[in] _data User data.
+ /// \return Reference to the new edge created or NullEdge if the
+ /// edge was not created (e.g. incorrect vertices).
+ public: EdgeType &AddEdge(const VertexId_P &_vertices,
+ const E &_data,
+ const double _weight = 1.0)
+ {
+ auto id = this->NextEdgeId();
+
+ // No space for new Ids.
+ if (id == kNullId)
+ {
+ std::cerr << "[Graph::AddEdge()] The limit of edges has been reached. "
+ << "Ignoring edge." << std::endl;
+ return EdgeType::NullEdge;
+ }
+
+ EdgeType newEdge(_vertices, _data, _weight, id);
+ return this->LinkEdge(std::move(newEdge));
+ }
+
+ /// \brief Links an edge to the graph. This function verifies that the
+ /// edge's two vertices exist in the graph, copies the edge into the
+ /// graph's internal data structure, and returns a reference to this
+ /// new edge.
+ /// \param[in] _edge An edge to copy into the graph.
+ /// \return A reference to the new edge.
+ public: EdgeType &LinkEdge(const EdgeType &_edge)
+ {
+ auto edgeVertices = _edge.Vertices();
+
+ // Sanity check: Both vertices should exist.
+ for (auto const &v : {edgeVertices.first, edgeVertices.second})
+ {
+ if (this->vertices.find(v) == this->vertices.end())
+ return EdgeType::NullEdge;
+ }
+
+ // Link the new edge.
+ for (auto const &v : {edgeVertices.first, edgeVertices.second})
+ {
+ if (v != kNullId)
+ {
+ auto vertexIt = this->adjList.find(v);
+ assert(vertexIt != this->adjList.end());
+ vertexIt->second.insert(_edge.Id());
+ }
+ }
+
+ auto ret = this->edges.insert(std::make_pair(_edge.Id(), _edge));
+
+ // Return the new edge.
+ return ret.first->second;
+ }
+
+ /// \brief The collection of all edges in the graph.
+ /// \return A map of edges, where keys are Ids and values are references
+ /// to the edges.
+ public: const EdgeRef_M<EdgeType> Edges() const
+ {
+ EdgeRef_M<EdgeType> res;
+ for (auto const &edge : this->edges)
+ {
+ res.emplace(std::make_pair(edge.first, std::cref(edge.second)));
+ }
+
+ return std::move(res);
+ }
+
+ /// \brief Get all vertices that are directly connected with one edge
+ /// from a given vertex. In other words, this function will return
+ /// child vertices of the given vertex (all vertices from the given
+ /// vertex). E.g. j is adjacent from i (the given vertex) if there is an
+ /// edge (i->j).
+ ///
+ /// In an undirected graph, the result of this function will match
+ /// the result provided by AdjacentsTo.
+ ///
+ /// \param[in] _vertex The Id of the vertex from which adjacent
+ /// vertices will be returned.
+ /// \return A map of vertices, where keys are Ids and values are
+ /// references to the vertices. This is the set of adjacent vertices.
+ /// An empty map will be returned when the _vertex is not found in the
+ /// graph.
+ public: VertexRef_M<V> AdjacentsFrom(const VertexId &_vertex) const
+ {
+ VertexRef_M<V> res;
+
+ // Make sure the vertex exists
+ auto vertexIt = this->adjList.find(_vertex);
+ if (vertexIt == this->adjList.end())
+ return res;
+
+ for (auto const &edgeId : vertexIt->second)
+ {
+ const auto &edge = this->EdgeFromId(edgeId);
+ auto neighborVertexId = edge.From(_vertex);
+ if (neighborVertexId != kNullId)
+ {
+ const auto &neighborVertex = this->VertexFromId(neighborVertexId);
+ res.emplace(
+ std::make_pair(neighborVertexId, std::cref(neighborVertex)));
+ }
+ }
+
+ return res;
+ }
+
+ /// \brief Get all vertices that are directly connected with one edge
+ /// from a given vertex. In other words, this function will return
+ /// child vertices of the given vertex (all vertices from the given
+ /// vertex). E.g. j is adjacent from i (the given vertex) if there is an
+ /// edge (i->j).
+ ///
+ /// In an undirected graph, the result of this function will match
+ /// the result provided by AdjacentsTo.
+ ///
+ /// \param[in] _vertex The Id of the vertex from which adjacent
+ /// vertices will be returned.
+ /// \return A map of vertices, where keys are Ids and values are
+ /// references to the vertices. This is the set of adjacent vertices.
+ /// An empty map will be returned when the _vertex is not found in the
+ /// graph.
+ public: VertexRef_M<V> AdjacentsFrom(const Vertex<V> &_vertex) const
+ {
+ return this->AdjacentsFrom(_vertex.Id());
+ }
+
+ /// \brief Get all vertices that are directly connected with one edge
+ /// to a given vertex. In other words, this function will return
+ /// child vertices of the given vertex (all vertices from the given
+ /// vertex).
+ ///
+ /// In an undirected graph, the result of this function will match
+ /// the result provided by AdjacentsFrom.
+ ///
+ /// E.g. i is adjacent to j (the given vertex) if there is an
+ /// edge (i->j).
+ /// \param[in] _vertex The Id of the vertex to check adjacentsTo.
+ /// \return A map of vertices, where keys are Ids and values are
+ /// references to the vertices. An empty map is returned if the
+ /// _vertex is not present in this graph, or when there are no
+ /// adjacent vertices.
+ public: VertexRef_M<V> AdjacentsTo(const VertexId &_vertex) const
+ {
+ auto incidentEdges = this->IncidentsTo(_vertex);
+
+ VertexRef_M<V> res;
+ for (auto const &incidentEdgeRef : incidentEdges)
+ {
+ const auto &incidentEdgeId = incidentEdgeRef.first;
+ const auto &incidentEdge = this->EdgeFromId(incidentEdgeId);
+ const auto &neighborVertexId = incidentEdge.To(_vertex);
+ const auto &neighborVertex = this->VertexFromId(neighborVertexId);
+ res.emplace(
+ std::make_pair(neighborVertexId, std::cref(neighborVertex)));
+ }
+
+ return res;
+ }
+
+ /// \brief Get all vertices that are directly connected with one edge
+ /// to a given vertex. In other words, this function will return
+ /// child vertices of the given vertex (all vertices from the given
+ /// vertex).
+ ///
+ /// In an undirected graph, the result of this function will match
+ /// the result provided by AdjacentsFrom.
+ ///
+ /// E.g. i is adjacent to j (the given vertex) if there is an
+ /// edge (i->j).
+ /// \param[in] _vertex The vertex to check adjacentsTo.
+ /// \return A map of vertices, where keys are Ids and values are
+ /// references to the vertices. An empty map is returned if the
+ /// _vertex is not present in this graph, or when there are no
+ /// adjacent vertices.
+ public: VertexRef_M<V> AdjacentsTo(const Vertex<V> &_vertex) const
+ {
+ return this->AdjacentsTo(_vertex.Id());
+ }
+
+ /// \brief Get the number of edges incident to a vertex.
+ /// \param[in] _vertex The vertex Id.
+ /// \return The number of edges incidents to a vertex.
+ public: size_t InDegree(const VertexId &_vertex) const
+ {
+ return this->IncidentsTo(_vertex).size();
+ }
+
+ /// \brief Get the number of edges incident to a vertex.
+ /// \param[in] _vertex The vertex.
+ /// \return The number of edges incidents to a vertex.
+ public: size_t InDegree(const Vertex<V> &_vertex) const
+ {
+ return this->IncidentsTo(this->VertexFromId(_vertex.Id())).size();
+ }
+
+ /// \brief Get the number of edges incident from a vertex.
+ /// \param[in] _vertex The vertex Id.
+ /// \return The number of edges incidents from a vertex.
+ public: size_t OutDegree(const VertexId &_vertex) const
+ {
+ return this->IncidentsFrom(_vertex).size();
+ }
+
+ /// \brief Get the number of edges incident from a vertex.
+ /// \param[in] _vertex The vertex.
+ /// \return The number of edges incidents from a vertex.
+ public: size_t OutDegree(const Vertex<V> &_vertex) const
+ {
+ return this->IncidentsFrom(this->VertexFromId(_vertex.Id())).size();
+ }
+
+ /// \brief Get the set of outgoing edges from a given vertex.
+ /// \param[in] _vertex Id of the vertex.
+ /// \return A map of edges, where keys are Ids and values are
+ /// references to the edges. An empty map is returned when the provided
+ /// vertex does not exist, or when there are no outgoing edges.
+ public: const EdgeRef_M<EdgeType> IncidentsFrom(const VertexId &_vertex)
+ const
+ {
+ EdgeRef_M<EdgeType> res;
+
+ const auto &adjIt = this->adjList.find(_vertex);
+ if (adjIt == this->adjList.end())
+ return res;
+
+ const auto &edgeIds = adjIt->second;
+ for (auto const &edgeId : edgeIds)
+ {
+ const auto &edge = this->EdgeFromId(edgeId);
+ if (edge.From(_vertex) != kNullId)
+ res.emplace(std::make_pair(edge.Id(), std::cref(edge)));
+ }
+
+ return std::move(res);
+ }
+
+ /// \brief Get the set of outgoing edges from a given vertex.
+ /// \param[in] _vertex The vertex.
+ /// \return A map of edges, where keys are Ids and values are
+ /// references to the edges. An empty map is returned when the provided
+ /// vertex does not exist, or when there are no outgoing edges.
+ public: const EdgeRef_M<EdgeType> IncidentsFrom(
+ const Vertex<V> &_vertex) const
+ {
+ return this->IncidentsFrom(_vertex.Id());
+ }
+
+ /// \brief Get the set of incoming edges to a given vertex.
+ /// \param[in] _vertex Id of the vertex.
+ /// \return A map of edges, where keys are Ids and values are
+ /// references to the edges. An empty map is returned when the provided
+ /// vertex does not exist, or when there are no incoming edges.
+ public: const EdgeRef_M<EdgeType> IncidentsTo(
+ const VertexId &_vertex) const
+ {
+ EdgeRef_M<EdgeType> res;
+
+ const auto &adjIt = this->adjList.find(_vertex);
+ if (adjIt == this->adjList.end())
+ return res;
+
+ const auto &edgeIds = adjIt->second;
+ for (auto const &edgeId : edgeIds)
+ {
+ const auto &edge = this->EdgeFromId(edgeId);
+ if (edge.To(_vertex) != kNullId)
+ res.emplace(std::make_pair(edge.Id(), std::cref(edge)));
+ }
+
+ return std::move(res);
+ }
+
+ /// \brief Get the set of incoming edges to a given vertex.
+ /// \param[in] _vertex The vertex.
+ /// \return A map of edges, where keys are Ids and values are
+ /// references to the edges. An empty map is returned when the provided
+ /// vertex does not exist, or when there are no incoming edges.
+ public: const EdgeRef_M<EdgeType> IncidentsTo(const Vertex<V> &_vertex)
+ const
+ {
+ return this->IncidentsTo(_vertex.Id());
+ }
+
+ /// \brief Get whether the graph is empty.
+ /// \return True when there are no vertices in the graph or
+ /// false otherwise.
+ public: bool Empty() const
+ {
+ return this->vertices.empty();
+ }
+
+ /// \brief Remove an existing vertex from the graph.
+ /// \param[in] _vertex Id of the vertex to be removed.
+ /// \return True when the vertex was removed or false otherwise.
+ public: bool RemoveVertex(const VertexId &_vertex)
+ {
+ auto vIt = this->vertices.find(_vertex);
+ if (vIt == this->vertices.end())
+ return false;
+
+ std::string name = vIt->second.Name();
+
+ // Remove incident edges.
+ auto incidents = this->IncidentsTo(_vertex);
+ for (auto edgePair : incidents)
+ this->RemoveEdge(edgePair.first);
+
+ // Remove all outgoing edges.
+ incidents = this->IncidentsFrom(_vertex);
+ for (auto edgePair : incidents)
+ this->RemoveEdge(edgePair.first);
+
+ // Remove the vertex (key) from the adjacency list.
+ this->adjList.erase(_vertex);
+
+ // Remove the vertex.
+ this->vertices.erase(_vertex);
+
+ // Get an iterator to all vertices sharing name.
+ auto iterPair = this->names.equal_range(name);
+ for (auto it = iterPair.first; it != iterPair.second; ++it)
+ {
+ if (it->second == _vertex)
+ {
+ this->names.erase(it);
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ /// \brief Remove an existing vertex from the graph.
+ /// \param[in] _vertex The vertex to be removed.
+ /// \return True when the vertex was removed or false otherwise.
+ public: bool RemoveVertex(Vertex<V> &_vertex)
+ {
+ return this->RemoveVertex(_vertex.Id());
+ }
+
+ /// \brief Remove all vertices with name == _name.
+ /// \param[in] _name Name of the vertices to be removed.
+ /// \return The number of vertices removed.
+ public: size_t RemoveVertices(const std::string &_name)
+ {
+ size_t numVertices = this->names.count(_name);
+ size_t result = 0;
+ for (size_t i = 0; i < numVertices; ++i)
+ {
+ auto iter = this->names.find(_name);
+ if (this->RemoveVertex(iter->second))
+ ++result;
+ }
+
+ return result;
+ }
+
+ /// \brief Remove an existing edge from the graph. After the removal, it
+ /// won't be possible to reach any of the vertices from the edge, unless
+ /// there are other edges that connect the to vertices.
+ /// \param[in] _edge Id of the edge to be removed.
+ /// \return True when the edge was removed or false otherwise.
+ public: bool RemoveEdge(const EdgeId &_edge)
+ {
+ auto edgeIt = this->edges.find(_edge);
+ if (edgeIt == this->edges.end())
+ return false;
+
+ auto edgeVertices = edgeIt->second.Vertices();
+
+ // Unlink the edge.
+ for (auto const &v : {edgeVertices.first, edgeVertices.second})
+ {
+ if (edgeIt->second.From(v) != kNullId)
+ {
+ auto vertex = this->adjList.find(v);
+ assert(vertex != this->adjList.end());
+ vertex->second.erase(_edge);
+ }
+ }
+
+ this->edges.erase(_edge);
+
+ return true;
+ }
+
+ /// \brief Remove an existing edge from the graph. After the removal, it
+ /// won't be possible to reach any of the vertices from the edge, unless
+ /// there are other edges that connect the to vertices.
+ /// \param[in] _edge The edge to be removed.
+ /// \return True when the edge was removed or false otherwise.
+ public: bool RemoveEdge(EdgeType &_edge)
+ {
+ return this->RemoveEdge(_edge.Id());
+ }
+
+ /// \brief Get a reference to a vertex using its Id.
+ /// \param[in] _id The Id of the vertex.
+ /// \return A reference to the vertex with Id = _id or NullVertex if
+ /// not found.
+ public: const Vertex<V> &VertexFromId(const VertexId &_id) const
+ {
+ auto iter = this->vertices.find(_id);
+ if (iter == this->vertices.end())
+ return Vertex<V>::NullVertex;
+
+ return iter->second;
+ }
+
+ /// \brief Get a mutable reference to a vertex using its Id.
+ /// \param[in] _id The Id of the vertex.
+ /// \return A mutable reference to the vertex with Id = _id or NullVertex
+ /// if not found.
+ public: Vertex<V> &VertexFromId(const VertexId &_id)
+ {
+ auto iter = this->vertices.find(_id);
+ if (iter == this->vertices.end())
+ return Vertex<V>::NullVertex;
+
+ return iter->second;
+ }
+
+ /// \brief Get a reference to an edge using its Id.
+ /// \param[in] _id The Id of the edge.
+ /// \return A reference to the edge with Id = _id or NullEdge if
+ /// not found.
+ public: const EdgeType &EdgeFromId(const EdgeId &_id) const
+ {
+ auto iter = this->edges.find(_id);
+ if (iter == this->edges.end())
+ return EdgeType::NullEdge;
+
+ return iter->second;
+ }
+
+ /// \brief Stream insertion operator. The output uses DOT graph
+ /// description language.
+ /// \param[out] _out The output stream.
+ /// \param[in] _g Graph to write to the stream.
+ /// \ref https://en.wikipedia.org/wiki/DOT_(graph_description_language).
+ public: template<typename VV, typename EE, typename EEdgeType>
+ friend std::ostream &operator<<(std::ostream &_out,
+ const Graph<VV, EE, EEdgeType> &_g);
+
+ /// \brief Get an available Id to be assigned to a new vertex.
+ /// \return The next available Id or kNullId if there aren't ids available.
+ private: VertexId &NextVertexId()
+ {
+ while (this->vertices.find(this->nextVertexId) != this->vertices.end()
+ && this->nextVertexId < MAX_UI64)
+ {
+ ++this->nextVertexId;
+ }
+
+ return this->nextVertexId;
+ }
+
+ /// \brief Get an available Id to be assigned to a new edge.
+ /// \return The next available Id or kNullId if there aren't ids available.
+ private: VertexId &NextEdgeId()
+ {
+ while (this->edges.find(this->nextEdgeId) != this->edges.end() &&
+ this->nextEdgeId < MAX_UI64)
+ {
+ ++this->nextEdgeId;
+ }
+
+ return this->nextEdgeId;
+ }
+
+ /// \brief The next vertex Id to be assigned to a new vertex.
+ protected: VertexId nextVertexId = 0u;
+
+ /// \brief The next edge Id to be assigned to a new edge.
+ protected: VertexId nextEdgeId = 0u;
+
+ /// \brief The set of vertices.
+ private: std::map<VertexId, Vertex<V>> vertices;
+
+ /// \brief The set of edges.
+ private: std::map<EdgeId, EdgeType> edges;
+
+ /// \brief The adjacency list.
+ /// A map where the keys are vertex Ids. For each vertex (v)
+ /// with id (vId), the map value contains a set of edge Ids. Each of
+ /// the edges (e) with Id (eId) represents a connected path from (v) to
+ /// another vertex via (e).
+ private: std::map<VertexId, EdgeId_S> adjList;
+
+ /// \brief Association between names and vertices curently used.
+ private: std::multimap<std::string, VertexId> names;
+ };
+
+ /////////////////////////////////////////////////
+ /// Partial template specification for undirected edges.
+ template<typename VV, typename EE>
+ std::ostream &operator<<(std::ostream &_out,
+ const Graph<VV, EE, UndirectedEdge<EE>> &_g)
+ {
+ _out << "graph {" << std::endl;
+
+ // All vertices with the name and Id as a "label" attribute.
+ for (auto const &vertexMap : _g.Vertices())
+ {
+ auto vertex = vertexMap.second.get();
+ _out << vertex;
+ }
+
+ // All edges.
+ for (auto const &edgeMap : _g.Edges())
+ {
+ auto edge = edgeMap.second.get();
+ _out << edge;
+ }
+
+ _out << "}" << std::endl;
+
+ return _out;
+ }
+
+ /////////////////////////////////////////////////
+ /// Partial template specification for directed edges.
+ template<typename VV, typename EE>
+ std::ostream &operator<<(std::ostream &_out,
+ const Graph<VV, EE, DirectedEdge<EE>> &_g)
+ {
+ _out << "digraph {" << std::endl;
+
+ // All vertices with the name and Id as a "label" attribute.
+ for (auto const &vertexMap : _g.Vertices())
+ {
+ auto vertex = vertexMap.second.get();
+ _out << vertex;
+ }
+
+ // All edges.
+ for (auto const &edgeMap : _g.Edges())
+ {
+ auto edge = edgeMap.second.get();
+ _out << edge;
+ }
+
+ _out << "}" << std::endl;
+
+ return _out;
+ }
+
+ /// \def UndirectedGraph
+ /// \brief An undirected graph.
+ template<typename V, typename E>
+ using UndirectedGraph = Graph<V, E, UndirectedEdge<E>>;
+
+ /// \def DirectedGraph
+ /// \brief A directed graph.
+ template<typename V, typename E>
+ using DirectedGraph = Graph<V, E, DirectedEdge<E>>;
+}
+}
+}
+#endif
diff --git a/include/ignition/math/graph/GraphAlgorithms.hh b/include/ignition/math/graph/GraphAlgorithms.hh
new file mode 100644
index 0000000..b8796e0
--- /dev/null
+++ b/include/ignition/math/graph/GraphAlgorithms.hh
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_GRAPH_GRAPHALGORITHMS_HH_
+#define IGNITION_MATH_GRAPH_GRAPHALGORITHMS_HH_
+
+#include <functional>
+#include <list>
+#include <map>
+#include <queue>
+#include <stack>
+#include <utility>
+#include <vector>
+
+#include "ignition/math/graph/Graph.hh"
+#include "ignition/math/Helpers.hh"
+
+namespace ignition
+{
+namespace math
+{
+namespace graph
+{
+ /// \def CostInfo.
+ /// \brief Used in Dijkstra. For a given source vertex, this pair represents
+ /// the cost (first element) to reach a destination vertex (second element).
+ using CostInfo = std::pair<double, VertexId>;
+
+ /// \brief Breadth first sort (BFS).
+ /// Starting from the vertex == _from, it traverses the graph exploring the
+ /// neighbors first, before moving to the next level neighbors.
+ /// \param[in] _graph A graph.
+ /// \param[in] _from The starting vertex.
+ /// \return The vector of vertices Ids traversed in a breadth first manner.
+ template<typename V, typename E, typename EdgeType>
+ std::vector<VertexId> BreadthFirstSort(const Graph<V, E, EdgeType> &_graph,
+ const VertexId &_from)
+ {
+ // Create an auxiliary graph, where the data is just a boolean value that
+ // stores whether the vertex has been visited or not.
+ Graph<bool, E, EdgeType> visitorGraph;
+
+ // Copy the vertices (just the Id).
+ for (auto const &v : _graph.Vertices())
+ visitorGraph.AddVertex("", false, v.first);
+
+ // Copy the edges (without data).
+ for (auto const &e : _graph.Edges())
+ visitorGraph.AddEdge(e.second.get().Vertices(), E());
+
+ std::vector<VertexId> visited;
+ std::list<VertexId> pending = {_from};
+
+ while (!pending.empty())
+ {
+ auto vId = pending.front();
+ pending.pop_front();
+
+ // If the vertex has been visited, skip.
+ auto &vertex = visitorGraph.VertexFromId(vId);
+ if (vertex.Data())
+ continue;
+
+ visited.push_back(vId);
+ vertex.Data() = true;
+
+ // Add more vertices to visit if they haven't been visited yet.
+ auto adjacents = visitorGraph.AdjacentsFrom(vId);
+ for (auto const &adj : adjacents)
+ {
+ vId = adj.first;
+ auto &v = adj.second.get();
+ if (!v.Data())
+ pending.push_back(vId);
+ }
+ }
+
+ return visited;
+ }
+
+ /// \brief Depth first sort (DFS).
+ /// Starting from the vertex == _from, it visits the graph as far as
+ /// possible along each branch before backtracking.
+ /// \param[in] _graph A graph.
+ /// \param[in] _from The starting vertex.
+ /// \return The vector of vertices Ids visited in a depth first manner.
+ template<typename V, typename E, typename EdgeType>
+ std::vector<VertexId> DepthFirstSort(const Graph<V, E, EdgeType> &_graph,
+ const VertexId &_from)
+ {
+ // Create an auxiliary graph, where the data is just a boolean value that
+ // stores whether the vertex has been visited or not.
+ Graph<bool, E, EdgeType> visitorGraph;
+
+ // Copy the vertices (just the Id).
+ for (auto const &v : _graph.Vertices())
+ visitorGraph.AddVertex("", false, v.first);
+
+ // Copy the edges (without data).
+ for (auto const &e : _graph.Edges())
+ visitorGraph.AddEdge(e.second.get().Vertices(), E());
+
+ std::vector<VertexId> visited;
+ std::stack<VertexId> pending({_from});
+
+ while (!pending.empty())
+ {
+ auto vId = pending.top();
+ pending.pop();
+
+ // If the vertex has been visited, skip.
+ auto &vertex = visitorGraph.VertexFromId(vId);
+ if (vertex.Data())
+ continue;
+
+ visited.push_back(vId);
+ vertex.Data() = true;
+
+ // Add more vertices to visit if they haven't been visited yet.
+ auto adjacents = visitorGraph.AdjacentsFrom(vId);
+ for (auto const &adj : adjacents)
+ {
+ vId = adj.first;
+ auto &v = adj.second.get();
+ if (!v.Data())
+ pending.push(vId);
+ }
+ }
+
+ return visited;
+ }
+
+ /// \brief Dijkstra algorithm.
+ /// Find the shortest path between the vertices in a graph.
+ /// If only a graph and a source vertex is provided, the algorithm will
+ /// find shortest paths from the source vertex to all other vertices in the
+ /// graph. If an additional destination vertex is provided, the algorithm
+ /// will stop when the shortest path is found between the source and
+ /// destination vertex.
+ /// \param[in] _graph A graph.
+ /// \param[in] _from The starting vertex.
+ /// \param[in] _to Optional destination vertex.
+ /// \return A map where the keys are the destination vertices. For each
+ /// destination, the value is another pair, where the key is the shortest
+ /// cost from the origin vertex. The value is the previous neighbor Id in the
+ /// shortest path.
+ /// Note: In the case of providing a destination vertex, only the entry in the
+ /// map with key = _to should be used. The rest of the map may contain
+ /// incomplete information. If you want all shortest paths to all other
+ /// vertices, please remove the destination vertex.
+ /// If the source or destination vertex don't exist, the function will return
+ /// an empty map.
+ ///
+ /// E.g.: Given the following undirected graph, g, with five vertices:
+ ///
+ /// (6) |
+ /// 0-------1 |
+ /// | /|\ |
+ /// | / | \(5) |
+ /// | (2)/ | \ |
+ /// | / | 2 |
+ /// (1)| / (2)| / |
+ /// | / | /(5) |
+ /// |/ |/ |
+ /// 3-------4 |
+ /// (1) |
+ ///
+ /// This is the resut of Dijkstra(g, 0):
+ ///
+ /// ================================
+ /// | Dst | Cost | Previous vertex |
+ /// ================================
+ /// | 0 | 0 | 0 |
+ /// | 1 | 3 | 3 |
+ /// | 2 | 7 | 4 |
+ /// | 3 | 1 | 0 |
+ /// | 4 | 2 | 3 |
+ /// ================================
+ ///
+ /// This is the result of Dijkstra(g, 0, 3):
+ ///
+ /// ================================
+ /// | Dst | Cost | Previous vertex |
+ /// ================================
+ /// | 0 | 0 | 0 |
+ /// | 1 |ignore| ignore |
+ /// | 2 |ignore| ignore |
+ /// | 3 | 1 | 0 |
+ /// | 4 |ignore| ignore |
+ /// ================================
+ template<typename V, typename E, typename EdgeType>
+ std::map<VertexId, CostInfo> Dijkstra(const Graph<V, E, EdgeType> &_graph,
+ const VertexId &_from,
+ const VertexId &_to = kNullId)
+ {
+ auto allVertices = _graph.Vertices();
+
+ // Sanity check: The source vertex should exist.
+ if (allVertices.find(_from) == allVertices.end())
+ {
+ std::cerr << "Vertex [" << _from << "] Not found" << std::endl;
+ return {};
+ }
+
+ // Sanity check: The destination vertex should exist (if used).
+ if (_to != kNullId &&
+ allVertices.find(_to) == allVertices.end())
+ {
+ std::cerr << "Vertex [" << _from << "] Not found" << std::endl;
+ return {};
+ }
+
+ // Store vertices that are being preprocessed.
+ std::priority_queue<CostInfo,
+ std::vector<CostInfo>, std::greater<CostInfo>> pq;
+
+ // Create a map for distances and next neightbor and initialize all
+ // distances as infinite.
+ std::map<VertexId, CostInfo> dist;
+ for (auto const &v : allVertices)
+ {
+ auto id = v.first;
+ dist[id] = std::make_pair(MAX_D, kNullId);
+ }
+
+ // Insert _from in the priority queue and initialize its distance as 0.
+ pq.push(std::make_pair(0.0, _from));
+ dist[_from] = std::make_pair(0.0, _from);
+
+ while (!pq.empty())
+ {
+ // This is the minimum distance vertex.
+ VertexId u = pq.top().second;
+
+ // Shortcut: Destination vertex found, exiting.
+ if (_to != kNullId && _to == u)
+ break;
+
+ pq.pop();
+
+ for (auto const &edgePair : _graph.IncidentsFrom(u))
+ {
+ const auto &edge = edgePair.second.get();
+ const auto &v = edge.From(u);
+ double weight = edge.Weight();
+
+ // If there is shorted path to v through u.
+ if (dist[v].first > dist[u].first + weight)
+ {
+ // Updating distance of v.
+ dist[v] = std::make_pair(dist[u].first + weight, u);
+ pq.push(std::make_pair(dist[v].first, v));
+ }
+ }
+ }
+
+ return dist;
+ }
+
+ /// \brief Calculate the connected components of an undirected graph.
+ /// A connected component of an undirected graph is a subgraph in which any
+ /// two vertices are connected to each other by paths, and which is connected
+ /// to no additional vertices in the supergraph.
+ /// \ref https://en.wikipedia.org/wiki/Connected_component_(graph_theory)
+ /// \param[in] _graph A graph.
+ /// \return A vector of graphs. Each element of the graph is a component
+ /// (subgraph) of the original graph.
+ template<typename V, typename E>
+ std::vector<UndirectedGraph<V, E>> ConnectedComponents(
+ const UndirectedGraph<V, E> &_graph)
+ {
+ std::map<VertexId, unsigned int> visited;
+ unsigned int componentCount = 0;
+
+ for (auto const &v : _graph.Vertices())
+ {
+ if (visited.find(v.first) == visited.end())
+ {
+ auto component = BreadthFirstSort(_graph, v.first);
+ for (auto const &vId : component)
+ visited[vId] = componentCount;
+ ++componentCount;
+ }
+ }
+
+ std::vector<UndirectedGraph<V, E>> res(componentCount);
+
+ // Create the vertices.
+ for (auto const &vPair : _graph.Vertices())
+ {
+ const auto &v = vPair.second.get();
+ const auto &componentId = visited[v.Id()];
+ res[componentId].AddVertex(v.Name(), v.Data(), v.Id());
+ }
+
+ // Create the edges.
+ for (auto const &ePair : _graph.Edges())
+ {
+ const auto &e = ePair.second.get();
+ const auto &vertices = e.Vertices();
+ const auto &componentId = visited[vertices.first];
+ res[componentId].AddEdge(vertices, e.Data(), e.Weight());
+ }
+
+ return res;
+ }
+}
+}
+}
+#endif
diff --git a/include/ignition/math/graph/Vertex.hh b/include/ignition/math/graph/Vertex.hh
new file mode 100644
index 0000000..20f39dc
--- /dev/null
+++ b/include/ignition/math/graph/Vertex.hh
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_GRAPH_VERTEX_HH_
+#define IGNITION_MATH_GRAPH_VERTEX_HH_
+
+// uint64_t
+#include <cstdint>
+#include <functional>
+#include <iostream>
+#include <map>
+#include <string>
+#include <utility>
+
+#include <ignition/math/Helpers.hh>
+
+namespace ignition
+{
+namespace math
+{
+namespace graph
+{
+ /// \def VertexId.
+ /// \brief The unique Id of each vertex.
+ using VertexId = uint64_t;
+
+ /// \def VertexId_P
+ /// \brief A pair of two vertex Ids.
+ using VertexId_P = std::pair<VertexId, VertexId>;
+
+ /// \brief Represents an invalid Id.
+ static const VertexId kNullId = MAX_UI64;
+
+ /// \brief A vertex of a graph. It stores user information, an optional name,
+ /// and keeps an internal unique Id. This class does not enforce to choose a
+ /// unique name.
+ template<typename V>
+ class Vertex
+ {
+ /// \brief An invalid vertex.
+ public: static Vertex<V> NullVertex;
+
+ /// \brief Constructor.
+ /// \param[in] _name Non-unique vertex name.
+ /// \param[in] _data User information.
+ /// \param[in] _id Optional unique id.
+ public: Vertex(const std::string &_name,
+ const V &_data = V(),
+ const VertexId _id = kNullId)
+ : name(_name),
+ data(_data),
+ id(_id)
+ {
+ }
+
+ /// \brief Retrieve the user information.
+ /// \return Reference to the user information.
+ public: const V &Data() const
+ {
+ return this->data;
+ }
+
+ /// \brief Get a mutable reference to the user information.
+ /// \return Mutable reference to the user information.
+ public: V &Data()
+ {
+ return this->data;
+ }
+
+ /// \brief Get the vertex Id.
+ /// \return The vertex Id.
+ public: VertexId Id() const
+ {
+ return this->id;
+ }
+
+ /// \brief Get the vertex name.
+ /// \return The vertex name.
+ public: std::string Name() const
+ {
+ return this->name;
+ }
+
+ /// \brief Whether the vertex is considered valid or not (id==kNullId).
+ /// \return True when the vertex is valid or false otherwise (id==kNullId)
+ public: bool Valid() const
+ {
+ return this->id != kNullId;
+ }
+
+ /// \brief Stream insertion operator. The output uses DOT graph
+ /// description language.
+ /// \param[out] _out The output stream.
+ /// \param[in] _v Vertex to write to the stream.
+ /// \ref https://en.wikipedia.org/wiki/DOT_(graph_description_language).
+ public: friend std::ostream &operator<<(std::ostream &_out,
+ const Vertex<V> &_v)
+ {
+ _out << " " << _v.Id() << " [label=\"" << _v.Name()
+ << " (" << _v.Id() << ")\"];" << std::endl;
+ return _out;
+ }
+
+ /// \brief Non-unique vertex name.
+ private: std::string name = "";
+
+ /// \brief User information.
+ private: V data;
+
+ /// \brief Unique vertex Id.
+ private: VertexId id = kNullId;
+ };
+
+ /// \brief An invalid vertex.
+ template<typename V>
+ Vertex<V> Vertex<V>::NullVertex("__null__", V(), kNullId);
+
+ /// \def VertexRef_M
+ /// \brief Map of vertices. The key is the vertex Id. The value is a
+ /// reference to the vertex.
+ template<typename V>
+ using VertexRef_M =
+ std::map<VertexId, std::reference_wrapper<const Vertex<V>>>;
+}
+}
+}
+#endif
diff --git a/include/ignition/math/math.hh.in b/include/ignition/math/math.hh.in
new file mode 100644
index 0000000..4b76db8
--- /dev/null
+++ b/include/ignition/math/math.hh.in
@@ -0,0 +1,3 @@
+// Automatically generated
+#include <ignition/math/config.hh>
+${ign_headers}
diff --git a/src/Angle.cc b/src/Angle.cc
new file mode 100644
index 0000000..def990b
--- /dev/null
+++ b/src/Angle.cc
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 "ignition/math/Helpers.hh"
+#include "ignition/math/Angle.hh"
+
+using namespace ignition;
+using namespace math;
+
+const Angle Angle::Zero = math::Angle(0);
+const Angle Angle::Pi = math::Angle(IGN_PI);
+const Angle Angle::HalfPi = math::Angle(IGN_PI_2);
+const Angle Angle::TwoPi = math::Angle(IGN_PI * 2.0);
+
+//////////////////////////////////////////////////
+Angle::Angle()
+{
+ this->value = 0;
+}
+
+//////////////////////////////////////////////////
+Angle::Angle(const double _radian)
+{
+ this->value = _radian;
+}
+
+//////////////////////////////////////////////////
+Angle::Angle(const Angle &_angle)
+{
+ this->value = _angle.value;
+}
+
+//////////////////////////////////////////////////
+Angle::~Angle()
+{
+}
+
+//////////////////////////////////////////////////
+void Angle::Radian(double _radian)
+{
+ this->value = _radian;
+}
+
+//////////////////////////////////////////////////
+void Angle::Degree(double _degree)
+{
+ this->value = _degree * IGN_PI / 180.0;
+}
+
+//////////////////////////////////////////////////
+double Angle::Radian() const
+{
+ return this->value;
+}
+
+//////////////////////////////////////////////////
+double Angle::Degree() const
+{
+ return this->value * 180.0 / IGN_PI;
+}
+
+//////////////////////////////////////////////////
+void Angle::Normalize()
+{
+ this->value = atan2(sin(this->value), cos(this->value));
+}
+
+//////////////////////////////////////////////////
+Angle Angle::operator-(const Angle &angle) const
+{
+ return Angle(this->value - angle.value);
+}
+
+//////////////////////////////////////////////////
+Angle Angle::operator+(const Angle &angle) const
+{
+ return Angle(this->value + angle.value);
+}
+
+//////////////////////////////////////////////////
+Angle Angle::operator*(const Angle &angle) const
+{
+ return Angle(this->value * angle.value);
+}
+
+//////////////////////////////////////////////////
+Angle Angle::operator/(const Angle &angle) const
+{
+ return Angle(this->value / angle.value);
+}
+
+//////////////////////////////////////////////////
+Angle Angle::operator-=(const Angle &angle)
+{
+ this->value -= angle.value;
+ return *this;
+}
+
+//////////////////////////////////////////////////
+Angle Angle::operator+=(const Angle &angle)
+{
+ this->value += angle.value;
+ return *this;
+}
+
+//////////////////////////////////////////////////
+Angle Angle::operator*=(const Angle &angle)
+{
+ this->value *= angle.value;
+ return *this;
+}
+
+//////////////////////////////////////////////////
+Angle Angle::operator/=(const Angle &angle)
+{
+ this->value /= angle.value;
+ return *this;
+}
+
+//////////////////////////////////////////////////
+bool Angle::operator==(const Angle &angle) const
+{
+ return equal(this->value, angle.value, 0.001);
+}
+
+//////////////////////////////////////////////////
+bool Angle::operator!=(const Angle &angle) const
+{
+ return !(*this == angle);
+}
+
+//////////////////////////////////////////////////
+bool Angle::operator<(const Angle &angle) const
+{
+ return this->value < angle.value;
+}
+
+//////////////////////////////////////////////////
+bool Angle::operator<=(const Angle &angle) const
+{
+ return this->value < angle.value || math::equal(this->value, angle.value);
+}
+
+//////////////////////////////////////////////////
+bool Angle::operator>(const Angle &angle) const
+{
+ return this->value > angle.value;
+}
+
+//////////////////////////////////////////////////
+bool Angle::operator>=(const Angle &angle) const
+{
+ return this->value > angle.value || math::equal(this->value, angle.value);
+}
+
+//////////////////////////////////////////////////
+double Angle::operator()() const
+{
+ return this->value;
+}
diff --git a/src/Angle_TEST.cc b/src/Angle_TEST.cc
new file mode 100644
index 0000000..a6e6f91
--- /dev/null
+++ b/src/Angle_TEST.cc
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include <cmath>
+
+#include "ignition/math/Helpers.hh"
+#include "ignition/math/Angle.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(AngleTest, Angle)
+{
+ math::Angle angle1;
+ EXPECT_TRUE(math::equal(0.0, angle1.Radian()));
+
+ angle1.Degree(180.0);
+ EXPECT_TRUE(angle1 == IGN_PI);
+
+ EXPECT_FALSE(angle1 == IGN_PI + 0.1);
+ EXPECT_TRUE(angle1 == IGN_PI + 0.0001);
+ EXPECT_TRUE(angle1 == IGN_PI - 0.0001);
+ EXPECT_TRUE(math::Angle(0) == math::Angle(0));
+ EXPECT_TRUE(math::Angle(0) == math::Angle(0.001));
+
+ angle1 = math::Angle(0.1) - math::Angle(0.3);
+ EXPECT_TRUE(angle1 == -0.2);
+
+ math::Angle angle(0.5);
+ EXPECT_TRUE(math::equal(0.5, angle.Radian()));
+
+ angle.Radian(IGN_PI);
+ EXPECT_TRUE(math::equal(IGN_RTOD(IGN_PI), angle.Degree()));
+
+ angle.Normalize();
+ EXPECT_TRUE(math::equal(IGN_RTOD(IGN_PI), angle.Degree()));
+
+ angle = math::Angle(0.1) + math::Angle(0.2);
+ EXPECT_TRUE(math::equal(0.3, angle.Radian()));
+
+ angle = math::Angle(0.1) * math::Angle(0.2);
+ EXPECT_TRUE(math::equal(0.02, angle.Radian()));
+
+ angle = math::Angle(0.1) / math::Angle(0.2);
+ EXPECT_TRUE(math::equal(0.5, angle.Radian()));
+
+ angle -= math::Angle(0.1);
+ EXPECT_TRUE(math::equal(0.4, angle.Radian()));
+
+ angle += math::Angle(0.2);
+ EXPECT_TRUE(math::equal(0.6, angle.Radian()));
+
+ angle *= math::Angle(0.5);
+ EXPECT_TRUE(math::equal(0.3, angle.Radian()));
+
+ angle /= math::Angle(0.1);
+ EXPECT_TRUE(math::equal(3.0, angle.Radian()));
+ EXPECT_TRUE(angle == math::Angle(3));
+ EXPECT_TRUE(angle != math::Angle(2));
+ EXPECT_TRUE(angle < math::Angle(4));
+ EXPECT_TRUE(angle > math::Angle(2));
+ EXPECT_TRUE(angle >= math::Angle(3));
+ EXPECT_TRUE(angle <= math::Angle(3));
+
+ angle = 1.2;
+ EXPECT_TRUE(angle <= 1.21);
+ EXPECT_FALSE(angle <= 1.19);
+ EXPECT_TRUE(angle <= 1.2);
+ EXPECT_FALSE(angle <= -1.19);
+
+ EXPECT_TRUE(math::Angle(1.2) <= math::Angle(1.2000000001));
+ EXPECT_TRUE(math::Angle(1.2000000001) <= math::Angle(1.2));
+
+ angle = 1.2;
+ EXPECT_FALSE(angle >= 1.21);
+ EXPECT_TRUE(angle >= 1.19);
+ EXPECT_TRUE(angle >= 1.2);
+ EXPECT_TRUE(angle >= -1.19);
+
+ EXPECT_TRUE(math::Angle(1.2) >= math::Angle(1.2000000001));
+ EXPECT_TRUE(math::Angle(1.2000000001) >= math::Angle(1.2));
+}
+
+/////////////////////////////////////////////////
+TEST(AngleTest, StreamExtraction)
+{
+ math::Angle angle;
+ std::istringstream stream("1.25");
+
+ EXPECT_NEAR(*angle, 0, 1e-2);
+
+ stream >> angle;
+ EXPECT_NEAR(*angle, 1.25, 1e-2);
+}
+
+/////////////////////////////////////////////////
+TEST(AngleTest, OperatorStreamOut)
+{
+ math::Angle a(0.1);
+ std::ostringstream stream;
+ stream << a;
+ EXPECT_EQ(stream.str(), "0.1");
+}
diff --git a/src/Box.cc b/src/Box.cc
new file mode 100644
index 0000000..0dd587e
--- /dev/null
+++ b/src/Box.cc
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <cmath>
+#include <ignition/math/Box.hh>
+
+#include "BoxPrivate.hh"
+
+using namespace ignition;
+using namespace math;
+
+//////////////////////////////////////////////////
+Box::Box()
+: dataPtr(new BoxPrivate)
+{
+}
+
+//////////////////////////////////////////////////
+Box::Box(double _vec1X, double _vec1Y, double _vec1Z,
+ double _vec2X, double _vec2Y, double _vec2Z)
+: dataPtr(new BoxPrivate)
+{
+ this->dataPtr->min.Set(_vec1X, _vec1Y, _vec1Z);
+ this->dataPtr->max.Set(_vec2X, _vec2Y, _vec2Z);
+
+ this->dataPtr->min.Min(math::Vector3d(_vec2X, _vec2Y, _vec2Z));
+ this->dataPtr->max.Max(math::Vector3d(_vec1X, _vec1Y, _vec1Z));
+}
+
+//////////////////////////////////////////////////
+Box::Box(const Vector3d &_vec1, const Vector3d &_vec2)
+: dataPtr(new BoxPrivate)
+{
+ this->dataPtr->min = _vec1;
+ this->dataPtr->min.Min(_vec2);
+
+ this->dataPtr->max = _vec2;
+ this->dataPtr->max.Max(_vec1);
+}
+
+//////////////////////////////////////////////////
+Box::Box(const Box &_b)
+: dataPtr(new BoxPrivate)
+{
+ this->dataPtr->min = _b.dataPtr->min;
+ this->dataPtr->max = _b.dataPtr->max;
+}
+
+//////////////////////////////////////////////////
+Box::~Box()
+{
+ delete this->dataPtr;
+ this->dataPtr = NULL;
+}
+
+//////////////////////////////////////////////////
+double Box::XLength() const
+{
+ return std::max(0.0, this->dataPtr->max.X() - this->dataPtr->min.X());
+}
+
+//////////////////////////////////////////////////
+double Box::YLength() const
+{
+ return std::max(0.0, this->dataPtr->max.Y() - this->dataPtr->min.Y());
+}
+
+//////////////////////////////////////////////////
+double Box::ZLength() const
+{
+ return std::max(0.0, this->dataPtr->max.Z() - this->dataPtr->min.Z());
+}
+
+//////////////////////////////////////////////////
+math::Vector3d Box::Size() const
+{
+ return math::Vector3d(this->XLength(),
+ this->YLength(),
+ this->ZLength());
+}
+
+//////////////////////////////////////////////////
+math::Vector3d Box::Center() const
+{
+ return 0.5 * this->dataPtr->min + 0.5 * this->dataPtr->max;
+}
+
+
+//////////////////////////////////////////////////
+void Box::Merge(const Box &_box)
+{
+ this->dataPtr->min.Min(_box.dataPtr->min);
+ this->dataPtr->max.Max(_box.dataPtr->max);
+}
+
+//////////////////////////////////////////////////
+Box &Box::operator =(const Box &_b)
+{
+ this->dataPtr->max = _b.dataPtr->max;
+ this->dataPtr->min = _b.dataPtr->min;
+
+ return *this;
+}
+
+//////////////////////////////////////////////////
+Box Box::operator+(const Box &_b) const
+{
+ Box result(*this);
+ result += _b;
+ return result;
+}
+
+//////////////////////////////////////////////////
+const Box &Box::operator+=(const Box &_b)
+{
+ this->dataPtr->min.Min(_b.dataPtr->min);
+ this->dataPtr->max.Max(_b.dataPtr->max);
+ return *this;
+}
+
+//////////////////////////////////////////////////
+bool Box::operator==(const Box &_b) const
+{
+ return this->dataPtr->min == _b.dataPtr->min &&
+ this->dataPtr->max == _b.dataPtr->max;
+}
+
+//////////////////////////////////////////////////
+bool Box::operator!=(const Box &_b) const
+{
+ return !(*this == _b);
+}
+
+//////////////////////////////////////////////////
+Box Box::operator-(const Vector3d &_v)
+{
+ return Box(this->dataPtr->min - _v, this->dataPtr->max - _v);
+}
+
+//////////////////////////////////////////////////
+bool Box::Intersects(const Box &_box) const
+{
+ // Check the six separating planes.
+ if (this->Max().X() < _box.Min().X())
+ return false;
+ if (this->Max().Y() < _box.Min().Y())
+ return false;
+ if (this->Max().Z() < _box.Min().Z())
+ return false;
+
+ if (this->Min().X() > _box.Max().X())
+ return false;
+ if (this->Min().Y() > _box.Max().Y())
+ return false;
+ if (this->Min().Z() > _box.Max().Z())
+ return false;
+
+ // Otherwise the two boxes must intersect.
+ return true;
+}
+
+//////////////////////////////////////////////////
+const Vector3d &Box::Min() const
+{
+ return this->dataPtr->min;
+}
+
+//////////////////////////////////////////////////
+const Vector3d &Box::Max() const
+{
+ return this->dataPtr->max;
+}
+
+//////////////////////////////////////////////////
+Vector3d &Box::Min()
+{
+ return this->dataPtr->min;
+}
+
+//////////////////////////////////////////////////
+Vector3d &Box::Max()
+{
+ return this->dataPtr->max;
+}
+
+//////////////////////////////////////////////////
+bool Box::Contains(const Vector3d &_p) const
+{
+ return _p.X() >= this->dataPtr->min.X() && _p.X() <= this->dataPtr->max.X() &&
+ _p.Y() >= this->dataPtr->min.Y() && _p.Y() <= this->dataPtr->max.Y() &&
+ _p.Z() >= this->dataPtr->min.Z() && _p.Z() <= this->dataPtr->max.Z();
+}
+
+//////////////////////////////////////////////////
+bool Box::ClipLine(const int _d, const Line3d &_line,
+ double &_low, double &_high) const
+{
+ // dimLow and dimHigh are the results we're calculating for this
+ // current dimension.
+ double dimLow, dimHigh;
+
+ // Find the point of intersection in this dimension only as a fraction of
+ // the total vector http://youtu.be/USjbg5QXk3g?t=3m12s
+ dimLow = (this->dataPtr->min[_d] - _line[0][_d]) /
+ (_line[1][_d] - _line[0][_d]);
+
+ dimHigh = (this->dataPtr->max[_d] - _line[0][_d]) /
+ (_line[1][_d] - _line[0][_d]);
+
+ // Make sure low is less than high
+ if (dimHigh < dimLow)
+ std::swap(dimHigh, dimLow);
+
+ // If this dimension's high is less than the low we got then we definitely
+ // missed. http://youtu.be/USjbg5QXk3g?t=7m16s
+ if (dimHigh < _low)
+ return false;
+
+ // Likewise if the low is less than the high.
+ if (dimLow > _high)
+ return false;
+
+ // Add the clip from this dimension to the previous results
+ // http://youtu.be/USjbg5QXk3g?t=5m32s
+ if (std::isfinite(dimLow))
+ _low = std::max(dimLow, _low);
+
+ if (std::isfinite(dimHigh))
+ _high = std::min(dimHigh, _high);
+
+ return true;
+}
+
+/////////////////////////////////////////////////
+bool Box::IntersectCheck(const Vector3d &_origin, const Vector3d &_dir,
+ const double _min, const double _max) const
+{
+ return std::get<0>(this->Intersect(_origin, _dir, _min, _max));
+}
+
+/////////////////////////////////////////////////
+std::tuple<bool, double> Box::IntersectDist(const Vector3d &_origin,
+ const Vector3d &_dir, const double _min, const double _max) const
+{
+ return std::make_tuple(
+ std::get<0>(this->Intersect(_origin, _dir, _min, _max)),
+ std::get<1>(this->Intersect(_origin, _dir, _min, _max)));
+}
+
+/////////////////////////////////////////////////
+std::tuple<bool, double, Vector3d> Box::Intersect(
+ const Vector3d &_origin, const Vector3d &_dir,
+ const double _min, const double _max) const
+{
+ Vector3d dir = _dir;
+ dir.Normalize();
+ return this->Intersect(Line3d(_origin + dir * _min, _origin + dir * _max));
+}
+
+/////////////////////////////////////////////////
+// Find the intersection of a line from v0 to v1 and an
+// axis-aligned bounding box http://www.youtube.com/watch?v=USjbg5QXk3g
+std::tuple<bool, double, Vector3d> Box::Intersect(const Line3d &_line) const
+{
+ // low and high are the results from all clipping so far.
+ // We'll write our results back out to those parameters.
+ double low = 0;
+ double high = 1;
+
+ if (!this->ClipLine(0, _line, low, high))
+ return std::make_tuple(false, 0, Vector3d::Zero);
+
+ if (!this->ClipLine(1, _line, low, high))
+ return std::make_tuple(false, 0, Vector3d::Zero);
+
+ if (!this->ClipLine(2, _line, low, high))
+ return std::make_tuple(false, 0, Vector3d::Zero);
+
+ // The formula for I: http://youtu.be/USjbg5QXk3g?t=6m24s
+ Vector3d intersection = _line[0] + ((_line[1] - _line[0]) * low);
+
+ return std::make_tuple(true, _line[0].Distance(intersection), intersection);
+}
diff --git a/src/BoxPrivate.hh b/src/BoxPrivate.hh
new file mode 100644
index 0000000..583711b
--- /dev/null
+++ b/src/BoxPrivate.hh
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_BOXPRIVATE_HH_
+#define IGNITION_MATH_BOXPRIVATE_HH_
+
+#include <ignition/math/Vector3.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \internal
+ /// \brief Private data for Box class
+ class BoxPrivate
+ {
+ /// \brief Minimum corner of the box
+ public: Vector3d min = Vector3d(MAX_D, MAX_D, MAX_D);
+
+ /// \brief Maximum corner of the box
+ public: Vector3d max = Vector3d(LOW_D, LOW_D, LOW_D);
+ };
+ }
+}
+#endif
diff --git a/src/Box_TEST.cc b/src/Box_TEST.cc
new file mode 100644
index 0000000..81b636d
--- /dev/null
+++ b/src/Box_TEST.cc
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+#include <cmath>
+
+#include "ignition/math/Box.hh"
+
+using namespace ignition;
+
+class myBox : public math::Box
+{
+ public: myBox()
+ : math::Box()
+ {}
+};
+
+class ExampleBox : public ::testing::Test
+{
+ protected:
+ virtual void SetUp()
+ {
+ box = math::Box(math::Vector3d(0, -1, 2), math::Vector3d(1, -2, 3));
+ }
+
+ math::Box box;
+};
+
+/////////////////////////////////////////////////
+TEST(BoxTest, Inherit)
+{
+ myBox *box = NULL;
+
+ {
+ box = new myBox();
+ EXPECT_TRUE(box != NULL);
+ }
+
+ EXPECT_TRUE(box->Min() == math::Vector3d(math::MAX_D, math::MAX_D,
+ math::MAX_D));
+ EXPECT_TRUE(box->Max() == math::Vector3d(math::LOW_D, math::LOW_D,
+ math::LOW_D));
+
+ {
+ delete box;
+ box = NULL;
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(BoxTest, EmptyConstructorNew)
+{
+ math::Box *box = NULL;
+
+ {
+ box = new math::Box;
+ EXPECT_TRUE(box != NULL);
+ }
+
+ EXPECT_TRUE(box->Min() == math::Vector3d(math::MAX_D, math::MAX_D,
+ math::MAX_D));
+ EXPECT_TRUE(box->Max() == math::Vector3d(math::LOW_D, math::LOW_D,
+ math::LOW_D));
+
+ {
+ delete box;
+ box = NULL;
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(BoxTest, EmptyConstructor)
+{
+ math::Box box;
+ EXPECT_TRUE(box.Min() == math::Vector3d(math::MAX_D, math::MAX_D,
+ math::MAX_D));
+ EXPECT_TRUE(box.Max() == math::Vector3d(math::LOW_D, math::LOW_D,
+ math::LOW_D));
+}
+
+/////////////////////////////////////////////////
+TEST_F(ExampleBox, Constructor)
+{
+ EXPECT_EQ(box.Min(), math::Vector3d(0, -2, 2));
+ EXPECT_EQ(box.Max(), math::Vector3d(1, -1, 3));
+}
+
+/////////////////////////////////////////////////
+TEST_F(ExampleBox, CopyConstructor)
+{
+ math::Box box1(box);
+ EXPECT_TRUE(box1.Min() == box.Min());
+ EXPECT_TRUE(box1.Max() == box.Max());
+}
+
+/////////////////////////////////////////////////
+TEST_F(ExampleBox, ManuallySet)
+{
+ math::Box box1;
+ box1.Min().Set(-2, 2, 3);
+ box1.Max().Set(0, 0, 3);
+
+ box1 += box;
+ EXPECT_DOUBLE_EQ(box1.Min().X(), -2);
+ EXPECT_DOUBLE_EQ(box1.Min().Y(), -2);
+ EXPECT_DOUBLE_EQ(box1.Min().Z(), 2);
+
+ EXPECT_DOUBLE_EQ(box1.Max().X(), 1);
+ EXPECT_DOUBLE_EQ(box1.Max().Y(), 0);
+ EXPECT_DOUBLE_EQ(box1.Max().Z(), 3);
+
+ math::Box box2;
+ box2.Max().Set(1, 1, 1);
+ EXPECT_DOUBLE_EQ(box2.Size().X(), 0);
+ EXPECT_DOUBLE_EQ(box2.Size().Y(), 0);
+ EXPECT_DOUBLE_EQ(box2.Size().Z(), 0);
+
+ math::Box box3;
+ box3.Min().Set(-1, -1, -1);
+ EXPECT_DOUBLE_EQ(box3.Size().X(), 0);
+ EXPECT_DOUBLE_EQ(box3.Size().Y(), 0);
+ EXPECT_DOUBLE_EQ(box3.Size().Z(), 0);
+}
+
+/////////////////////////////////////////////////
+TEST_F(ExampleBox, Length)
+{
+ EXPECT_DOUBLE_EQ(box.XLength(), 1);
+ EXPECT_DOUBLE_EQ(box.YLength(), 1);
+ EXPECT_DOUBLE_EQ(box.ZLength(), 1);
+}
+
+/////////////////////////////////////////////////
+TEST_F(ExampleBox, Size)
+{
+ EXPECT_TRUE(box.Size() == math::Vector3d(1, 1, 1));
+}
+
+/////////////////////////////////////////////////
+TEST_F(ExampleBox, Center)
+{
+ EXPECT_TRUE(box.Center() == math::Vector3d(0.5, -1.5, 2.5));
+}
+
+/////////////////////////////////////////////////
+TEST(BoxTest, MergeEmpty)
+{
+ math::Box box1;
+ math::Box box2;
+
+ box1.Merge(box2);
+ EXPECT_DOUBLE_EQ(box1.Min().X(), math::MAX_D);
+ EXPECT_DOUBLE_EQ(box1.Min().Y(), math::MAX_D);
+ EXPECT_DOUBLE_EQ(box1.Min().Z(), math::MAX_D);
+
+ EXPECT_DOUBLE_EQ(box1.Max().X(), math::LOW_D);
+ EXPECT_DOUBLE_EQ(box1.Max().Y(), math::LOW_D);
+ EXPECT_DOUBLE_EQ(box1.Max().Z(), math::LOW_D);
+}
+
+/////////////////////////////////////////////////
+TEST(BoxTest, DefaultConstructor)
+{
+ math::Box defaultBox1, defaultBox2;
+
+ EXPECT_DOUBLE_EQ(defaultBox1.Size().X(), 0);
+ EXPECT_DOUBLE_EQ(defaultBox1.Size().Y(), 0);
+ EXPECT_DOUBLE_EQ(defaultBox1.Size().Z(), 0);
+
+ EXPECT_DOUBLE_EQ(defaultBox1.XLength(), 0);
+ EXPECT_DOUBLE_EQ(defaultBox1.YLength(), 0);
+ EXPECT_DOUBLE_EQ(defaultBox1.ZLength(), 0);
+
+ EXPECT_DOUBLE_EQ(defaultBox1.Center().X(), 0);
+ EXPECT_DOUBLE_EQ(defaultBox1.Center().Y(), 0);
+ EXPECT_DOUBLE_EQ(defaultBox1.Center().Z(), 0);
+
+ EXPECT_FALSE(defaultBox1.Intersects(defaultBox2));
+
+ EXPECT_FALSE(defaultBox1.Intersects(math::Box(
+ math::Vector3d(0, 0, 0), math::Vector3d(1, 1, 1))));
+
+ EXPECT_FALSE(defaultBox2.Contains(math::Vector3d::Zero));
+}
+
+/////////////////////////////////////////////////
+TEST(BoxTest, Minus)
+{
+ math::Box box1(1, 2, 3, 4, 5, 6);
+ math::Vector3d sub(1, 1, 1);
+
+ math::Box box2 = box1 - sub;
+ EXPECT_EQ(box2.Min(), box1.Min() - sub);
+ EXPECT_EQ(box2.Max(), box1.Max() - sub);
+}
+
+/////////////////////////////////////////////////
+TEST(BoxTest, PlusEmpty)
+{
+ math::Box box1;
+ math::Box box2;
+
+ box1 += box2;
+ EXPECT_DOUBLE_EQ(box1.Min().X(), math::MAX_D);
+ EXPECT_DOUBLE_EQ(box1.Min().Y(), math::MAX_D);
+ EXPECT_DOUBLE_EQ(box1.Min().Z(), math::MAX_D);
+
+ EXPECT_DOUBLE_EQ(box1.Max().X(), math::LOW_D);
+ EXPECT_DOUBLE_EQ(box1.Max().Y(), math::LOW_D);
+ EXPECT_DOUBLE_EQ(box1.Max().Z(), math::LOW_D);
+
+ math::Box box3 = box2 + box1;
+ EXPECT_DOUBLE_EQ(box3.Min().X(), math::MAX_D);
+ EXPECT_DOUBLE_EQ(box3.Min().Y(), math::MAX_D);
+ EXPECT_DOUBLE_EQ(box3.Min().Z(), math::MAX_D);
+
+ EXPECT_DOUBLE_EQ(box3.Max().X(), math::LOW_D);
+ EXPECT_DOUBLE_EQ(box3.Max().Y(), math::LOW_D);
+ EXPECT_DOUBLE_EQ(box3.Max().Z(), math::LOW_D);
+}
+
+/////////////////////////////////////////////////
+TEST_F(ExampleBox, Merge)
+{
+ box.Merge(math::Box(math::Vector3d(-1, -1, -1), math::Vector3d(2, 2, 2)));
+ EXPECT_TRUE(box == math::Box(math::Vector3d(-1, -2, -1),
+ math::Vector3d(2, 2, 3)));
+}
+
+/////////////////////////////////////////////////
+TEST(BoxTest, OperatorEqual)
+{
+ math::Box box = math::Box(math::Vector3d(1, 1, 1), math::Vector3d(3, 3, 3));
+ math::Box box2 = math::Box(math::Vector3d(1, 1, 1), math::Vector3d(1, 3, 3));
+ math::Box box3 = math::Box(math::Vector3d(0, 1, 1), math::Vector3d(1, 3, 3));
+ EXPECT_TRUE(box == math::Box(math::Vector3d(1, 1, 1),
+ math::Vector3d(3, 3, 3)));
+ EXPECT_FALSE(box == box2);
+ EXPECT_FALSE(box3 == box);
+}
+
+/////////////////////////////////////////////////
+TEST(BoxTest, OperatorNotEqual)
+{
+ math::Box box = math::Box(math::Vector3d(1, 1, 1), math::Vector3d(3, 3, 3));
+ math::Box box2 = math::Box(math::Vector3d(1, 1, 1), math::Vector3d(1, 3, 3));
+ math::Box box3 = math::Box(math::Vector3d(0, 1, 1), math::Vector3d(1, 3, 3));
+ EXPECT_FALSE(box != math::Box(math::Vector3d(1, 1, 1),
+ math::Vector3d(3, 3, 3)));
+ EXPECT_TRUE(box != box2);
+ EXPECT_TRUE(box3 != box);
+}
+
+/////////////////////////////////////////////////
+TEST(BoxTest, OperatorPlusEqual)
+{
+ math::Box box = math::Box(math::Vector3d(1, 1, 1), math::Vector3d(3, 3, 3));
+ box += math::Box(math::Vector3d(2, 2, 2), math::Vector3d(4, 4, 4));
+ EXPECT_TRUE(box == math::Box(math::Vector3d(1, 1, 1),
+ math::Vector3d(4, 4, 4)));
+}
+
+/////////////////////////////////////////////////
+TEST(BoxTest, OperatorPlus)
+{
+ math::Box box = math::Box(math::Vector3d(1, 1, 1), math::Vector3d(4, 4, 4));
+ box = box + math::Box(math::Vector3d(-2, -2, -2), math::Vector3d(4, 4, 4));
+ EXPECT_TRUE(box == math::Box(math::Vector3d(-2, -2, -2),
+ math::Vector3d(4, 4, 4)));
+}
+
+/////////////////////////////////////////////////
+TEST(BoxTest, Intersects)
+{
+ math::Box box = math::Box(math::Vector3d(0, 0, 0), math::Vector3d(1, 1, 1));
+
+ EXPECT_FALSE(box.Intersects(math::Box(
+ math::Vector3d(1.1, 0, 0), math::Vector3d(2, 1, 1))));
+
+ EXPECT_FALSE(box.Intersects(math::Box(
+ math::Vector3d(0, 1.1, 0), math::Vector3d(1, 2, 1))));
+
+ EXPECT_FALSE(box.Intersects(math::Box(
+ math::Vector3d(0, 0, 1.1), math::Vector3d(1, 1, 2))));
+
+
+ EXPECT_FALSE(box.Intersects(math::Box(
+ math::Vector3d(-1, -1, -1), math::Vector3d(-0.1, 0, 0))));
+
+ EXPECT_FALSE(box.Intersects(math::Box(
+ math::Vector3d(-1, -1, -1), math::Vector3d(0, -0.1, 0))));
+
+ EXPECT_FALSE(box.Intersects(math::Box(
+ math::Vector3d(-1, -1, -1), math::Vector3d(0, 0, -0.1))));
+
+ EXPECT_TRUE(box.Intersects(math::Box(
+ math::Vector3d(0, 0, 0), math::Vector3d(1, 1, 1))));
+}
+
+/////////////////////////////////////////////////
+TEST(BoxTest, Contains)
+{
+ math::Box box = math::Box(math::Vector3d(0, 0, 0), math::Vector3d(1, 1, 1));
+
+ EXPECT_TRUE(box.Contains(math::Vector3d(0, 0, 0)));
+ EXPECT_TRUE(box.Contains(math::Vector3d(0, 0, 1)));
+ EXPECT_TRUE(box.Contains(math::Vector3d(0, 1, 1)));
+ EXPECT_TRUE(box.Contains(math::Vector3d(1, 1, 1)));
+ EXPECT_TRUE(box.Contains(math::Vector3d(1, 1, 0)));
+ EXPECT_TRUE(box.Contains(math::Vector3d(1, 0, 0)));
+ EXPECT_TRUE(box.Contains(math::Vector3d(0.5, 0.5, 0.5)));
+
+ EXPECT_FALSE(box.Contains(math::Vector3d(0, 0, -1)));
+ EXPECT_FALSE(box.Contains(math::Vector3d(0, -1, -1)));
+ EXPECT_FALSE(box.Contains(math::Vector3d(-1, -1, -1)));
+ EXPECT_FALSE(box.Contains(math::Vector3d(-1, -1, 0)));
+ EXPECT_FALSE(box.Contains(math::Vector3d(-1, 0, 0)));
+
+ EXPECT_FALSE(box.Contains(math::Vector3d(0.5, 0.5, -0.5)));
+ EXPECT_FALSE(box.Contains(math::Vector3d(0.5, -0.5, 0.5)));
+ EXPECT_FALSE(box.Contains(math::Vector3d(-0.5, 0.5, 0.5)));
+ EXPECT_FALSE(box.Contains(math::Vector3d(-0.5, -0.5, 0.5)));
+ EXPECT_FALSE(box.Contains(math::Vector3d(-0.5, -0.5, -0.5)));
+
+ EXPECT_FALSE(box.Contains(math::Vector3d(0, 0, -0.01)));
+ EXPECT_FALSE(box.Contains(math::Vector3d(0, -0.01, 0)));
+ EXPECT_FALSE(box.Contains(math::Vector3d(-0.01, 0, 0)));
+}
+
+/////////////////////////////////////////////////
+TEST(BoxTest, OperatorStreamOut)
+{
+ math::Box b(0.1, 1.2, 2.3, 1.1, 2.2, 4.3);
+ std::ostringstream stream;
+ stream << b;
+ EXPECT_EQ(stream.str(), "Min[0.1 1.2 2.3] Max[1.1 2.2 4.3]");
+}
+
+/////////////////////////////////////////////////
+TEST(BoxTest, Intersect)
+{
+ math::Box b(0, 0, 0, 1, 1, 1);
+
+ bool intersect = false;
+ double dist = 0;
+ math::Vector3d pt;
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(-1, 0, 0),
+ math::Vector3d(1, 0, 0), 0, 1000);
+ EXPECT_TRUE(intersect);
+ EXPECT_TRUE(b.IntersectCheck(math::Vector3d(-1, 0, 0),
+ math::Vector3d(1, 0, 0), 0, 1000));
+ EXPECT_DOUBLE_EQ(dist, 1);
+ EXPECT_DOUBLE_EQ(std::get<1>(b.Intersect(math::Vector3d(-1, 0, 0),
+ math::Vector3d(1, 0, 0), 0, 1000)), dist);
+ EXPECT_EQ(pt, math::Vector3d::Zero);
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(1, 0, 0),
+ math::Vector3d(-1, 0, 0), 0, 1000);
+ EXPECT_TRUE(intersect);
+ EXPECT_TRUE(b.IntersectCheck(math::Vector3d(1, 0, 0),
+ math::Vector3d(-1, 0, 0), 0, 1000));
+ EXPECT_DOUBLE_EQ(dist, 0);
+ EXPECT_DOUBLE_EQ(std::get<1>(b.IntersectDist(math::Vector3d(1, 0, 0),
+ math::Vector3d(-1, 0, 0), 0, 1000)), dist);
+ EXPECT_EQ(pt, math::Vector3d(1, 0, 0));
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(2, 2, 0),
+ math::Vector3d(-1, -1, 0), 0, 1000);
+ EXPECT_TRUE(intersect);
+ EXPECT_TRUE(b.IntersectCheck(math::Vector3d(2, 2, 0),
+ math::Vector3d(-1, -1, 0), 0, 1000));
+ EXPECT_DOUBLE_EQ(dist, IGN_SQRT2);
+ EXPECT_DOUBLE_EQ(std::get<1>(b.IntersectDist(math::Vector3d(2, 2, 0),
+ math::Vector3d(-1, -1, 0), 0, 1000)), dist);
+ EXPECT_EQ(pt, math::Vector3d(1, 1, 0));
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(-10, -10, 0),
+ math::Vector3d(1, 1, 0), 0, 1000);
+ EXPECT_TRUE(intersect);
+ EXPECT_TRUE(b.IntersectCheck(math::Vector3d(-10, -10, 0),
+ math::Vector3d(1, 1, 0), 0, 1000));
+ EXPECT_DOUBLE_EQ(dist, std::sqrt(200));
+ EXPECT_DOUBLE_EQ(std::get<1>(b.IntersectDist(math::Vector3d(-10, -10, 0),
+ math::Vector3d(1, 1, 0), 0, 1000)), dist);
+ EXPECT_EQ(pt, math::Vector3d::Zero);
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(-1, -2, 0),
+ math::Vector3d(1, 1, 0), 0, 1000);
+ EXPECT_TRUE(intersect);
+ EXPECT_TRUE(b.IntersectCheck(math::Vector3d(-1, -2, 0),
+ math::Vector3d(1, 1, 0), 0, 1000));
+ EXPECT_DOUBLE_EQ(dist, 2*IGN_SQRT2);
+ EXPECT_DOUBLE_EQ(std::get<1>(b.IntersectDist(math::Vector3d(-1, -2, 0),
+ math::Vector3d(1, 1, 0), 0, 1000)), dist);
+ EXPECT_EQ(pt, math::Vector3d(1, 0, 0));
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(2, 1, 0),
+ math::Vector3d(-1, -1, 0), 0, 1000);
+ EXPECT_TRUE(intersect);
+ EXPECT_TRUE(b.IntersectCheck(math::Vector3d(2, 1, 0),
+ math::Vector3d(-1, -1, 0), 0, 1000));
+ EXPECT_DOUBLE_EQ(dist, IGN_SQRT2);
+ EXPECT_DOUBLE_EQ(std::get<1>(b.IntersectDist(math::Vector3d(2, 1, 0),
+ math::Vector3d(-1, -1, 0), 0, 1000)), dist);
+ EXPECT_EQ(pt, math::Vector3d(1, 0, 0));
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(0.5, 0.5, 2),
+ math::Vector3d(0, 0, -1), 0, 1000);
+ EXPECT_TRUE(intersect);
+ EXPECT_TRUE(b.IntersectCheck(math::Vector3d(0.5, 0.5, 2),
+ math::Vector3d(0, 0, -1), 0, 1000));
+ EXPECT_DOUBLE_EQ(dist, 1);
+ EXPECT_DOUBLE_EQ(std::get<1>(b.IntersectDist(math::Vector3d(0.5, 0.5, 2),
+ math::Vector3d(0, 0, -1), 0, 1000)), dist);
+ EXPECT_EQ(pt, math::Vector3d(0.5, 0.5, 1));
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(0.5, 0.5, 2),
+ math::Vector3d(0, 0, 1), 0, 1000);
+ EXPECT_FALSE(intersect);
+ EXPECT_FALSE(b.IntersectCheck(math::Vector3d(0.5, 0.5, 2),
+ math::Vector3d(0, 0, 1), 0, 1000));
+ EXPECT_EQ(pt, math::Vector3d::Zero);
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(-1, -1, 1),
+ math::Vector3d(0, 0, -1), 0, 1000);
+ EXPECT_FALSE(intersect);
+ EXPECT_FALSE(b.IntersectCheck(math::Vector3d(-1, -1, 1),
+ math::Vector3d(0, 0, -1), 0, 1000));
+ EXPECT_EQ(pt, math::Vector3d::Zero);
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(2, 2, 0),
+ math::Vector3d(1, 1, 0), 0, 1000);
+ EXPECT_FALSE(intersect);
+ EXPECT_FALSE(b.IntersectCheck(math::Vector3d(2, 2, 0),
+ math::Vector3d(1, 1, 0), 0, 1000));
+ EXPECT_EQ(pt, math::Vector3d::Zero);
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(2, 2, 0),
+ math::Vector3d(0, 1, 0), 0, 1000);
+ EXPECT_FALSE(intersect);
+ EXPECT_FALSE(b.IntersectCheck(math::Vector3d(2, 2, 0),
+ math::Vector3d(0, 1, 0), 0, 1000));
+ EXPECT_EQ(pt, math::Vector3d::Zero);
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(0.1, 0.1, 200),
+ math::Vector3d(0, 0, -1), 0, 100);
+ EXPECT_FALSE(intersect);
+ EXPECT_FALSE(b.IntersectCheck(math::Vector3d(0.1, 0.1, 200),
+ math::Vector3d(0, 0, -1), 0, 100));
+ EXPECT_DOUBLE_EQ(dist, 0);
+ EXPECT_DOUBLE_EQ(std::get<1>(b.IntersectDist(math::Vector3d(0.1, 0.1, 200),
+ math::Vector3d(0, 0, -1), 0, 100)), dist);
+ EXPECT_EQ(pt, math::Vector3d::Zero);
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(0.1, 0.1, 1),
+ math::Vector3d(0, 0, -1), 1.0, 1000);
+ EXPECT_TRUE(intersect);
+ EXPECT_TRUE(b.IntersectCheck(math::Vector3d(0.1, 0.1, 1),
+ math::Vector3d(0, 0, -1), 1.0, 1000));
+ EXPECT_DOUBLE_EQ(dist, 0.0);
+ EXPECT_DOUBLE_EQ(std::get<1>(b.IntersectDist(math::Vector3d(0.1, 0.1, 1),
+ math::Vector3d(0, 0, -1), 1.0, 1000)), dist);
+ EXPECT_EQ(pt, math::Vector3d(0.1, 0.1, 0));
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(0.1, 0.1, 1),
+ math::Vector3d(0, 0, -1), 1.1, 1000);
+ EXPECT_FALSE(intersect);
+ EXPECT_FALSE(b.IntersectCheck(math::Vector3d(0.1, 0.1, 1),
+ math::Vector3d(0, 0, -1), 1.1, 1000));
+ EXPECT_EQ(pt, math::Vector3d::Zero);
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(0.1, 0.1, 10),
+ math::Vector3d(0, 0, -1), 1.1, 5);
+ EXPECT_FALSE(intersect);
+ EXPECT_FALSE(b.IntersectCheck(math::Vector3d(0.1, 0.1, 10),
+ math::Vector3d(0, 0, -1), 1.1, 5));
+ EXPECT_DOUBLE_EQ(dist, 0);
+ EXPECT_DOUBLE_EQ(std::get<1>(b.IntersectDist(math::Vector3d(0.1, 0.1, 10),
+ math::Vector3d(0, 0, -1), 1.1, 5)), dist);
+ EXPECT_EQ(pt, math::Vector3d::Zero);
+
+ std::tie(intersect, dist, pt) = b.Intersect(
+ math::Line3d(math::Vector3d(4, 0, 0.5), math::Vector3d(0, 10, 0.5)));
+ EXPECT_FALSE(intersect);
+ EXPECT_DOUBLE_EQ(dist, 0);
+ EXPECT_EQ(pt, math::Vector3d::Zero);
+
+ std::tie(intersect, dist, pt) = b.Intersect(
+ math::Line3d(math::Vector3d(1, -1, 1.5), math::Vector3d(0, 1, 1.5)));
+ EXPECT_FALSE(intersect);
+ EXPECT_DOUBLE_EQ(dist, 0);
+ EXPECT_EQ(pt, math::Vector3d::Zero);
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(0, 0, 1),
+ math::Vector3d(0, 0, -1), 0, 1000);
+ EXPECT_TRUE(intersect);
+ EXPECT_TRUE(b.IntersectCheck(math::Vector3d(0, 0, 1),
+ math::Vector3d(0, 0, -1), 0, 1000));
+ EXPECT_DOUBLE_EQ(dist, 0);
+ EXPECT_DOUBLE_EQ(std::get<1>(b.IntersectDist(math::Vector3d(0, 0, 1),
+ math::Vector3d(0, 0, -1), 0, 1000)), dist);
+ EXPECT_EQ(pt, math::Vector3d(0, 0, 1));
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(0, 0, 0),
+ math::Vector3d(1, 0, 0), 0, 1000);
+ EXPECT_TRUE(intersect);
+ EXPECT_TRUE(b.IntersectCheck(math::Vector3d(0, 0, 0),
+ math::Vector3d(1, 0, 0), 0, 1000));
+ EXPECT_DOUBLE_EQ(dist, 0);
+ EXPECT_DOUBLE_EQ(std::get<1>(b.IntersectDist(math::Vector3d(0, 0, 0),
+ math::Vector3d(1, 0, 0), 0, 1000)), dist);
+ EXPECT_EQ(pt, math::Vector3d::Zero);
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(0, 0, 0),
+ math::Vector3d(-1, 0, 0), 0, 1000);
+ EXPECT_TRUE(intersect);
+ EXPECT_TRUE(b.IntersectCheck(math::Vector3d(0, 0, 0),
+ math::Vector3d(-1, 0, 0), 0, 1000));
+ EXPECT_DOUBLE_EQ(dist, 0);
+ EXPECT_DOUBLE_EQ(std::get<1>(b.IntersectDist(math::Vector3d(0, 0, 0),
+ math::Vector3d(-1, 0, 0), 0, 1000)), dist);
+ EXPECT_EQ(pt, math::Vector3d::Zero);
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(0, 0, 0),
+ math::Vector3d(0, 1, 0), 0, 1000);
+ EXPECT_TRUE(intersect);
+ EXPECT_TRUE(b.IntersectCheck(math::Vector3d(0, 0, 0),
+ math::Vector3d(0, 1, 0), 0, 1000));
+ EXPECT_DOUBLE_EQ(dist, 0);
+ EXPECT_DOUBLE_EQ(std::get<1>(b.IntersectDist(math::Vector3d(0, 0, 0),
+ math::Vector3d(0, 1, 0), 0, 1000)), dist);
+ EXPECT_EQ(pt, math::Vector3d::Zero);
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(0.5, 0.5, 0.5),
+ math::Vector3d(-.707107, 0, -0.707107), 0, 1000);
+ EXPECT_TRUE(intersect);
+ EXPECT_TRUE(b.IntersectCheck(math::Vector3d(0.5, 0.5, 0.5),
+ math::Vector3d(-.707107, 0, -0.707107), 0, 1000));
+ EXPECT_NEAR(dist, 0, 1e-5);
+ EXPECT_NEAR(std::get<1>(b.Intersect(math::Vector3d(0.5, 0.5, 0.5),
+ math::Vector3d(-.707107, 0, -0.707107), 0, 1000)), dist, 1e-5);
+ EXPECT_EQ(pt, math::Vector3d(0.5, 0.5, 0.5));
+
+ std::tie(intersect, dist, pt) = b.Intersect(math::Vector3d(1.2, 0, 0.5),
+ math::Vector3d(-0.707107, 0, -0.707107), 0, 1000);
+ EXPECT_TRUE(intersect);
+ EXPECT_TRUE(b.IntersectCheck(math::Vector3d(1.2, 0, 0.5),
+ math::Vector3d(-0.707107, 0, -0.707107), 0, 1000));
+ EXPECT_NEAR(dist, 0.28284, 1e-5);
+ EXPECT_NEAR(std::get<1>(b.Intersect(math::Vector3d(1.2, 0, 0.5),
+ math::Vector3d(-0.707107, 0, -0.707107), 0, 1000)), dist, 1e-5);
+ EXPECT_EQ(pt, math::Vector3d(1, 0, 0.3));
+}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..912e5e8
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,20 @@
+
+# Collect source files into the "sources" variable and unit test files into the
+# "gtest_sources" variable
+ign_get_libsources_and_unittests(sources gtest_sources)
+
+# Create the library target
+ign_add_library(${PROJECT_LIBRARY_TARGET_NAME} ${sources})
+
+# Turn on C++14 support for the library
+ign_set_project_public_cxx_standard(14)
+
+# Create installation instructions for the library target. This must be called
+# in the same scope that the target is created.
+ign_install_library()
+
+# Build the unit tests
+ign_build_tests(TYPE UNIT SOURCES ${gtest_sources})
+
+# graph namespace
+add_subdirectory(graph)
diff --git a/src/Color.cc b/src/Color.cc
new file mode 100644
index 0000000..2d6f7d6
--- /dev/null
+++ b/src/Color.cc
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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 <math.h>
+#include <algorithm>
+
+#include "ignition/math/Color.hh"
+
+using namespace ignition;
+using namespace math;
+
+const Color Color::White = Color(1, 1, 1, 1);
+const Color Color::Black = Color(0, 0, 0, 1);
+const Color Color::Red = Color(1, 0, 0, 1);
+const Color Color::Green = Color(0, 1, 0, 1);
+const Color Color::Blue = Color(0, 0, 1, 1);
+const Color Color::Yellow = Color(1, 1, 0, 1);
+const Color Color::Magenta = Color(1, 0, 1, 1);
+const Color Color::Cyan = Color(0, 1, 1, 1);
+
+//////////////////////////////////////////////////
+Color::Color()
+{
+}
+
+//////////////////////////////////////////////////
+Color::Color(const float _r, const float _g, const float _b, const float _a)
+: r(_r), g(_g), b(_b), a(_a)
+{
+ this->Clamp();
+}
+
+//////////////////////////////////////////////////
+Color::Color(const Color &_pt)
+: r(_pt.r), g(_pt.g), b(_pt.b), a(_pt.a)
+{
+ this->Clamp();
+}
+
+//////////////////////////////////////////////////
+Color::~Color()
+{
+}
+
+//////////////////////////////////////////////////
+void Color::Reset()
+{
+ this->r = this->g = this->b = 0;
+ this->a = 1;
+}
+
+//////////////////////////////////////////////////
+void Color::Set(const float _r, const float _g, const float _b, const float _a)
+{
+ this->r = _r;
+ this->g = _g;
+ this->b = _b;
+ this->a = _a;
+
+ this->Clamp();
+}
+
+//////////////////////////////////////////////////
+void Color::SetFromHSV(const float _h, const float _s, const float _v)
+{
+ int i;
+ float f, p , q, t;
+
+ float h = static_cast<float>(static_cast<int>(_h < 0 ? 0 : _h) % 360);
+
+ if (equal(_s, 0.0f))
+ {
+ // acromatic (grey)
+ this->r = this->g = this->b = _v;
+ return;
+ }
+
+ // sector 0 - 5
+ h /= 60;
+
+ i = static_cast<int>(floor(h));
+
+ f = h - i;
+
+ p = _v * (1-_s);
+ q = _v * (1 - _s * f);
+ t = _v * (1 - _s * (1-f));
+
+ switch (i)
+ {
+ case 0:
+ this->r = _v;
+ this->g = t;
+ this->b = p;
+ break;
+ case 1:
+ this->r = q;
+ this->g = _v;
+ this->b = p;
+ break;
+ case 2:
+ this->r = p;
+ this->g = _v;
+ this->b = t;
+ break;
+ case 3:
+ this->r = p;
+ this->g = q;
+ this->b = _v;
+ break;
+ case 4:
+ this->r = t;
+ this->g = p;
+ this->b = _v;
+ break;
+ case 5:
+ default:
+ this->r = _v;
+ this->g = p;
+ this->b = q;
+ break;
+ }
+
+ this->Clamp();
+}
+
+//////////////////////////////////////////////////
+Vector3f Color::HSV() const
+{
+ Vector3f hsv;
+
+ float min = std::min(this->r, std::min(this->g, this->b));
+ float max = std::max(this->r, std::max(this->g, this->b));
+ float delta = max - min;
+
+ hsv.Y() = delta / max;
+ hsv.Z() = max;
+
+ if (equal(delta, 0.0f))
+ {
+ hsv.X() = -1;
+ hsv.Y() = 0.0;
+ }
+ else if (equal(this->r, min))
+ hsv.X() = 3 - ((this->g - this->b) / delta);
+ else if (equal(this->g, min))
+ hsv.X() = 5 - ((this->b - this->r) / delta);
+ else
+ hsv.X() = 1 - ((this->r - this->g) / delta);
+
+ return hsv;
+}
+
+//////////////////////////////////////////////////
+Vector3f Color::YUV() const
+{
+ Vector3f yuv;
+
+ yuv.X() = 0.299f*this->r + 0.587f*this->g + 0.114f*this->b;
+ yuv.Y() = -0.1679f*this->r - 0.332f*this->g + 0.5f*this->b + 0.5f;
+ yuv.Z() = 0.5f*this->r - 0.4189f*this->g - 0.08105f*this->b + 0.5f;
+
+ yuv.X() = yuv.X() < 0 ? 0: yuv.X();
+ yuv.X() = yuv.X() > 255 ? 255.0f: yuv.X();
+
+ yuv.Y() = yuv.Y() < 0 ? 0: yuv.Y();
+ yuv.Y() = yuv.Y() > 255 ? 255.0f: yuv.Y();
+
+ yuv.Z() = yuv.Z() < 0 ? 0: yuv.Z();
+ yuv.Z() = yuv.Z() > 255 ? 255.0f: yuv.Z();
+
+ return yuv;
+}
+
+//////////////////////////////////////////////////
+void Color::SetFromYUV(const float _y, const float _u, const float _v)
+{
+ this->r = _y + 1.140f*_v;
+ this->g = _y - 0.395f*_u - 0.581f*_v;
+ this->b = _y + 2.032f*_u;
+ this->Clamp();
+}
+
+//////////////////////////////////////////////////
+float Color::operator[](const unsigned int index)
+{
+ switch (index)
+ {
+ case 0:
+ return this->r;
+ case 1:
+ return this->g;
+ case 2:
+ return this->b;
+ case 3:
+ return this->a;
+ default:
+ break;
+ }
+
+ return NAN_F;
+}
+
+//////////////////////////////////////////////////
+Color::RGBA Color::AsRGBA() const
+{
+ uint8_t val8;
+ unsigned int val32;
+
+ // Convert to 32bit pattern
+ // (RGBA = 8888)
+
+ val8 = static_cast<uint8_t>(this->r * 255);
+ val32 = val8 << 24;
+
+ val8 = static_cast<uint8_t>(this->g * 255);
+ val32 += val8 << 16;
+
+ val8 = static_cast<uint8_t>(this->b * 255);
+ val32 += val8 << 8;
+
+ val8 = static_cast<uint8_t>(this->a * 255);
+ val32 += val8;
+
+ return val32;
+}
+
+//////////////////////////////////////////////////
+Color::BGRA Color::AsBGRA() const
+{
+ uint8_t val8;
+ unsigned int val32 = 0;
+
+ // Convert to 32bit pattern
+ // (BGRA = 8888)
+
+ val8 = static_cast<uint8_t>(this->b * 255);
+ val32 = val8 << 24;
+
+ val8 = static_cast<uint8_t>(this->g * 255);
+ val32 += val8 << 16;
+
+ val8 = static_cast<uint8_t>(this->r * 255);
+ val32 += val8 << 8;
+
+ val8 = static_cast<uint8_t>(this->a * 255);
+ val32 += val8;
+
+ return val32;
+}
+
+//////////////////////////////////////////////////
+Color::ARGB Color::AsARGB() const
+{
+ uint8_t val8;
+ unsigned int val32 = 0;
+
+ // Convert to 32bit pattern
+ // (ARGB = 8888)
+
+ val8 = static_cast<uint8_t>(this->a * 255);
+ val32 = val8 << 24;
+
+ val8 = static_cast<uint8_t>(this->r * 255);
+ val32 += val8 << 16;
+
+ val8 = static_cast<uint8_t>(this->g * 255);
+ val32 += val8 << 8;
+
+ val8 = static_cast<uint8_t>(this->b * 255);
+ val32 += val8;
+
+ return val32;
+}
+
+//////////////////////////////////////////////////
+Color::ABGR Color::AsABGR() const
+{
+ uint8_t val8;
+ unsigned int val32 = 0;
+
+ // Convert to 32bit pattern
+ // (ABGR = 8888)
+
+ val8 = static_cast<uint8_t>(this->a * 255);
+ val32 = val8 << 24;
+
+ val8 = static_cast<uint8_t>(this->b * 255);
+ val32 += val8 << 16;
+
+ val8 = static_cast<uint8_t>(this->g * 255);
+ val32 += val8 << 8;
+
+ val8 = static_cast<uint8_t>(this->r * 255);
+ val32 += val8;
+
+ return val32;
+}
+
+//////////////////////////////////////////////////
+void Color::SetFromRGBA(const Color::RGBA _v)
+{
+ unsigned int val32 = _v;
+
+ // Convert from 32bit pattern
+ // (RGBA = 8888)
+
+ this->r = ((val32 >> 24) & 0xFF) / 255.0f;
+ this->g = ((val32 >> 16) & 0xFF) / 255.0f;
+ this->b = ((val32 >> 8) & 0xFF) / 255.0f;
+ this->a = (val32 & 0xFF) / 255.0f;
+}
+
+//////////////////////////////////////////////////
+void Color::SetFromBGRA(const Color::BGRA _v)
+{
+ unsigned int val32 = _v;
+
+ // Convert from 32bit pattern
+ // (BGRA = 8888)
+
+ this->b = ((val32 >> 24) & 0xFF) / 255.0f;
+ this->g = ((val32 >> 16) & 0xFF) / 255.0f;
+ this->r = ((val32 >> 8) & 0xFF) / 255.0f;
+ this->a = (val32 & 0xFF) / 255.0f;
+}
+
+//////////////////////////////////////////////////
+void Color::SetFromARGB(const Color::ARGB _v)
+{
+ unsigned int val32 = _v;
+
+ // Convert from 32bit pattern
+ // (ARGB = 8888)
+
+ this->a = ((val32 >> 24) & 0xFF) / 255.0f;
+ this->r = ((val32 >> 16) & 0xFF) / 255.0f;
+ this->g = ((val32 >> 8) & 0xFF) / 255.0f;
+ this->b = (val32 & 0xFF) / 255.0f;
+}
+
+//////////////////////////////////////////////////
+void Color::SetFromABGR(const Color::ABGR _v)
+{
+ unsigned int val32 = _v;
+
+ // Convert from 32bit pattern
+ // (ABGR = 8888)
+
+ this->a = ((val32 >> 24) & 0xFF) / 255.0f;
+ this->b = ((val32 >> 16) & 0xFF) / 255.0f;
+ this->g = ((val32 >> 8) & 0xFF) / 255.0f;
+ this->r = (val32 & 0xFF) / 255.0f;
+}
+
+//////////////////////////////////////////////////
+Color &Color::operator=(const Color &_clr)
+{
+ this->r = _clr.r;
+ this->g = _clr.g;
+ this->b = _clr.b;
+ this->a = _clr.a;
+
+ return *this;
+}
+
+//////////////////////////////////////////////////
+Color Color::operator+(const Color &_pt) const
+{
+ return Color(this->r + _pt.r, this->g + _pt.g,
+ this->b + _pt.b, this->a + _pt.a);
+}
+
+//////////////////////////////////////////////////
+Color Color::operator+(const float &_v) const
+{
+ return Color(this->r + _v, this->g + _v, this->b + _v, this->a + _v);
+}
+
+//////////////////////////////////////////////////
+const Color &Color::operator+=(const Color &_pt)
+{
+ this->r += _pt.r;
+ this->g += _pt.g;
+ this->b += _pt.b;
+ this->a += _pt.a;
+
+ this->Clamp();
+
+ return *this;
+}
+
+//////////////////////////////////////////////////
+Color Color::operator-(const Color &_pt) const
+{
+ return Color(this->r - _pt.r, this->g - _pt.g,
+ this->b - _pt.b, this->a - _pt.a);
+}
+
+//////////////////////////////////////////////////
+Color Color::operator-(const float &_v) const
+{
+ return Color(this->r - _v, this->g - _v, this->b - _v, this->a - _v);
+}
+
+//////////////////////////////////////////////////
+const Color &Color::operator-=(const Color &_pt)
+{
+ this->r -= _pt.r;
+ this->g -= _pt.g;
+ this->b -= _pt.b;
+ this->a -= _pt.a;
+
+ this->Clamp();
+
+ return *this;
+}
+
+//////////////////////////////////////////////////
+const Color Color::operator/(const float &_i) const
+{
+ return Color(this->r / _i, this->g / _i, this->b / _i, this->a / _i);
+}
+
+//////////////////////////////////////////////////
+const Color Color::operator/(const Color &_pt) const
+{
+ return Color(this->r / _pt.r, this->g / _pt.g,
+ this->b / _pt.b, this->a / _pt.a);
+}
+
+//////////////////////////////////////////////////
+const Color &Color::operator/=(const Color &_pt)
+{
+ this->r /= _pt.r;
+ this->g /= _pt.g;
+ this->b /= _pt.b;
+ this->a /= _pt.a;
+
+ this->Clamp();
+
+ return *this;
+}
+
+//////////////////////////////////////////////////
+const Color Color::operator*(const float &_i) const
+{
+ return Color(this->r * _i, this->g * _i, this->b * _i, this->a * _i);
+}
+
+//////////////////////////////////////////////////
+const Color Color::operator*(const Color &_pt) const
+{
+ return Color(this->r * _pt.r, this->g * _pt.g,
+ this->b * _pt.b, this->a * _pt.a);
+}
+
+//////////////////////////////////////////////////
+const Color &Color::operator*=(const Color &_pt)
+{
+ this->r *= _pt.r;
+ this->g *= _pt.g;
+ this->b *= _pt.b;
+ this->a *= _pt.a;
+
+ this->Clamp();
+
+ return *this;
+}
+
+//////////////////////////////////////////////////
+bool Color::operator==(const Color &_pt) const
+{
+ return equal(this->r, _pt.r) && equal(this->g, _pt.g) &&
+ equal(this->b, _pt.b) && equal(this->a, _pt.a);
+}
+
+//////////////////////////////////////////////////
+bool Color::operator!=(const Color &_pt) const
+{
+ return !(*this == _pt);
+}
+
+//////////////////////////////////////////////////
+void Color::Clamp()
+{
+ this->r = this->r < 0 || isnan(this->r) ? 0: this->r;
+ this->r = this->r > 1 ? this->r/255.0f: this->r;
+
+ this->g = this->g < 0 || isnan(this->g) ? 0: this->g;
+ this->g = this->g > 1 ? this->g/255.0f: this->g;
+
+ this->b = this->b < 0 || isnan(this->b) ? 0: this->b;
+ this->b = this->b > 1 ? this->b/255.0f: this->b;
+
+ this->a = this->a < 0 || isnan(this->a) ? 0: this->a;
+ this->a = this->a > 1 ? 1.0f: this->a;
+}
+
+//////////////////////////////////////////////////
+float Color::R() const
+{
+ return this->r;
+}
+
+//////////////////////////////////////////////////
+float Color::G() const
+{
+ return this->g;
+}
+
+//////////////////////////////////////////////////
+float Color::B() const
+{
+ return this->b;
+}
+
+//////////////////////////////////////////////////
+float Color::A() const
+{
+ return this->a;
+}
+
+//////////////////////////////////////////////////
+float &Color::R()
+{
+ return this->r;
+}
+
+//////////////////////////////////////////////////
+float &Color::G()
+{
+ return this->g;
+}
+
+//////////////////////////////////////////////////
+float &Color::B()
+{
+ return this->b;
+}
+
+//////////////////////////////////////////////////
+float &Color::A()
+{
+ return this->a;
+}
+
+//////////////////////////////////////////////////
+void Color::R(const float _r)
+{
+ this->r = _r;
+}
+
+//////////////////////////////////////////////////
+void Color::G(const float _g)
+{
+ this->g = _g;
+}
+
+//////////////////////////////////////////////////
+void Color::B(const float _b)
+{
+ this->b = _b;
+}
+
+//////////////////////////////////////////////////
+void Color::A(const float _a)
+{
+ this->a = _a;
+}
diff --git a/src/Color_TEST.cc b/src/Color_TEST.cc
new file mode 100644
index 0000000..4b0bf2d
--- /dev/null
+++ b/src/Color_TEST.cc
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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.A()pache.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 <math.h>
+#include <gtest/gtest.h>
+
+#include <ignition/math/Color.hh>
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(Color, ConstColors)
+{
+ EXPECT_FLOAT_EQ(1.0f, math::Color::White.R());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::White.G());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::White.B());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::White.A());
+
+ EXPECT_FLOAT_EQ(0.0f, math::Color::Black.R());
+ EXPECT_FLOAT_EQ(0.0f, math::Color::Black.G());
+ EXPECT_FLOAT_EQ(0.0f, math::Color::Black.B());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Black.A());
+
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Red.R());
+ EXPECT_FLOAT_EQ(0.0f, math::Color::Red.G());
+ EXPECT_FLOAT_EQ(0.0f, math::Color::Red.B());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Red.A());
+
+ EXPECT_FLOAT_EQ(0.0f, math::Color::Green.R());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Green.G());
+ EXPECT_FLOAT_EQ(0.0f, math::Color::Green.B());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Green.A());
+
+ EXPECT_FLOAT_EQ(0.0f, math::Color::Blue.R());
+ EXPECT_FLOAT_EQ(0.0f, math::Color::Blue.G());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Blue.B());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Blue.A());
+
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Yellow.R());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Yellow.G());
+ EXPECT_FLOAT_EQ(0.0f, math::Color::Yellow.B());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Yellow.A());
+
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Magenta.R());
+ EXPECT_FLOAT_EQ(0.0f, math::Color::Magenta.G());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Magenta.B());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Magenta.A());
+
+ EXPECT_FLOAT_EQ(0.0f, math::Color::Cyan.R());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Cyan.G());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Cyan.B());
+ EXPECT_FLOAT_EQ(1.0f, math::Color::Cyan.A());
+}
+
+/////////////////////////////////////////////////
+TEST(Color, Color)
+{
+ math::Color clr0;
+ EXPECT_FLOAT_EQ(0.0f, clr0.R());
+ EXPECT_FLOAT_EQ(0.0f, clr0.G());
+ EXPECT_FLOAT_EQ(0.0f, clr0.B());
+ EXPECT_FLOAT_EQ(1.0f, clr0.A());
+ EXPECT_EQ(clr0.AsRGBA(), 255u);
+ clr0.A(0.0);
+ EXPECT_EQ(clr0.AsRGBA(), 0u);
+
+ math::Color clr(.1f, .2f, .3f, 1.0f);
+ EXPECT_FLOAT_EQ(0.1f, clr.R());
+ EXPECT_FLOAT_EQ(0.2f, clr.G());
+ EXPECT_FLOAT_EQ(0.3f, clr.B());
+ EXPECT_FLOAT_EQ(1.0f, clr.A());
+
+ clr.Set(1, 0, 0, 0);
+ EXPECT_EQ(clr.AsRGBA(), static_cast<uint32_t>(255) << 24);
+ EXPECT_EQ(clr.AsBGRA(), static_cast<uint32_t>(255) << 8);
+ EXPECT_EQ(clr.AsARGB(), static_cast<uint32_t>(255) << 16);
+ EXPECT_EQ(clr.AsABGR(), static_cast<uint32_t>(255));
+ clr0.SetFromRGBA(static_cast<uint32_t>(255) << 24);
+ EXPECT_EQ(clr0, clr);
+ clr0.SetFromBGRA(static_cast<uint32_t>(255) << 8);
+ EXPECT_EQ(clr0, clr);
+ clr0.SetFromARGB(static_cast<uint32_t>(255) << 16);
+ EXPECT_EQ(clr0, clr);
+ clr0.SetFromABGR(static_cast<uint32_t>(255));
+ EXPECT_EQ(clr0, clr);
+
+ clr.Set(0, 1, 0, 0);
+ EXPECT_EQ(clr.AsRGBA(), static_cast<uint32_t>(255) << 16);
+ EXPECT_EQ(clr.AsBGRA(), static_cast<uint32_t>(255) << 16);
+ EXPECT_EQ(clr.AsARGB(), static_cast<uint32_t>(255) << 8);
+ EXPECT_EQ(clr.AsABGR(), static_cast<uint32_t>(255) << 8);
+ clr0.SetFromRGBA(static_cast<uint32_t>(255) << 16);
+ EXPECT_EQ(clr0, clr);
+ clr0.SetFromBGRA(static_cast<uint32_t>(255) << 16);
+ EXPECT_EQ(clr0, clr);
+ clr0.SetFromARGB(static_cast<uint32_t>(255) << 8);
+ EXPECT_EQ(clr0, clr);
+ clr0.SetFromABGR(static_cast<uint32_t>(255) << 8);
+ EXPECT_EQ(clr0, clr);
+
+ clr.Set(0, 0, 1, 0);
+ EXPECT_EQ(clr.AsRGBA(), static_cast<uint32_t>(255) << 8);
+ EXPECT_EQ(clr.AsBGRA(), static_cast<uint32_t>(255) << 24);
+ EXPECT_EQ(clr.AsARGB(), static_cast<uint32_t>(255));
+ EXPECT_EQ(clr.AsABGR(), static_cast<uint32_t>(255) << 16);
+ clr0.SetFromRGBA(static_cast<uint32_t>(255) << 8);
+ EXPECT_EQ(clr0, clr);
+ clr0.SetFromBGRA(static_cast<uint32_t>(255) << 24);
+ EXPECT_EQ(clr0, clr);
+ clr0.SetFromARGB(static_cast<uint32_t>(255));
+ EXPECT_EQ(clr0, clr);
+ clr0.SetFromABGR(static_cast<uint32_t>(255) << 16);
+ EXPECT_EQ(clr0, clr);
+
+ clr.Set(0, 0, 0, 1);
+ EXPECT_EQ(clr.AsRGBA(), static_cast<uint32_t>(255));
+ EXPECT_EQ(clr.AsBGRA(), static_cast<uint32_t>(255));
+ EXPECT_EQ(clr.AsARGB(), static_cast<uint32_t>(255) << 24);
+ EXPECT_EQ(clr.AsABGR(), static_cast<uint32_t>(255) << 24);
+ clr0.SetFromRGBA(static_cast<uint32_t>(255));
+ EXPECT_EQ(clr0, clr);
+ clr0.SetFromBGRA(static_cast<uint32_t>(255));
+ EXPECT_EQ(clr0, clr);
+ clr0.SetFromARGB(static_cast<uint32_t>(255) << 24);
+ EXPECT_EQ(clr0, clr);
+ clr0.SetFromABGR(static_cast<uint32_t>(255) << 24);
+ EXPECT_EQ(clr0, clr);
+
+ clr.Reset();
+ EXPECT_FLOAT_EQ(0.0f, clr.R());
+ EXPECT_FLOAT_EQ(0.0f, clr.G());
+ EXPECT_FLOAT_EQ(0.0f, clr.B());
+ EXPECT_FLOAT_EQ(1.0f, clr.A());
+
+ clr.SetFromHSV(0, 0.5, 1.0);
+ EXPECT_FLOAT_EQ(1.0f, clr.R());
+ EXPECT_FLOAT_EQ(0.5f, clr.G());
+ EXPECT_FLOAT_EQ(0.5f, clr.B());
+ EXPECT_FLOAT_EQ(1.0f, clr.A());
+
+ EXPECT_TRUE(clr.HSV() == math::Vector3f(6, 0.5, 1));
+
+ clr.SetFromHSV(60, 0.0, 1.0);
+ EXPECT_FLOAT_EQ(1.0f, clr.R());
+ EXPECT_FLOAT_EQ(1.0f, clr.G());
+ EXPECT_FLOAT_EQ(1.0f, clr.B());
+ EXPECT_FLOAT_EQ(1.0f, clr.A());
+
+ clr.SetFromHSV(120, 0.5, 1.0);
+ EXPECT_FLOAT_EQ(0.5f, clr.R());
+ EXPECT_FLOAT_EQ(1.0f, clr.G());
+ EXPECT_FLOAT_EQ(0.5f, clr.B());
+ EXPECT_FLOAT_EQ(1.0f, clr.A());
+
+ clr.SetFromHSV(180, 0.5, 1.0);
+ EXPECT_FLOAT_EQ(0.5f, clr.R());
+ EXPECT_FLOAT_EQ(1.0f, clr.G());
+ EXPECT_FLOAT_EQ(1.0f, clr.B());
+ EXPECT_FLOAT_EQ(1.0f, clr.A());
+
+ clr.SetFromHSV(240, 0.5, 1.0);
+ EXPECT_FLOAT_EQ(0.5f, clr.R());
+ EXPECT_FLOAT_EQ(0.5f, clr.G());
+ EXPECT_FLOAT_EQ(1.0f, clr.B());
+ EXPECT_FLOAT_EQ(1.0f, clr.A());
+
+ clr.SetFromHSV(300, 0.5, 1.0);
+ EXPECT_FLOAT_EQ(1.0f, clr[0]);
+ EXPECT_FLOAT_EQ(0.5f, clr[1]);
+ EXPECT_FLOAT_EQ(1.0f, clr[2]);
+ EXPECT_FLOAT_EQ(1.0f, clr[3]);
+ EXPECT_TRUE(std::isnan(clr[4]));
+
+ clr.R() = 0.1f;
+ clr.G() = 0.2f;
+ clr.B() = 0.3f;
+ clr.A() = 0.4f;
+ EXPECT_FLOAT_EQ(0.1f, clr[0]);
+ EXPECT_FLOAT_EQ(0.2f, clr[1]);
+ EXPECT_FLOAT_EQ(0.3f, clr[2]);
+ EXPECT_FLOAT_EQ(0.4f, clr[3]);
+
+ clr.Set(0.1f, 0.2f, 0.3f, 0.4f);
+ clr = clr + 0.2f;
+ EXPECT_TRUE(clr == math::Color(0.3f, 0.4f, 0.5f, 0.6f));
+
+ clr.Set(0.1f, 0.2f, 0.3f, 0.4f);
+ clr += math::Color(0.2f, 0.2f, 0.2f, 0.2f);
+ EXPECT_TRUE(clr == math::Color(0.3f, 0.4f, 0.5f, 0.6f));
+
+
+ clr.Set(0.1f, 0.2f, 0.3f, 0.4f);
+ clr = clr - 0.1f;
+ EXPECT_TRUE(clr == math::Color(0.0f, 0.1f, 0.2f, 0.3f));
+
+ clr.Set(0.1f, 0.2f, 0.3f, 0.4f);
+ clr -= math::Color(0.1f, 0.1f, 0.1f, 0.1f);
+ EXPECT_TRUE(clr == math::Color(0.0f, 0.1f, 0.2f, 0.3f));
+
+
+ clr.Set(1.f, 1.f, 1.f, 1.f);
+ clr = clr / 1.6f;
+ EXPECT_TRUE(clr == math::Color(0.625f, 0.625f, 0.625f, 0.625f));
+
+ clr.Set(1.f, 1.f, 1.f, 1.f);
+ clr /= math::Color(1.f, 1.f, 1.f, 1.f);
+ EXPECT_TRUE(clr == math::Color(1.f, 1.f, 1.f, 1.f));
+
+
+ clr.Set(.1f, .2f, .3f, .4f);
+ clr = clr * .1f;
+ EXPECT_TRUE(clr == math::Color(0.01f, 0.02f, 0.03f, 0.04f));
+
+ clr.Set(.1f, .2f, .3f, .4f);
+ clr *= math::Color(0.1f, 0.1f, 0.1f, 0.1f);
+ EXPECT_TRUE(clr == math::Color(0.01f, 0.02f, 0.03f, 0.04f));
+
+
+ clr.SetFromYUV(0.5f, 0.2f, 0.8f);
+ EXPECT_TRUE(math::equal(0.00553f, clr.R(), 1e-3f));
+ EXPECT_TRUE(math::equal(0.0f, clr.G()));
+ EXPECT_TRUE(math::equal(0.9064f, clr.B(), 1e-3f));
+ EXPECT_TRUE(math::equal(0.04f, clr.A()));
+
+ EXPECT_TRUE(clr.YUV() == math::Vector3f(0.104985f, 0.95227f, 0.429305f));
+
+ clr = math::Color(1.0f, 0.0f, 0.5f, 1.0f) +
+ math::Color(0.1f, 0.3f, 0.4f, 1.0f);
+ EXPECT_TRUE(math::equal(0.00431373f, clr.R()));
+ EXPECT_TRUE(math::equal(0.3f, clr.G()));
+ EXPECT_TRUE(math::equal(0.9f, clr.B()));
+ EXPECT_TRUE(math::equal(1.0f, clr.A()));
+
+ clr = math::Color(1.0f, 0.0f, 0.5f, 1.0f) -
+ math::Color(0.1f, 0.3f, 0.4f, 1.0f);
+ EXPECT_TRUE(math::equal(0.9f, clr.R()));
+ EXPECT_TRUE(math::equal(0.0f, clr.G()));
+ EXPECT_TRUE(math::equal(0.1f, clr.B()));
+ EXPECT_TRUE(math::equal(0.0f, clr.A()));
+
+ clr = math::Color(0.5f, 0.2f, 0.4f, 0.6f) / 2.0f;
+ EXPECT_TRUE(math::equal(0.25f, clr.R()));
+ EXPECT_TRUE(math::equal(0.1f, clr.G()));
+ EXPECT_TRUE(math::equal(0.2f, clr.B()));
+ EXPECT_TRUE(math::equal(0.3f, clr.A()));
+}
+
+
+/////////////////////////////////////////////////
+TEST(Color, MulOp)
+{
+ math::Color clr(0.0f, 0.01f, 0.2f, 1.0f);
+ math::Color clr2(1.0f, 0.2f, 0.2f, 0.0f);
+ math::Color clr3 = clr * clr2;
+
+ EXPECT_FLOAT_EQ(clr3.R(), 0.0f);
+ EXPECT_FLOAT_EQ(clr3.G(), 0.002f);
+ EXPECT_FLOAT_EQ(clr3.B(), 0.04f);
+ EXPECT_FLOAT_EQ(clr3.A(), 0.0f);
+}
+
+/////////////////////////////////////////////////
+TEST(Color, DivisonOp)
+{
+ math::Color clr(0.0f, 0.01f, 0.2f, 1.0f);
+ math::Color clr2 = clr / 0.2f;
+ EXPECT_FLOAT_EQ(clr2.R(), 0.0f);
+ EXPECT_FLOAT_EQ(clr2.G(), 0.05f);
+ EXPECT_FLOAT_EQ(clr2.B(), 1.0f);
+ EXPECT_FLOAT_EQ(clr2.A(), 1.0f);
+
+ clr2 = clr / 2.0f;
+ EXPECT_FLOAT_EQ(clr2.R(), 0.0f);
+ EXPECT_FLOAT_EQ(clr2.G(), 0.005f);
+ EXPECT_FLOAT_EQ(clr2.B(), 0.1f);
+ EXPECT_FLOAT_EQ(clr2.A(), 0.5f);
+
+ clr2.Set(0.0f, 0.2f, 0.4f, 0.5f);
+ math::Color clr3 = clr / clr2;
+ EXPECT_FLOAT_EQ(clr3.R(), 0.0f);
+ EXPECT_FLOAT_EQ(clr3.G(), 0.05f);
+ EXPECT_FLOAT_EQ(clr3.B(), 0.5f);
+ EXPECT_FLOAT_EQ(clr3.A(), 1.0f);
+
+ clr.Set(0.0f, 0.0f, 0.0f, 0.0f);
+ clr2.Set(0.0f, 0.0f, 0.0f, 0.0f);
+ clr3 = clr / clr2;
+ EXPECT_FLOAT_EQ(clr3.R(), 0.0f);
+ EXPECT_FLOAT_EQ(clr3.G(), 0.0f);
+ EXPECT_FLOAT_EQ(clr3.B(), 0.0f);
+ EXPECT_FLOAT_EQ(clr3.A(), 0.0f);
+}
+
+/////////////////////////////////////////////////
+TEST(Color, ConstAndSet)
+{
+ const math::Color clr(0.1f, 0.2f, 0.3f, 0.4f);
+
+ EXPECT_FLOAT_EQ(clr.R(), 0.1f);
+ EXPECT_FLOAT_EQ(clr.G(), 0.2f);
+ EXPECT_FLOAT_EQ(clr.B(), 0.3f);
+ EXPECT_FLOAT_EQ(clr.A(), 0.4f);
+
+ math::Color clr2;
+ clr2.R(0.4f);
+ clr2.G(0.3f);
+ clr2.B(0.2f);
+ clr2.A(0.1f);
+ EXPECT_FLOAT_EQ(clr2.R(), 0.4f);
+ EXPECT_FLOAT_EQ(clr2.G(), 0.3f);
+ EXPECT_FLOAT_EQ(clr2.B(), 0.2f);
+ EXPECT_FLOAT_EQ(clr2.A(), 0.1f);
+
+ EXPECT_TRUE(clr2 != clr);
+}
+
+/////////////////////////////////////////////////
+TEST(Color, OperatorStreamOut)
+{
+ math::Color c(0.1f, 0.2f, 0.3f, 0.5f);
+ std::ostringstream stream;
+ stream << c;
+ EXPECT_EQ(stream.str(), "0.1 0.2 0.3 0.5");
+}
+
+/////////////////////////////////////////////////
+TEST(Color, HSV)
+{
+ math::Color clr;
+ math::Vector3f hsv = clr.HSV();
+ EXPECT_FLOAT_EQ(hsv.X(), -1.0f);
+ EXPECT_FLOAT_EQ(hsv.Y(), 0.0f);
+ EXPECT_FLOAT_EQ(hsv.Z(), 0.0f);
+
+ clr.Set(0.1f, 0.2f, 0.3f, 1.0f);
+ hsv = clr.HSV();
+ EXPECT_NEAR(hsv.X(), 3.5f, 1e-3);
+ EXPECT_NEAR(hsv.Y(), 0.666667f, 1e-3);
+ EXPECT_NEAR(hsv.Z(), 0.3f, 1e-3);
+
+ clr.Set(0.3f, 0.2f, 0.1f, 1.0f);
+ hsv = clr.HSV();
+ EXPECT_NEAR(hsv.X(), 0.5f, 1e-3);
+ EXPECT_NEAR(hsv.Y(), 0.666667f, 1e-3);
+ EXPECT_NEAR(hsv.Z(), 0.3f, 1e-3);
+
+ clr.SetFromHSV(60, 10, 5);
+ EXPECT_NEAR(clr.R(), 0.0196078f, 1e-3);
+ EXPECT_NEAR(clr.G(), 0.0196078f, 1e-3);
+ EXPECT_NEAR(clr.B(), 0.0f, 1e-3);
+ EXPECT_NEAR(clr.A(), 1.0, 1e-3);
+
+ clr.SetFromHSV(360.0f, 0.5f, 0.6f);
+ EXPECT_NEAR(clr.R(), 0.6f, 1e-3);
+ EXPECT_NEAR(clr.G(), 0.3f, 1e-3);
+ EXPECT_NEAR(clr.B(), 0.3f, 1e-3);
+ EXPECT_NEAR(clr.A(), 1.0, 1e-3);
+}
+
+/////////////////////////////////////////////////
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/Filter_TEST.cc b/src/Filter_TEST.cc
new file mode 100644
index 0000000..8451066
--- /dev/null
+++ b/src/Filter_TEST.cc
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Filter.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(FilterTest, OnePole)
+{
+ math::OnePole<double> filterA;
+ EXPECT_DOUBLE_EQ(filterA.Process(0.2), 0.0);
+
+ filterA.Fc(0.6, 1.4);
+ EXPECT_DOUBLE_EQ(filterA.Process(2.5), 2.3307710879153634);
+
+ math::OnePole<double> filterB(0.1, 0.2);
+ EXPECT_DOUBLE_EQ(filterB.Process(0.5), 0.47839304086811385);
+
+ filterB.Set(5.4);
+ EXPECT_DOUBLE_EQ(filterB.Value(), 5.4);
+}
+
+/////////////////////////////////////////////////
+TEST(FilterTest, OnePoleQuaternion)
+{
+ math::OnePoleQuaternion filterA;
+ EXPECT_EQ(filterA.Value(), math::Quaterniond(1, 0, 0, 0));
+
+ math::OnePoleQuaternion filterB(0.4, 1.4);
+ EXPECT_EQ(filterB.Value(), math::Quaterniond(1, 0, 0, 0));
+
+ EXPECT_EQ(filterA.Process(math::Quaterniond(0.1, 0.2, 0.3)),
+ math::Quaterniond(1, 0, 0, 0));
+
+ EXPECT_EQ(filterB.Process(math::Quaterniond(0.1, 0.2, 0.3)),
+ math::Quaterniond(0.98841, 0.0286272, 0.0885614, 0.119929));
+}
+
+/////////////////////////////////////////////////
+TEST(FilterTest, OnePoleVector3)
+{
+ math::OnePoleVector3 filterA;
+ EXPECT_EQ(filterA.Value(), math::Vector3d(0, 0, 0));
+
+ math::OnePoleVector3 filterB(1.2, 3.4);
+ EXPECT_EQ(filterB.Value(), math::Vector3d(0, 0, 0));
+
+ EXPECT_EQ(filterA.Process(math::Vector3d(0.1, 0.2, 0.3)),
+ math::Vector3d(0, 0, 0));
+
+ EXPECT_EQ(filterB.Process(math::Vector3d(0.1, 0.2, 0.3)),
+ math::Vector3d(0.089113, 0.178226, 0.267339));
+}
+
+/////////////////////////////////////////////////
+TEST(FilterTest, Biquad)
+{
+ math::BiQuad<double> filterA;
+ EXPECT_NEAR(filterA.Value(), 0.0, 1e-10);
+ EXPECT_NEAR(filterA.Process(1.1), 0.0, 1e-10);
+
+ filterA.Fc(0.3, 1.4);
+ EXPECT_DOUBLE_EQ(filterA.Process(1.2), 0.66924691484768517);
+
+ filterA.Fc(0.3, 1.4, 0.1);
+ EXPECT_DOUBLE_EQ(filterA.Process(10.25), 0.96057152402651302);
+
+ math::BiQuad<double> filterB(4.3, 10.6);
+ EXPECT_NEAR(filterB.Value(), 0.0, 1e-10);
+ EXPECT_DOUBLE_EQ(filterB.Process(0.1234), 0.072418159950486546);
+
+ filterB.Set(4.5);
+ EXPECT_DOUBLE_EQ(filterB.Value(), 4.5);
+}
+
+/////////////////////////////////////////////////
+TEST(FilterTest, BiquadVector3)
+{
+ math::BiQuadVector3 filterA;
+ EXPECT_EQ(filterA.Value(), math::Vector3d(0, 0, 0));
+ EXPECT_EQ(filterA.Process(math::Vector3d(1.1, 2.3, 3.4)),
+ math::Vector3d(0, 0, 0));
+
+ math::BiQuadVector3 filterB(6.5, 22.4);
+ EXPECT_EQ(filterB.Value(), math::Vector3d(0, 0, 0));
+ EXPECT_EQ(filterB.Process(math::Vector3d(0.1, 20.3, 33.45)),
+ math::Vector3d(0.031748, 6.44475, 10.6196));
+}
diff --git a/src/Frustum.cc b/src/Frustum.cc
new file mode 100644
index 0000000..9246798
--- /dev/null
+++ b/src/Frustum.cc
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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 <cmath>
+
+#include "ignition/math/Matrix4.hh"
+#include "FrustumPrivate.hh"
+#include "ignition/math/Frustum.hh"
+
+using namespace ignition;
+using namespace math;
+
+/////////////////////////////////////////////////
+Frustum::Frustum()
+ : dataPtr(new FrustumPrivate(0, 1, IGN_DTOR(45), 1, Pose3d::Zero))
+{
+}
+
+/////////////////////////////////////////////////
+Frustum::Frustum(const double _near,
+ const double _far,
+ const Angle &_fov,
+ const double _aspectRatio,
+ const Pose3d &_pose)
+ : dataPtr(new FrustumPrivate(_near, _far, _fov, _aspectRatio, _pose))
+{
+ // Compute plane based on near distance, far distance, field of view,
+ // aspect ratio, and pose
+ this->ComputePlanes();
+}
+
+/////////////////////////////////////////////////
+Frustum::~Frustum()
+{
+ delete this->dataPtr;
+ this->dataPtr = NULL;
+}
+
+/////////////////////////////////////////////////
+Frustum::Frustum(const Frustum &_p)
+ : dataPtr(new FrustumPrivate(_p.Near(), _p.Far(), _p.FOV(),
+ _p.AspectRatio(), _p.Pose()))
+{
+ for (int i = 0; i < 6; ++i)
+ this->dataPtr->planes[i] = _p.dataPtr->planes[i];
+}
+
+/////////////////////////////////////////////////
+Planed Frustum::Plane(const FrustumPlane _plane) const
+{
+ return this->dataPtr->planes[_plane];
+}
+
+/////////////////////////////////////////////////
+bool Frustum::Contains(const Box &_b) const
+{
+ // If the box is on the negative side of a plane, then the box is not
+ // visible.
+ for (auto const &plane : this->dataPtr->planes)
+ {
+ if (plane.Side(_b) == Planed::NEGATIVE_SIDE)
+ return false;
+ }
+
+ return true;
+}
+
+/////////////////////////////////////////////////
+bool Frustum::Contains(const Vector3d &_p) const
+{
+ // If the point is on the negative side of a plane, then the point is not
+ // visible.
+ for (auto const &plane : this->dataPtr->planes)
+ {
+ if (plane.Side(_p) == Planed::NEGATIVE_SIDE)
+ return false;
+ }
+
+ return true;
+}
+
+/////////////////////////////////////////////////
+double Frustum::Near() const
+{
+ return this->dataPtr->near;
+}
+
+/////////////////////////////////////////////////
+void Frustum::SetNear(const double _near)
+{
+ this->dataPtr->near = _near;
+ this->ComputePlanes();
+}
+
+/////////////////////////////////////////////////
+double Frustum::Far() const
+{
+ return this->dataPtr->far;
+}
+
+/////////////////////////////////////////////////
+void Frustum::SetFar(const double _far)
+{
+ this->dataPtr->far = _far;
+ this->ComputePlanes();
+}
+
+/////////////////////////////////////////////////
+Angle Frustum::FOV() const
+{
+ return this->dataPtr->fov;
+}
+
+/////////////////////////////////////////////////
+void Frustum::SetFOV(const Angle &_angle)
+{
+ this->dataPtr->fov = _angle;
+ this->ComputePlanes();
+}
+
+/////////////////////////////////////////////////
+Pose3d Frustum::Pose() const
+{
+ return this->dataPtr->pose;
+}
+
+/////////////////////////////////////////////////
+void Frustum::SetPose(const Pose3d &_pose)
+{
+ this->dataPtr->pose = _pose;
+ this->ComputePlanes();
+}
+
+/////////////////////////////////////////////////
+double Frustum::AspectRatio() const
+{
+ return this->dataPtr->aspectRatio;
+}
+
+/////////////////////////////////////////////////
+void Frustum::SetAspectRatio(const double _aspectRatio)
+{
+ this->dataPtr->aspectRatio = _aspectRatio;
+ this->ComputePlanes();
+}
+
+/////////////////////////////////////////////////
+void Frustum::ComputePlanes()
+{
+ // Tangent of half the field of view.
+ double tanFOV2 = std::tan(this->dataPtr->fov() * 0.5);
+
+ // Width of near plane
+ double nearWidth = 2.0 * tanFOV2 * this->dataPtr->near;
+
+ // Height of near plane
+ double nearHeight = nearWidth / this->dataPtr->aspectRatio;
+
+ // Width of far plane
+ double farWidth = 2.0 * tanFOV2 * this->dataPtr->far;
+
+ // Height of far plane
+ double farHeight = farWidth / this->dataPtr->aspectRatio;
+
+ // Up, right, and forward unit vectors.
+ Vector3d forward = this->dataPtr->pose.Rot().RotateVector(Vector3d::UnitX);
+ Vector3d up = this->dataPtr->pose.Rot().RotateVector(Vector3d::UnitZ);
+ Vector3d right = this->dataPtr->pose.Rot().RotateVector(-Vector3d::UnitY);
+
+ // Near plane center
+ Vector3d nearCenter = this->dataPtr->pose.Pos() + forward *
+ this->dataPtr->near;
+
+ // Far plane center
+ Vector3d farCenter = this->dataPtr->pose.Pos() + forward *
+ this->dataPtr->far;
+
+ // These four variables are here for convenience.
+ Vector3d upNearHeight2 = up * (nearHeight * 0.5);
+ Vector3d rightNearWidth2 = right * (nearWidth * 0.5);
+ Vector3d upFarHeight2 = up * (farHeight * 0.5);
+ Vector3d rightFarWidth2 = right * (farWidth * 0.5);
+
+ // Compute the vertices of the near plane
+ Vector3d nearTopLeft = nearCenter + upNearHeight2 - rightNearWidth2;
+ Vector3d nearTopRight = nearCenter + upNearHeight2 + rightNearWidth2;
+ Vector3d nearBottomLeft = nearCenter - upNearHeight2 - rightNearWidth2;
+ Vector3d nearBottomRight = nearCenter - upNearHeight2 + rightNearWidth2;
+
+ // Compute the vertices of the far plane
+ Vector3d farTopLeft = farCenter + upFarHeight2 - rightFarWidth2;
+ Vector3d farTopRight = farCenter + upFarHeight2 + rightFarWidth2;
+ Vector3d farBottomLeft = farCenter - upFarHeight2 - rightFarWidth2;
+ Vector3d farBottomRight = farCenter - upFarHeight2 + rightFarWidth2;
+
+ Vector3d leftCenter =
+ (farTopLeft + nearTopLeft + farBottomLeft + nearBottomLeft) / 4.0;
+
+ Vector3d rightCenter =
+ (farTopRight + nearTopRight + farBottomRight + nearBottomRight) / 4.0;
+
+ Vector3d topCenter =
+ (farTopRight + nearTopRight + farTopLeft + nearTopLeft) / 4.0;
+
+ Vector3d bottomCenter =
+ (farBottomRight + nearBottomRight + farBottomLeft + nearBottomLeft) / 4.0;
+
+ // Compute plane offsets
+ // Set the planes, where the first value is the plane normal and the
+ // second the plane offset
+ Vector3d norm = Vector3d::Normal(nearTopLeft, nearTopRight, nearBottomLeft);
+ this->dataPtr->planes[FRUSTUM_PLANE_NEAR].Set(norm, nearCenter.Dot(norm));
+
+ norm = Vector3d::Normal(farTopRight, farTopLeft, farBottomLeft);
+ this->dataPtr->planes[FRUSTUM_PLANE_FAR].Set(norm, farCenter.Dot(norm));
+
+ norm = Vector3d::Normal(farTopLeft, nearTopLeft, nearBottomLeft);
+ this->dataPtr->planes[FRUSTUM_PLANE_LEFT].Set(norm, leftCenter.Dot(norm));
+
+ norm = Vector3d::Normal(nearTopRight, farTopRight, farBottomRight);
+ this->dataPtr->planes[FRUSTUM_PLANE_RIGHT].Set(norm, rightCenter.Dot(norm));
+
+ norm = Vector3d::Normal(nearTopLeft, farTopLeft, nearTopRight);
+ this->dataPtr->planes[FRUSTUM_PLANE_TOP].Set(norm, topCenter.Dot(norm));
+
+ norm = Vector3d::Normal(nearBottomLeft, nearBottomRight, farBottomRight);
+ this->dataPtr->planes[FRUSTUM_PLANE_BOTTOM].Set(norm, bottomCenter.Dot(norm));
+}
+
+//////////////////////////////////////////////////
+Frustum &Frustum::operator =(const Frustum &_f)
+{
+ this->dataPtr->near = _f.dataPtr->near;
+ this->dataPtr->far = _f.dataPtr->far;
+ this->dataPtr->fov = _f.dataPtr->fov;
+ this->dataPtr->aspectRatio = _f.dataPtr->aspectRatio;
+ this->dataPtr->pose = _f.dataPtr->pose;
+ this->ComputePlanes();
+
+ return *this;
+}
diff --git a/src/FrustumPrivate.hh b/src/FrustumPrivate.hh
new file mode 100644
index 0000000..251dd65
--- /dev/null
+++ b/src/FrustumPrivate.hh
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_FRUSTUMPRIVATE_HH_
+#define IGNITION_MATH_FRUSTUMPRIVATE_HH_
+
+#include <array>
+
+#include <ignition/math/Pose3.hh>
+#include <ignition/math/Angle.hh>
+#include <ignition/math/Plane.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \internal
+ /// \brief Private data for the Frustum class
+ class FrustumPrivate
+ {
+ /// \brief Constructor
+ /// \param[in] _near Near distance. This is the distance from
+ /// the frustum's vertex to the closest plane
+ /// \param[in] _far Far distance. This is the distance from the
+ /// frustum's vertex to the farthest plane.
+ /// \param[in] _fov Field of view. The field of view is the
+ /// angle between the frustum's vertex and the edges of the near or far
+ /// plane. This value represents the horizontal angle.
+ /// \param[in] _aspectRatio The aspect ratio, which is the width divided
+ /// by height of the near or far planes.
+ /// \param[in] _pose Pose of the frustum, which is the vertex (top of
+ /// the pyramid).
+ public: FrustumPrivate(const double _near,
+ const double _far,
+ const math::Angle &_fov,
+ const double _aspectRatio,
+ const Pose3d &_pose)
+ : near(_near), far(_far), fov(_fov),
+ aspectRatio(_aspectRatio), pose(_pose)
+ {
+ }
+
+ /// \brief Near distance
+ public: double near;
+
+ /// \brief Far distance
+ public: double far;
+
+ /// \brief Field of view
+ public: math::Angle fov;
+
+ /// \brief Aspect ratio of the near and far planes. This is the
+ // width divided by the height.
+ public: double aspectRatio;
+
+ /// \brief Pose of the frustum
+ public: math::Pose3d pose;
+
+ /// \brief Each plane of the frustum.
+ /// \sa Frustum::FrustumPlane
+ public: std::array<Planed, 6> planes;
+ };
+ }
+}
+#endif
diff --git a/src/Frustum_TEST.cc b/src/Frustum_TEST.cc
new file mode 100644
index 0000000..e51bb82
--- /dev/null
+++ b/src/Frustum_TEST.cc
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Helpers.hh"
+#include "ignition/math/Frustum.hh"
+
+using namespace ignition;
+using namespace math;
+
+/////////////////////////////////////////////////
+TEST(FrustumTest, Constructor)
+{
+ Frustum frustum;
+
+ EXPECT_DOUBLE_EQ(frustum.Near(), 0.0);
+ EXPECT_DOUBLE_EQ(frustum.Far(), 1.0);
+ EXPECT_EQ(frustum.FOV(), IGN_DTOR(45));
+ EXPECT_DOUBLE_EQ(frustum.AspectRatio(), 1.0);
+ EXPECT_EQ(frustum.Pose(), Pose3d::Zero);
+}
+
+/////////////////////////////////////////////////
+TEST(FrustumTest, CopyConstructor)
+{
+ // Frustum pointing down the +x axis
+ Frustum frustum(
+ // Near distance
+ 1,
+ // Far distance
+ 10,
+ // Field of view
+ Angle(IGN_DTOR(45)),
+ // Aspect ratio
+ 320.0/240.0,
+ // Pose
+ Pose3d(0, 0, 0, 0, 0, 0));
+
+ Frustum frustum2(frustum);
+
+ EXPECT_EQ(frustum.FOV(), frustum2.FOV());
+ EXPECT_DOUBLE_EQ(frustum.Near(), frustum2.Near());
+ EXPECT_DOUBLE_EQ(frustum.Far(), frustum2.Far());
+ EXPECT_DOUBLE_EQ(frustum.AspectRatio(), frustum2.AspectRatio());
+ EXPECT_DOUBLE_EQ(frustum.AspectRatio(), frustum2.AspectRatio());
+
+ EXPECT_EQ(frustum.Plane(Frustum::FRUSTUM_PLANE_NEAR).Normal(),
+ frustum2.Plane(Frustum::FRUSTUM_PLANE_NEAR).Normal());
+
+ EXPECT_EQ(frustum.Plane(Frustum::FRUSTUM_PLANE_FAR).Normal(),
+ frustum2.Plane(Frustum::FRUSTUM_PLANE_FAR).Normal());
+
+ EXPECT_EQ(frustum.Plane(Frustum::FRUSTUM_PLANE_LEFT).Normal(),
+ frustum2.Plane(Frustum::FRUSTUM_PLANE_LEFT).Normal());
+
+ EXPECT_EQ(frustum.Plane(Frustum::FRUSTUM_PLANE_RIGHT).Normal(),
+ frustum2.Plane(Frustum::FRUSTUM_PLANE_RIGHT).Normal());
+
+ EXPECT_EQ(frustum.Plane(Frustum::FRUSTUM_PLANE_TOP).Normal(),
+ frustum2.Plane(Frustum::FRUSTUM_PLANE_TOP).Normal());
+
+ EXPECT_EQ(frustum.Plane(Frustum::FRUSTUM_PLANE_BOTTOM).Normal(),
+ frustum2.Plane(Frustum::FRUSTUM_PLANE_BOTTOM).Normal());
+}
+
+/////////////////////////////////////////////////
+TEST(FrustumTest, AssignmentOperator)
+{
+ // Frustum pointing to the +X+Y diagonal
+ Frustum frustum(
+ // Near distance
+ 1,
+ // Far distance
+ 10,
+ // Field of view
+ Angle(IGN_DTOR(45)),
+ // Aspect ratio
+ 320.0/240.0,
+ // Pose
+ Pose3d(0, 0, 0, 0, 0, IGN_DTOR(45)));
+
+ Frustum frustum2;
+ frustum2 = frustum;
+
+ EXPECT_EQ(frustum.FOV(), frustum2.FOV());
+ EXPECT_DOUBLE_EQ(frustum.Near(), frustum2.Near());
+ EXPECT_DOUBLE_EQ(frustum.Far(), frustum2.Far());
+ EXPECT_DOUBLE_EQ(frustum.AspectRatio(), frustum2.AspectRatio());
+ EXPECT_DOUBLE_EQ(frustum.AspectRatio(), frustum2.AspectRatio());
+
+ EXPECT_EQ(frustum.Plane(Frustum::FRUSTUM_PLANE_NEAR).Normal(),
+ frustum2.Plane(Frustum::FRUSTUM_PLANE_NEAR).Normal());
+
+ EXPECT_EQ(frustum.Plane(Frustum::FRUSTUM_PLANE_FAR).Normal(),
+ frustum2.Plane(Frustum::FRUSTUM_PLANE_FAR).Normal());
+
+ EXPECT_EQ(frustum.Plane(Frustum::FRUSTUM_PLANE_LEFT).Normal(),
+ frustum2.Plane(Frustum::FRUSTUM_PLANE_LEFT).Normal());
+
+ EXPECT_EQ(frustum.Plane(Frustum::FRUSTUM_PLANE_RIGHT).Normal(),
+ frustum2.Plane(Frustum::FRUSTUM_PLANE_RIGHT).Normal());
+
+ EXPECT_EQ(frustum.Plane(Frustum::FRUSTUM_PLANE_TOP).Normal(),
+ frustum2.Plane(Frustum::FRUSTUM_PLANE_TOP).Normal());
+
+ EXPECT_EQ(frustum.Plane(Frustum::FRUSTUM_PLANE_BOTTOM).Normal(),
+ frustum2.Plane(Frustum::FRUSTUM_PLANE_BOTTOM).Normal());
+}
+
+/////////////////////////////////////////////////
+TEST(FrustumTest, PyramidXAxisPos)
+{
+ // Frustum pointing down the +x axis
+ Frustum frustum(
+ // Near distance
+ 1,
+ // Far distance
+ 10,
+ // Field of view
+ Angle(IGN_DTOR(45)),
+ // Aspect ratio
+ 320.0/240.0,
+ // Pose
+ Pose3d(0, 0, 0, 0, 0, 0));
+
+ EXPECT_FALSE(frustum.Contains(Vector3d(0, 0, 0)));
+ EXPECT_TRUE(frustum.Contains(Vector3d(1, 0, 0)));
+
+ EXPECT_TRUE(frustum.Contains(Vector3d(2, 0, 0)));
+ EXPECT_TRUE(frustum.Contains(Vector3d(10, 0, 0)));
+ EXPECT_FALSE(frustum.Contains(Vector3d(10.1, 0, 0)));
+
+ EXPECT_TRUE(frustum.Contains(Box(Vector3d(1, 0, 0), Vector3d(5, 5, 5))));
+ EXPECT_FALSE(frustum.Contains(Box(Vector3d(-1, 0, 0), Vector3d(.1, .2, .3))));
+}
+
+/////////////////////////////////////////////////
+TEST(FrustumTest, PyramidXAxisNeg)
+{
+ // Frustum pointing down the -x axis
+ Frustum frustum(
+ // Near distance
+ 1,
+ // Far distance
+ 10,
+ // Field of view
+ Angle(IGN_DTOR(45)),
+ // Aspect ratio
+ 320.0/240.0,
+ // Pose
+ Pose3d(0, 0, 0, 0, 0, IGN_PI));
+
+ EXPECT_FALSE(frustum.Contains(Vector3d(0, 0, 0)));
+ EXPECT_FALSE(frustum.Contains(Vector3d(-0.5, 0, 0)));
+ EXPECT_FALSE(frustum.Contains(Vector3d(-10.1, 0, 0)));
+
+ EXPECT_TRUE(frustum.Contains(Vector3d(-1, 0, 0)));
+ EXPECT_TRUE(frustum.Contains(Vector3d(-2, 0, 0)));
+ EXPECT_TRUE(frustum.Contains(Vector3d(-10, 0, 0)));
+
+ EXPECT_FALSE(frustum.Contains(Box(Vector3d(1, 0, 0), Vector3d(5, 5, 5))));
+ EXPECT_TRUE(frustum.Contains(Box(Vector3d(-1, 0, 0), Vector3d(.1, .2, .3))));
+}
+
+/////////////////////////////////////////////////
+TEST(FrustumTest, PyramidYAxis)
+{
+ // Frustum pointing down the +y axis
+ Frustum frustum(
+ // Near distance
+ .1,
+ // Far distance
+ 5,
+ // Field of view
+ Angle(IGN_DTOR(45)),
+ // Aspect ratio
+ 320.0/320.0,
+ // Pose
+ Pose3d(0, 0, 0, 0, 0, IGN_PI*0.5));
+
+ EXPECT_FALSE(frustum.Contains(Vector3d(0, 0, 0)));
+ EXPECT_FALSE(frustum.Contains(Vector3d(1, 0, 0)));
+ EXPECT_FALSE(frustum.Contains(Vector3d(.05, 0, 0)));
+
+ EXPECT_TRUE(frustum.Contains(Vector3d(0, .1, 0)));
+ EXPECT_TRUE(frustum.Contains(Vector3d(0, 1, 0)));
+ EXPECT_TRUE(frustum.Contains(Vector3d(0, 5, 0)));
+
+ EXPECT_TRUE(frustum.Contains(Box(Vector3d(0, 1, 0), Vector3d(5, 5, 5))));
+ EXPECT_FALSE(frustum.Contains(Box(Vector3d(0, -1, 0), Vector3d(.1, 0, .3))));
+}
+
+/////////////////////////////////////////////////
+TEST(FrustumTest, PyramidZAxis)
+{
+ // Frustum pointing down the -z axis
+ Frustum frustum(
+ // Near distance
+ 1,
+ // Far distance
+ 10,
+ // Field of view
+ Angle(IGN_DTOR(45)),
+ // Aspect ratio
+ 320.0/320.0,
+ // Pose
+ Pose3d(0, 0, 0, 0, IGN_PI*0.5, 0));
+
+ EXPECT_FALSE(frustum.Contains(Vector3d(0, 0, 0)));
+ EXPECT_FALSE(frustum.Contains(Vector3d(0, 0, -0.9)));
+ EXPECT_FALSE(frustum.Contains(Vector3d(0, 0, -10.5)));
+ EXPECT_FALSE(frustum.Contains(Vector3d(0, 0, 0.9)));
+ EXPECT_FALSE(frustum.Contains(Vector3d(0, 0, 10.5)));
+
+ EXPECT_TRUE(frustum.Contains(Vector3d(0, 0, -1.1)));
+ EXPECT_TRUE(frustum.Contains(Vector3d(0.5, 0.5, -5.5)));
+ EXPECT_TRUE(frustum.Contains(Vector3d(0, 0, -10)));
+
+ EXPECT_FALSE(frustum.Contains(Box(Vector3d(0, 0, 0), Vector3d(5, 5, 5))));
+ EXPECT_TRUE(frustum.Contains(Box(Vector3d(0, 0, -1), Vector3d(.1, 0, .3))));
+}
+
+/////////////////////////////////////////////////
+TEST(FrustumTest, NearFar)
+{
+ Frustum frustum(
+ // Near distance
+ 1,
+ // Far distance
+ 10,
+ // Field of view
+ Angle(IGN_DTOR(45)),
+ // Aspect ratio
+ 320.0/320.0,
+ // Pose
+ Pose3d(0, 0, 0, 0, IGN_PI*0.5, 0));
+
+ EXPECT_DOUBLE_EQ(frustum.Near(), 1.0);
+ EXPECT_DOUBLE_EQ(frustum.Far(), 10.0);
+
+ frustum.SetNear(-1.0);
+ frustum.SetFar(-10.0);
+
+ EXPECT_DOUBLE_EQ(frustum.Near(), -1.0);
+ EXPECT_DOUBLE_EQ(frustum.Far(), -10.0);
+}
+
+/////////////////////////////////////////////////
+TEST(FrustumTest, FOV)
+{
+ Frustum frustum(
+ // Near distance
+ 1,
+ // Far distance
+ 10,
+ // Field of view
+ Angle(IGN_DTOR(45)),
+ // Aspect ratio
+ 320.0/320.0,
+ // Pose
+ Pose3d(0, 0, 0, 0, IGN_PI*0.5, 0));
+
+ EXPECT_EQ(frustum.FOV(), math::Angle(IGN_DTOR(45)));
+
+ frustum.SetFOV(1.5707);
+
+ EXPECT_EQ(frustum.FOV(), math::Angle(1.5707));
+}
+
+/////////////////////////////////////////////////
+TEST(FrustumTest, AspectRatio)
+{
+ Frustum frustum(
+ // Near distance
+ 1,
+ // Far distance
+ 10,
+ // Field of view
+ Angle(IGN_DTOR(45)),
+ // Aspect ratio
+ 320.0/320.0,
+ // Pose
+ Pose3d(0, 0, 0, 0, IGN_PI*0.5, 0));
+
+ EXPECT_DOUBLE_EQ(frustum.AspectRatio(), 320.0/320.0);
+
+ frustum.SetAspectRatio(1.3434);
+
+ EXPECT_DOUBLE_EQ(frustum.AspectRatio(), 1.3434);
+}
+
+/////////////////////////////////////////////////
+TEST(FrustumTest, Pose)
+{
+ Frustum frustum(
+ // Near distance
+ 1,
+ // Far distance
+ 10,
+ // Field of view
+ Angle(IGN_DTOR(45)),
+ // Aspect ratio
+ 320.0/320.0,
+ // Pose
+ Pose3d(0, 0, 0, 0, IGN_PI*0.5, 0));
+
+ EXPECT_EQ(frustum.Pose(), Pose3d(0, 0, 0, 0, IGN_PI*0.5, 0));
+
+ frustum.SetPose(Pose3d(1, 2, 3, IGN_PI, 0, 0));
+
+ EXPECT_EQ(frustum.Pose(), Pose3d(1, 2, 3, IGN_PI, 0, 0));
+}
+
+/////////////////////////////////////////////////
+TEST(FrustumTest, PoseContains)
+{
+ Frustum frustum(
+ // Near distance
+ 1,
+ // Far distance
+ 10,
+ // Field of view
+ Angle(IGN_DTOR(60)),
+ // Aspect ratio
+ 1920.0/1080.0,
+ // Pose
+ Pose3d(0, -5, 0, 0, 0, IGN_PI*0.5));
+
+ // Test the near clip boundary
+ EXPECT_FALSE(frustum.Contains(Vector3d(0, -4.01, 0)));
+ EXPECT_TRUE(frustum.Contains(Vector3d(0, -4.0, 0)));
+
+ // Test a point between the near and far clip planes
+ EXPECT_TRUE(frustum.Contains(Vector3d(0, 1, 0)));
+
+ // Test the far clip boundary
+ EXPECT_TRUE(frustum.Contains(Vector3d(0, 5, 0)));
+ EXPECT_FALSE(frustum.Contains(Vector3d(0, 5.001, 0)));
+
+ // Use an offset for the test points. This makes the test more stable, and
+ // is also used to generate point outside the frustum.
+ double offset = 0.00001;
+
+ // Compute near clip points
+ Vector3d nearTopLeft(
+ -tan(IGN_DTOR(30)) + offset,
+ frustum.Pose().Pos().Y() + frustum.Near() + offset,
+ tan(IGN_DTOR(30)) / frustum.AspectRatio() - offset);
+
+ Vector3d nearTopLeftBad(
+ -tan(IGN_DTOR(30)) - offset,
+ frustum.Pose().Pos().Y() + frustum.Near() - offset,
+ tan(IGN_DTOR(30)) / frustum.AspectRatio() + offset);
+
+ Vector3d nearTopRight(
+ tan(IGN_DTOR(30)) - offset,
+ frustum.Pose().Pos().Y() + frustum.Near() + offset,
+ tan(IGN_DTOR(30)) / frustum.AspectRatio() - offset);
+
+ Vector3d nearTopRightBad(
+ tan(IGN_DTOR(30)) + offset,
+ frustum.Pose().Pos().Y() + frustum.Near() - offset,
+ tan(IGN_DTOR(30)) / frustum.AspectRatio() + offset);
+
+ Vector3d nearBottomLeft(
+ -tan(IGN_DTOR(30)) + offset,
+ frustum.Pose().Pos().Y() + frustum.Near() + offset,
+ -tan(IGN_DTOR(30)) / frustum.AspectRatio() + offset);
+
+ Vector3d nearBottomLeftBad(
+ -tan(IGN_DTOR(30)) - offset,
+ frustum.Pose().Pos().Y() + frustum.Near() - offset,
+ -tan(IGN_DTOR(30)) / frustum.AspectRatio() - offset);
+
+ Vector3d nearBottomRight(
+ tan(IGN_DTOR(30)) - offset,
+ frustum.Pose().Pos().Y() + frustum.Near() + offset,
+ -tan(IGN_DTOR(30)) / frustum.AspectRatio() + offset);
+
+ Vector3d nearBottomRightBad(
+ tan(IGN_DTOR(30)) + offset,
+ frustum.Pose().Pos().Y() + frustum.Near() - offset,
+ -tan(IGN_DTOR(30)) / frustum.AspectRatio() - offset);
+
+ // Test near clip corners
+ EXPECT_TRUE(frustum.Contains(nearTopLeft));
+ EXPECT_FALSE(frustum.Contains(nearTopLeftBad));
+
+ EXPECT_TRUE(frustum.Contains(nearTopRight));
+ EXPECT_FALSE(frustum.Contains(nearTopRightBad));
+
+ EXPECT_TRUE(frustum.Contains(nearBottomLeft));
+ EXPECT_FALSE(frustum.Contains(nearBottomLeftBad));
+
+ EXPECT_TRUE(frustum.Contains(nearBottomRight));
+ EXPECT_FALSE(frustum.Contains(nearBottomRightBad));
+
+ // Compute far clip points
+ Vector3d farTopLeft(
+ -tan(IGN_DTOR(30)) * frustum.Far() + offset,
+ frustum.Pose().Pos().Y() + frustum.Far() - offset,
+ (tan(IGN_DTOR(30)) * frustum.Far()) / frustum.AspectRatio() - offset);
+
+ Vector3d farTopLeftBad(
+ -tan(IGN_DTOR(30))*frustum.Far() - offset,
+ frustum.Pose().Pos().Y() + frustum.Far() + offset,
+ (tan(IGN_DTOR(30) * frustum.Far())) / frustum.AspectRatio() + offset);
+
+ Vector3d farTopRight(
+ tan(IGN_DTOR(30))*frustum.Far() - offset,
+ frustum.Pose().Pos().Y() + frustum.Far() - offset,
+ (tan(IGN_DTOR(30)) * frustum.Far()) / frustum.AspectRatio() - offset);
+
+ Vector3d farTopRightBad(
+ tan(IGN_DTOR(30))*frustum.Far() + offset,
+ frustum.Pose().Pos().Y() + frustum.Far() + offset,
+ (tan(IGN_DTOR(30)) * frustum.Far()) / frustum.AspectRatio() + offset);
+
+ Vector3d farBottomLeft(
+ -tan(IGN_DTOR(30))*frustum.Far() + offset,
+ frustum.Pose().Pos().Y() + frustum.Far() - offset,
+ (-tan(IGN_DTOR(30)) * frustum.Far()) / frustum.AspectRatio() + offset);
+
+ Vector3d farBottomLeftBad(
+ -tan(IGN_DTOR(30))*frustum.Far() - offset,
+ frustum.Pose().Pos().Y() + frustum.Far() + offset,
+ (-tan(IGN_DTOR(30)) * frustum.Far()) / frustum.AspectRatio() - offset);
+
+ Vector3d farBottomRight(
+ tan(IGN_DTOR(30))*frustum.Far() - offset,
+ frustum.Pose().Pos().Y() + frustum.Far() - offset,
+ (-tan(IGN_DTOR(30)) * frustum.Far()) / frustum.AspectRatio() + offset);
+
+ Vector3d farBottomRightBad(
+ tan(IGN_DTOR(30))*frustum.Far() + offset,
+ frustum.Pose().Pos().Y() + frustum.Far() + offset,
+ (-tan(IGN_DTOR(30)) * frustum.Far()) / frustum.AspectRatio() - offset);
+
+ // Test far clip corners
+ EXPECT_TRUE(frustum.Contains(farTopLeft));
+ EXPECT_FALSE(frustum.Contains(farTopLeftBad));
+
+ EXPECT_TRUE(frustum.Contains(farTopRight));
+ EXPECT_FALSE(frustum.Contains(farTopRightBad));
+
+ EXPECT_TRUE(frustum.Contains(farBottomLeft));
+ EXPECT_FALSE(frustum.Contains(farBottomLeftBad));
+
+ EXPECT_TRUE(frustum.Contains(farBottomRight));
+ EXPECT_FALSE(frustum.Contains(farBottomRightBad));
+
+ // Adjust to 45 degrees rotation
+ frustum.SetPose(Pose3d(1, 1, 0, 0, 0, -IGN_PI*0.25));
+ EXPECT_TRUE(frustum.Contains(Vector3d(2, -1, 0)));
+ EXPECT_FALSE(frustum.Contains(Vector3d(0, 0, 0)));
+ EXPECT_FALSE(frustum.Contains(Vector3d(1, 1, 0)));
+}
diff --git a/src/Helpers.cc b/src/Helpers.cc
new file mode 100644
index 0000000..836a613
--- /dev/null
+++ b/src/Helpers.cc
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 "ignition/math/Helpers.hh"
+
+/////////////////////////////////////////////
+ignition::math::PairOutput ignition::math::Pair(
+ const ignition::math::PairInput _a, const ignition::math::PairInput _b)
+{
+ // Store in 64bit local variable so that we don't overflow.
+ uint64_t a = _a;
+ uint64_t b = _b;
+
+ // Szudzik's function
+ return _a >= _b ?
+ static_cast<PairOutput>(a * a + a + b) :
+ static_cast<PairOutput>(a + b * b);
+}
+
+/////////////////////////////////////////////
+std::tuple<ignition::math::PairInput, ignition::math::PairInput>
+ignition::math::Unpair(const ignition::math::PairOutput _key)
+{
+ // Must explicitly cast so that the _key is not auto cast to a double
+ uint64_t sqrt = static_cast<uint64_t>(
+ std::floor(std::sqrt(static_cast<long double>(_key))));
+ uint64_t sq = sqrt * sqrt;
+
+ return ((_key - sq) >= sqrt) ?
+ std::make_tuple(static_cast<PairInput>(sqrt),
+ static_cast<PairInput>(_key - sq - sqrt)) :
+ std::make_tuple(static_cast<PairInput>(_key - sq),
+ static_cast<PairInput>(sqrt));
+}
diff --git a/src/Helpers_TEST.cc b/src/Helpers_TEST.cc
new file mode 100644
index 0000000..a573ca4
--- /dev/null
+++ b/src/Helpers_TEST.cc
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Rand.hh"
+#include "ignition/math/Vector3.hh"
+#include "ignition/math/Helpers.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+// Test a few function in Helpers
+TEST(HelpersTest, Helpers)
+{
+ EXPECT_EQ(12345, math::parseInt("12345"));
+ EXPECT_EQ(-12345, math::parseInt("-12345"));
+ EXPECT_EQ(-12345, math::parseInt(" -12345"));
+ EXPECT_EQ(0, math::parseInt(" "));
+
+ EXPECT_EQ(math::NAN_I, math::parseInt(""));
+ EXPECT_EQ(math::NAN_I, math::parseInt("?"));
+ EXPECT_EQ(math::NAN_I, math::parseInt("23ab67"));
+
+ EXPECT_DOUBLE_EQ(12.345, math::parseFloat("12.345"));
+ EXPECT_DOUBLE_EQ(-12.345, math::parseFloat("-12.345"));
+ EXPECT_DOUBLE_EQ(-12.345, math::parseFloat(" -12.345"));
+ EXPECT_DOUBLE_EQ(0.0, math::parseFloat(" "));
+ EXPECT_TRUE(math::equal(123.45, math::parseFloat("1.2345e2"), 1e-2));
+ EXPECT_TRUE(math::equal(123.45, math::parseFloat("1.2345e+2"), 1e-2));
+ EXPECT_TRUE(math::equal(123.45, math::parseFloat("1.2345e+002"), 1e-2));
+ EXPECT_TRUE(math::equal(.012345, math::parseFloat("1.2345e-2"), 1e-2));
+ EXPECT_TRUE(math::equal(.012345, math::parseFloat("1.2345e-002"), 1e-2));
+ EXPECT_TRUE(math::equal(1.2345, math::parseFloat("1.2345e+"), 1e-2));
+ EXPECT_TRUE(math::equal(1.2345, math::parseFloat("1.2345e-"), 1e-2));
+ EXPECT_TRUE(math::lessOrNearEqual(1.0, 2.0, 1e-2));
+ EXPECT_TRUE(math::lessOrNearEqual(1.0, 1.0 - 9e-3, 1e-2));
+ EXPECT_FALSE(math::lessOrNearEqual(1.0, 1.0 - 1.1e-2, 1e-2));
+ EXPECT_TRUE(math::greaterOrNearEqual(1.0, 0.5, 1e-2));
+ EXPECT_TRUE(math::greaterOrNearEqual(1.0, 1.0 + 9e-3, 1e-2));
+ EXPECT_FALSE(math::greaterOrNearEqual(1.0, 1.0 + 1.1e-2, 1e-2));
+ EXPECT_DOUBLE_EQ(1.2345, math::parseFloat("1.2345e+0"));
+
+ EXPECT_TRUE(math::isnan(math::parseFloat("")));
+ EXPECT_TRUE(math::isnan(math::parseFloat("?")));
+ EXPECT_TRUE(math::isnan(math::parseFloat("23ab67")));
+
+ EXPECT_EQ(1u, math::roundUpPowerOfTwo(0));
+ EXPECT_EQ(1u, math::roundUpPowerOfTwo(1));
+ EXPECT_EQ(2u, math::roundUpPowerOfTwo(2));
+ EXPECT_EQ(2048u, math::roundUpPowerOfTwo(1025));
+}
+
+/////////////////////////////////////////////////
+// Test Helpers::precision
+TEST(HelpersTest, Precision)
+{
+ EXPECT_DOUBLE_EQ(0, math::precision(0.0, 1));
+ EXPECT_DOUBLE_EQ(0.1, math::precision(0.1, 1));
+ EXPECT_DOUBLE_EQ(0.1, math::precision(0.14, 1));
+ EXPECT_DOUBLE_EQ(0.2, math::precision(0.15, 1));
+ EXPECT_DOUBLE_EQ(0.15, math::precision(0.15, 2));
+
+ EXPECT_DOUBLE_EQ(1, math::precision(1.4, 0));
+ EXPECT_EQ(0, math::precision(0, 0));
+}
+
+/////////////////////////////////////////////////
+// Test Helpers::isPowerOfTwo
+TEST(HelpersTest, PowerOfTwo)
+{
+ EXPECT_FALSE(math::isPowerOfTwo(0));
+ EXPECT_FALSE(math::isPowerOfTwo(3));
+
+ EXPECT_TRUE(math::isPowerOfTwo(1));
+
+ EXPECT_TRUE(math::isPowerOfTwo(2));
+ EXPECT_TRUE(math::isPowerOfTwo(4));
+}
+
+// MSVC report errors on division by zero
+#ifndef _MSC_VER
+/////////////////////////////////////////////////
+// Test Helpers::fixnan functions
+TEST(HelpersTest, FixNaN)
+{
+ EXPECT_DOUBLE_EQ(math::fixnan(1.0 / 0.0), 0.0);
+ EXPECT_DOUBLE_EQ(math::fixnan(-1.0 / 0.0), 0.0);
+ EXPECT_DOUBLE_EQ(math::fixnan(0.0 / 0.0), 0.0);
+
+ EXPECT_DOUBLE_EQ(math::fixnan(42.0), 42.0);
+ EXPECT_DOUBLE_EQ(math::fixnan(-42.0), -42.0);
+
+ EXPECT_FLOAT_EQ(math::fixnan(1.0f / 0.0f), 0.0f);
+ EXPECT_FLOAT_EQ(math::fixnan(-1.0f / 0.0f), 0.0f);
+ EXPECT_FLOAT_EQ(math::fixnan(0.0f / 0.0f), 0.0f);
+
+ EXPECT_FLOAT_EQ(math::fixnan(42.0f), 42.0f);
+ EXPECT_FLOAT_EQ(math::fixnan(-42.0f), -42.0f);
+}
+#endif
+
+/////////////////////////////////////////////////
+// Even test
+TEST(HelpersTest, Even)
+{
+ int i = 1;
+ signed s = 1;
+ signed int si = 1;
+ unsigned u = 1;
+ unsigned int ui = 1;
+
+ EXPECT_FALSE(math::isEven(i));
+ EXPECT_FALSE(math::isEven(s));
+ EXPECT_FALSE(math::isEven(si));
+ EXPECT_FALSE(math::isEven(u));
+ EXPECT_FALSE(math::isEven(ui));
+
+ i = -1;
+ s = -1;
+ si = -1;
+
+ EXPECT_FALSE(math::isEven(i));
+ EXPECT_FALSE(math::isEven(s));
+ EXPECT_FALSE(math::isEven(si));
+
+ i = 4;
+ s = 4;
+ si = 4;
+ u = 4;
+ ui = 4;
+
+ EXPECT_TRUE(math::isEven(i));
+ EXPECT_TRUE(math::isEven(s));
+ EXPECT_TRUE(math::isEven(si));
+ EXPECT_TRUE(math::isEven(u));
+ EXPECT_TRUE(math::isEven(ui));
+
+ i = -2;
+ s = -2;
+ si = -2;
+
+ EXPECT_TRUE(math::isEven(i));
+ EXPECT_TRUE(math::isEven(s));
+ EXPECT_TRUE(math::isEven(si));
+
+ i = 0;
+ s = 0;
+ si = 0;
+ u = 0;
+ ui = 0;
+
+ EXPECT_TRUE(math::isEven(i));
+ EXPECT_TRUE(math::isEven(s));
+ EXPECT_TRUE(math::isEven(si));
+ EXPECT_TRUE(math::isEven(u));
+ EXPECT_TRUE(math::isEven(ui));
+}
+
+/////////////////////////////////////////////////
+// Odd test
+TEST(HelpersTest, Odd)
+{
+ int i = 1;
+ signed s = 1;
+ signed int si = 1;
+ unsigned u = 1;
+ unsigned int ui = 1;
+
+ EXPECT_TRUE(math::isOdd(i));
+ EXPECT_TRUE(math::isOdd(s));
+ EXPECT_TRUE(math::isOdd(si));
+ EXPECT_TRUE(math::isOdd(u));
+ EXPECT_TRUE(math::isOdd(ui));
+
+ i = -1;
+ s = -1;
+ si = -1;
+
+ EXPECT_TRUE(math::isOdd(i));
+ EXPECT_TRUE(math::isOdd(s));
+ EXPECT_TRUE(math::isOdd(si));
+
+ i = 4;
+ s = 4;
+ si = 4;
+ u = 4;
+ ui = 4;
+
+ EXPECT_FALSE(math::isOdd(i));
+ EXPECT_FALSE(math::isOdd(s));
+ EXPECT_FALSE(math::isOdd(si));
+ EXPECT_FALSE(math::isOdd(u));
+ EXPECT_FALSE(math::isOdd(ui));
+
+ i = -2;
+ s = -2;
+ si = -2;
+
+ EXPECT_FALSE(math::isOdd(i));
+ EXPECT_FALSE(math::isOdd(s));
+ EXPECT_FALSE(math::isOdd(si));
+
+ i = 0;
+ s = 0;
+ si = 0;
+ u = 0;
+ ui = 0;
+
+ EXPECT_FALSE(math::isOdd(i));
+ EXPECT_FALSE(math::isOdd(s));
+ EXPECT_FALSE(math::isOdd(si));
+ EXPECT_FALSE(math::isOdd(u));
+ EXPECT_FALSE(math::isOdd(ui));
+}
+
+/////////////////////////////////////////////////
+// Signum test
+TEST(HelpersTest, Signum)
+{
+ int i = 1;
+ signed s = 1;
+ signed int si = 1;
+ unsigned u = 1;
+ unsigned int ui = 1;
+ float f = 1.f;
+ double d = 1.;
+
+ EXPECT_EQ(1, math::signum(i));
+ EXPECT_EQ(1, math::signum(s));
+ EXPECT_EQ(1, math::signum(si));
+ EXPECT_EQ(1, math::signum(u));
+ EXPECT_EQ(1, math::signum(ui));
+ EXPECT_EQ(1, math::signum(f));
+ EXPECT_EQ(1, math::signum(d));
+
+ i = s = si = u = ui = 2;
+ f = 2.f;
+ d = 2.;
+
+ EXPECT_EQ(1, math::signum(i));
+ EXPECT_EQ(1, math::signum(s));
+ EXPECT_EQ(1, math::signum(si));
+ EXPECT_EQ(1, math::signum(u));
+ EXPECT_EQ(1, math::signum(ui));
+ EXPECT_EQ(1, math::signum(f));
+ EXPECT_EQ(1, math::signum(d));
+
+ i = s = si = u = ui = 0;
+ f = 0.f;
+ d = 0.;
+
+ EXPECT_EQ(0, math::signum(i));
+ EXPECT_EQ(0, math::signum(s));
+ EXPECT_EQ(0, math::signum(si));
+ EXPECT_EQ(0, math::signum(u));
+ EXPECT_EQ(0, math::signum(ui));
+ EXPECT_EQ(0, math::signum(f));
+ EXPECT_EQ(0, math::signum(d));
+
+ i = s = si = -1;
+ f = -1.f;
+ d = -1.;
+
+ EXPECT_EQ(-1, math::signum(i));
+ EXPECT_EQ(-1, math::signum(s));
+ EXPECT_EQ(-1, math::signum(si));
+ EXPECT_EQ(-1, math::signum(f));
+ EXPECT_EQ(-1, math::signum(d));
+
+ i = s = si = -2;
+ f = -2.f;
+ d = -2.;
+
+ EXPECT_EQ(-1, math::signum(i));
+ EXPECT_EQ(-1, math::signum(s));
+ EXPECT_EQ(-1, math::signum(si));
+ EXPECT_EQ(-1, math::signum(f));
+ EXPECT_EQ(-1, math::signum(d));
+
+ f = -2.5f;
+ d = -2.5;
+
+ EXPECT_EQ(-1, math::signum(f));
+ EXPECT_EQ(-1, math::signum(d));
+
+ f = 2.5f;
+ d = 2.5;
+
+ EXPECT_EQ(1, math::signum(f));
+ EXPECT_EQ(1, math::signum(d));
+
+ f = 1e-10f;
+ d = 1e-10;
+
+ EXPECT_EQ(1, math::signum(f));
+ EXPECT_EQ(1, math::signum(d));
+}
+
+/////////////////////////////////////////////////
+TEST(HelpersTest, Sort)
+{
+ {
+ int a = 2;
+ int b = -1;
+ math::sort2(a, b);
+ EXPECT_LE(a, b);
+ }
+
+ {
+ int a = 0;
+ int b = 1;
+ math::sort2(a, b);
+ EXPECT_LE(a, b);
+ }
+
+ {
+ int a = 2;
+ int b = -1;
+ int c = 0;
+ math::sort3(a, b, c);
+ EXPECT_LE(a, b);
+ EXPECT_LE(b, c);
+ }
+
+ {
+ unsigned int a = 2;
+ unsigned int b = 1;
+ math::sort2(a, b);
+ EXPECT_LE(a, b);
+ }
+
+ {
+ unsigned int a = 2;
+ unsigned int b = 1;
+ unsigned int c = 0;
+ math::sort3(a, b, c);
+ EXPECT_LE(a, b);
+ EXPECT_LE(b, c);
+ }
+ {
+ unsigned int a = 0;
+ unsigned int b = 1;
+ unsigned int c = 2;
+ math::sort3(a, b, c);
+ EXPECT_LE(a, b);
+ EXPECT_LE(b, c);
+ }
+
+
+ {
+ float a = 2.1f;
+ float b = -1.1e-1f;
+ math::sort2(a, b);
+ EXPECT_LE(a, b);
+ }
+
+ {
+ float a = 34.5f;
+ float b = -1.34f;
+ float c = 0.194f;
+ math::sort3(a, b, c);
+ EXPECT_LE(a, b);
+ EXPECT_LE(b, c);
+ }
+
+ {
+ double a = 2.1;
+ double b = -1.1e-1;
+ math::sort2(a, b);
+ EXPECT_LE(a, b);
+ }
+
+ {
+ double a = 34.5;
+ double b = -1.34;
+ double c = 0.194;
+ math::sort3(a, b, c);
+ EXPECT_LE(a, b);
+ EXPECT_LE(b, c);
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(HelpersTest, Volume)
+{
+ EXPECT_DOUBLE_EQ(IGN_SPHERE_VOLUME(1.0), 4.0*IGN_PI*std::pow(1, 3)/3.0);
+ EXPECT_DOUBLE_EQ(IGN_SPHERE_VOLUME(0.1), 4.0*IGN_PI*std::pow(.1, 3)/3.0);
+ EXPECT_DOUBLE_EQ(IGN_SPHERE_VOLUME(-1.1), 4.0*IGN_PI*std::pow(-1.1, 3)/3.0);
+
+ EXPECT_DOUBLE_EQ(IGN_CYLINDER_VOLUME(0.5, 2.0), 2 * IGN_PI * std::pow(.5, 2));
+ EXPECT_DOUBLE_EQ(IGN_CYLINDER_VOLUME(1, -1), -1 * IGN_PI * std::pow(1, 2));
+
+ EXPECT_DOUBLE_EQ(IGN_BOX_VOLUME(1, 2, 3), 1 * 2 * 3);
+ EXPECT_DOUBLE_EQ(IGN_BOX_VOLUME(.1, .2, .3),
+ IGN_BOX_VOLUME_V(math::Vector3d(0.1, 0.2, 0.3)));
+}
+
+/////////////////////////////////////////////////
+TEST(HelpersTest, Pair)
+{
+#ifdef _MSC_VER
+ math::PairInput maxA = math::MAX_UI16;
+ math::PairInput maxB = math::MAX_UI16;
+#else
+ math::PairInput maxA = math::MAX_UI32;
+ math::PairInput maxB = math::MAX_UI32;
+#endif
+
+ math::PairInput maxC, maxD;
+
+ // Maximum parameters should generate a maximum key
+ math::PairOutput maxKey = math::Pair(maxA, maxB);
+#ifdef _MSC_VER
+ EXPECT_EQ(maxKey, math::MAX_UI32);
+#else
+ EXPECT_EQ(maxKey, math::MAX_UI64);
+#endif
+
+ std::tie(maxC, maxD) = math::Unpair(maxKey);
+ EXPECT_EQ(maxC, maxA);
+ EXPECT_EQ(maxD, maxB);
+
+#ifdef _MSC_VER
+ math::PairInput minA = math::MIN_UI16;
+ math::PairInput minB = math::MIN_UI16;
+#else
+ math::PairInput minA = math::MIN_UI32;
+ math::PairInput minB = math::MIN_UI32;
+#endif
+ math::PairInput minC, minD;
+
+ // Minimum parameters should generate a minimum key
+ math::PairOutput minKey = math::Pair(minA, minB);
+#ifdef _MSC_VER
+ EXPECT_EQ(minKey, math::MIN_UI32);
+#else
+ EXPECT_EQ(minKey, math::MIN_UI64);
+#endif
+
+ std::tie(minC, minD) = math::Unpair(minKey);
+ EXPECT_EQ(minC, minA);
+ EXPECT_EQ(minD, minB);
+
+ // Max key != min key
+ EXPECT_TRUE(minKey != maxKey);
+
+ // Just a simple test case
+ {
+ unsigned int a = 10;
+ unsigned int b = 20;
+ math::PairInput c, d;
+
+ auto key = math::Pair(static_cast<math::PairInput>(a),
+ static_cast<math::PairInput>(b));
+ EXPECT_EQ(key, 410u);
+ EXPECT_TRUE(key != maxKey);
+ EXPECT_TRUE(key != minKey);
+
+ std::tie(c, d) = math::Unpair(key);
+ EXPECT_EQ(c, a);
+ EXPECT_EQ(d, b);
+ }
+
+ {
+ math::PairInput c, d;
+ std::set<math::PairOutput> set;
+
+ // Iterate over range of pairs, and check for unique keys.
+ for (uint16_t a = math::MIN_UI16; a < math::MAX_UI16 - 500;
+ a += static_cast<uint16_t>(math::Rand::IntUniform(100, 500)))
+ {
+ for (uint16_t b = math::MIN_UI16; b < math::MAX_UI16 - 500;
+ b += static_cast<uint16_t>(math::Rand::IntUniform(100, 500)))
+ {
+ math::PairOutput key = math::Pair(a, b);
+ std::tie(c, d) = math::Unpair(key);
+ EXPECT_EQ(a, c);
+ EXPECT_EQ(b, d);
+ EXPECT_TRUE(set.find(key) == set.end());
+ EXPECT_TRUE(key != maxKey);
+ set.insert(key);
+ }
+ }
+
+#ifndef _MSC_VER
+ // Iterate over large numbers, and check for unique keys.
+ for (math::PairInput a = math::MAX_UI32-5000; a < math::MAX_UI32; a++)
+ {
+ for (math::PairInput b = math::MAX_UI32-5000; b < math::MAX_UI32; b++)
+ {
+ math::PairOutput key = math::Pair(a, b);
+ std::tie(c, d) = math::Unpair(key);
+ EXPECT_EQ(a, c);
+ EXPECT_EQ(b, d);
+ EXPECT_TRUE(set.find(key) == set.end());
+ EXPECT_TRUE(key != minKey);
+ set.insert(key);
+ }
+ }
+#endif
+ }
+}
diff --git a/src/Inertial_TEST.cc b/src/Inertial_TEST.cc
new file mode 100644
index 0000000..db9e390
--- /dev/null
+++ b/src/Inertial_TEST.cc
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+#include <cmath>
+
+#include "ignition/math/Inertial.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+/// \brief Compare quaternions, but allow rotations of PI about any axis.
+void CompareModuloPi(const math::Quaterniond &_q1,
+ const math::Quaterniond &_q2,
+ const double _tol = 1e-6)
+{
+ const auto rotErrorEuler = (_q1.Inverse() * _q2).Euler();
+ EXPECT_NEAR(sin(rotErrorEuler.X()), 0.0, _tol);
+ EXPECT_NEAR(sin(rotErrorEuler.Y()), 0.0, _tol);
+ EXPECT_NEAR(sin(rotErrorEuler.Z()), 0.0, _tol);
+}
+
+/////////////////////////////////////////////////
+// Simple constructor, test default values
+TEST(Inertiald_Test, Constructor)
+{
+ math::Inertiald inertial;
+ EXPECT_EQ(inertial.Pose(), math::Pose3d::Zero);
+ EXPECT_EQ(inertial.MassMatrix(), math::MassMatrix3d());
+ EXPECT_EQ(inertial.MOI(), math::Matrix3d::Zero);
+}
+
+/////////////////////////////////////////////////
+// Constructor with default arguments
+// Should match simple constructor and with copy constructor
+TEST(Inertiald_Test, ConstructorDefaultValues)
+{
+ math::Inertiald inertial(math::MassMatrix3d(), math::Pose3d::Zero);
+ EXPECT_EQ(inertial, math::Inertiald());
+ EXPECT_EQ(inertial, math::Inertiald(inertial));
+}
+
+/////////////////////////////////////////////////
+// Constructor with non-default arguments
+TEST(Inertiald_Test, ConstructorNonDefaultValues)
+{
+ const double mass = 5.0;
+ const math::Vector3d Ixxyyzz(2.0, 3.0, 4.0);
+ const math::Vector3d Ixyxzyz(0.2, 0.3, 0.4);
+ math::MassMatrix3d m(mass, Ixxyyzz, Ixyxzyz);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_TRUE(m.IsValid());
+ const math::Pose3d pose(1, 2, 3, IGN_PI/6, 0, 0);
+ math::Inertiald inertial(m, pose);
+
+ // Should not match simple constructor
+ EXPECT_NE(inertial, math::Inertiald());
+
+ // Should match with copy constructor
+ EXPECT_EQ(inertial, math::Inertiald(inertial));
+
+ // Test accessors
+ EXPECT_EQ(inertial.MassMatrix(), m);
+ EXPECT_EQ(inertial.Pose(), pose);
+ EXPECT_TRUE(inertial.MassMatrix().IsPositive());
+ EXPECT_TRUE(inertial.MassMatrix().IsValid());
+
+ // Test assignment operator
+ math::Inertiald inertial2;
+ EXPECT_NE(inertial, inertial2);
+ inertial2 = inertial;
+ EXPECT_EQ(inertial, inertial2);
+}
+
+/////////////////////////////////////////////////
+TEST(Inertiald_Test, CoverageExtra)
+{
+ // getting full destructor coverage
+ math::Inertiald *p = new math::Inertiald;
+ EXPECT_TRUE(p != NULL);
+ delete p;
+}
+
+/////////////////////////////////////////////////
+TEST(Inertiald_Test, Setters)
+{
+ const double mass = 5.0;
+ const math::Vector3d Ixxyyzz(2.0, 3.0, 4.0);
+ const math::Vector3d Ixyxzyz(0.2, 0.3, 0.4);
+ math::MassMatrix3d m(mass, Ixxyyzz, Ixyxzyz);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_TRUE(m.IsValid());
+ const math::Pose3d pose(1, 2, 3, IGN_PI/6, 0, 0);
+ math::Inertiald inertial;
+
+ // Initially invalid
+ EXPECT_FALSE(inertial.SetPose(pose));
+
+ // Valid once valid mass matrix is set
+ EXPECT_TRUE(inertial.SetMassMatrix(m));
+
+ // Verify values
+ EXPECT_EQ(inertial.MassMatrix(), m);
+ EXPECT_EQ(inertial.Pose(), pose);
+
+ // Invalid again if an invalid inertia is set
+ math::MassMatrix3d mInvalid(-1, Ixxyyzz, Ixyxzyz);
+ EXPECT_FALSE(inertial.SetMassMatrix(mInvalid));
+}
+
+/////////////////////////////////////////////////
+TEST(Inertiald_Test, MOI_Diagonal)
+{
+ const double mass = 12.0;
+ const math::Vector3d Ixxyyzz(2.0, 3.0, 4.0);
+ const math::Vector3d Ixyxzyz(0, 0, 0);
+ const math::MassMatrix3d m(mass, Ixxyyzz, Ixyxzyz);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_TRUE(m.IsValid());
+
+ // no rotation, expect MOI's to match
+ {
+ const math::Pose3d pose(0, 0, 0, 0, 0, 0);
+ math::Inertiald inertial(m, pose);
+ EXPECT_EQ(inertial.MOI(), m.MOI());
+ }
+
+ // 90 deg rotation about X axis, expect different MOI
+ {
+ const math::Pose3d pose(0, 0, 0, IGN_PI_2, 0, 0);
+ const math::Matrix3d expectedMOI(2, 0, 0, 0, 4, 0, 0, 0, 3);
+ math::Inertiald inertial(m, pose);
+ EXPECT_NE(inertial.MOI(), m.MOI());
+ EXPECT_EQ(inertial.MOI(), expectedMOI);
+ }
+
+ // 90 deg rotation about Y axis, expect different MOI
+ {
+ const math::Pose3d pose(0, 0, 0, 0, IGN_PI_2, 0);
+ const math::Matrix3d expectedMOI(4, 0, 0, 0, 3, 0, 0, 0, 2);
+ math::Inertiald inertial(m, pose);
+ EXPECT_NE(inertial.MOI(), m.MOI());
+ EXPECT_EQ(inertial.MOI(), expectedMOI);
+ }
+
+ // 90 deg rotation about Z axis, expect different MOI
+ {
+ const math::Pose3d pose(0, 0, 0, 0, 0, IGN_PI_2);
+ const math::Matrix3d expectedMOI(3, 0, 0, 0, 2, 0, 0, 0, 4);
+ math::Inertiald inertial(m, pose);
+ EXPECT_NE(inertial.MOI(), m.MOI());
+ EXPECT_EQ(inertial.MOI(), expectedMOI);
+ }
+
+ // 45 deg rotation about Z axis, expect different MOI
+ {
+ const math::Pose3d pose(0, 0, 0, 0, 0, IGN_PI_4);
+ const math::Matrix3d expectedMOI(2.5, -0.5, 0, -0.5, 2.5, 0, 0, 0, 4);
+ math::Inertiald inertial(m, pose);
+ EXPECT_NE(inertial.MOI(), m.MOI());
+ EXPECT_EQ(inertial.MOI(), expectedMOI);
+
+ // double check with a second MassMatrix3 instance
+ // that has the same base frame MOI but no pose rotation
+ math::MassMatrix3d m2;
+ EXPECT_FALSE(m2.Mass(mass));
+ EXPECT_TRUE(m2.MOI(expectedMOI));
+ EXPECT_EQ(inertial.MOI(), m2.MOI());
+ // There are multiple correct rotations due to symmetry
+ CompareModuloPi(m2.PrincipalAxesOffset(), pose.Rot());
+ }
+}
+
+/////////////////////////////////////////////////
+// Base frame MOI should be invariant
+void SetRotation(const double _mass,
+ const math::Vector3d &_ixxyyzz,
+ const math::Vector3d &_ixyxzyz,
+ const bool _unique = true)
+{
+ const math::MassMatrix3d m(_mass, _ixxyyzz, _ixyxzyz);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_TRUE(m.IsValid());
+
+ math::Pose3d pose(math::Vector3d::Zero, math::Quaterniond::Identity);
+ const math::Inertiald inertialRef(m, pose);
+ const auto moi = inertialRef.MOI();
+
+ const std::vector<math::Quaterniond> rotations = {
+ math::Quaterniond::Identity,
+ math::Quaterniond(IGN_PI, 0, 0),
+ math::Quaterniond(0, IGN_PI, 0),
+ math::Quaterniond(0, 0, IGN_PI),
+ math::Quaterniond(IGN_PI_2, 0, 0),
+ math::Quaterniond(0, IGN_PI_2, 0),
+ math::Quaterniond(0, 0, IGN_PI_2),
+ math::Quaterniond(IGN_PI_4, 0, 0),
+ math::Quaterniond(0, IGN_PI_4, 0),
+ math::Quaterniond(0, 0, IGN_PI_4),
+ math::Quaterniond(IGN_PI/6, 0, 0),
+ math::Quaterniond(0, IGN_PI/6, 0),
+ math::Quaterniond(0, 0, IGN_PI/6),
+ math::Quaterniond(0.1, 0.2, 0.3),
+ math::Quaterniond(-0.1, 0.2, -0.3),
+ math::Quaterniond(0.4, 0.2, 0.5),
+ math::Quaterniond(-0.1, 0.7, -0.7)};
+ for (const auto rot : rotations)
+ {
+ {
+ auto inertial = inertialRef;
+
+ const double tol = -1e-6;
+ EXPECT_TRUE(inertial.SetMassMatrixRotation(rot, tol));
+ EXPECT_EQ(moi, inertial.MOI());
+ if (_unique)
+ {
+ CompareModuloPi(rot, inertial.MassMatrix().PrincipalAxesOffset(tol));
+ }
+
+ EXPECT_TRUE(inertial.SetInertialRotation(rot));
+ EXPECT_EQ(rot, inertial.Pose().Rot());
+ EXPECT_EQ(moi, inertial.MOI());
+ }
+
+ {
+ auto inertial = inertialRef;
+
+ EXPECT_TRUE(inertial.SetInertialRotation(rot));
+ EXPECT_EQ(rot, inertial.Pose().Rot());
+ EXPECT_EQ(moi, inertial.MOI());
+
+ const double tol = -1e-6;
+ EXPECT_TRUE(inertial.SetMassMatrixRotation(rot, tol));
+ EXPECT_EQ(moi, inertial.MOI());
+ if (_unique)
+ {
+ CompareModuloPi(rot, inertial.MassMatrix().PrincipalAxesOffset(tol));
+ }
+ }
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(Inertiald_Test, SetRotationUniqueDiagonal)
+{
+ SetRotation(12, math::Vector3d(2, 3, 4), math::Vector3d::Zero);
+ SetRotation(12, math::Vector3d(3, 2, 4), math::Vector3d::Zero);
+ SetRotation(12, math::Vector3d(2, 4, 3), math::Vector3d::Zero);
+ SetRotation(12, math::Vector3d(3, 4, 2), math::Vector3d::Zero);
+ SetRotation(12, math::Vector3d(4, 2, 3), math::Vector3d::Zero);
+ SetRotation(12, math::Vector3d(4, 3, 2), math::Vector3d::Zero);
+}
+
+/////////////////////////////////////////////////
+TEST(Inertiald_Test, SetRotationUniqueNondiagonal)
+{
+ SetRotation(12, math::Vector3d(2, 3, 4), math::Vector3d(0.3, 0.2, 0.1));
+}
+
+/////////////////////////////////////////////////
+TEST(Inertiald_Test, SetRotationNonuniqueDiagonal)
+{
+ SetRotation(12, math::Vector3d(2, 2, 2), math::Vector3d::Zero, false);
+ SetRotation(12, math::Vector3d(2, 2, 3), math::Vector3d::Zero, false);
+ SetRotation(12, math::Vector3d(2, 3, 2), math::Vector3d::Zero, false);
+ SetRotation(12, math::Vector3d(3, 2, 2), math::Vector3d::Zero, false);
+ SetRotation(12, math::Vector3d(2, 3, 3), math::Vector3d::Zero, false);
+ SetRotation(12, math::Vector3d(3, 2, 3), math::Vector3d::Zero, false);
+ SetRotation(12, math::Vector3d(3, 3, 2), math::Vector3d::Zero, false);
+}
+
+/////////////////////////////////////////////////
+TEST(Inertiald_Test, SetRotationNonuniqueNondiagonal)
+{
+ SetRotation(12, math::Vector3d(4, 4, 3), math::Vector3d(-1, 0, 0), false);
+ SetRotation(12, math::Vector3d(4, 3, 4), math::Vector3d(0, -1, 0), false);
+ SetRotation(12, math::Vector3d(3, 4, 4), math::Vector3d(0, 0, -1), false);
+ SetRotation(12, math::Vector3d(4, 4, 5), math::Vector3d(-1, 0, 0), false);
+ SetRotation(12, math::Vector3d(5, 4, 4), math::Vector3d(0, 0, -1), false);
+ SetRotation(12, math::Vector3d(5.5, 4.125, 4.375),
+ 0.25*math::Vector3d(-sqrt(3), 3.0, -sqrt(3)/2), false);
+ SetRotation(12, math::Vector3d(4.125, 5.5, 4.375),
+ 0.25*math::Vector3d(-sqrt(3), -sqrt(3)/2, 3.0), false);
+}
+
+/////////////////////////////////////////////////
+// test for diagonalizing MassMatrix
+// verify MOI is conserved
+// and that off-diagonal terms are zero
+void Diagonalize(
+ const double _mass,
+ const math::Vector3d &_ixxyyzz,
+ const math::Vector3d &_ixyxzyz)
+{
+ const math::MassMatrix3d m(_mass, _ixxyyzz, _ixyxzyz);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_TRUE(m.IsValid());
+
+ math::Pose3d pose(math::Vector3d::Zero, math::Quaterniond::Identity);
+ math::Inertiald inertial(m, pose);
+ const auto moi = inertial.MOI();
+
+ EXPECT_TRUE(inertial.SetMassMatrixRotation(math::Quaterniond::Identity));
+ EXPECT_EQ(moi, inertial.MOI());
+ EXPECT_EQ(inertial.MassMatrix().OffDiagonalMoments(), math::Vector3d::Zero);
+
+ // try again with negative tolerance
+ EXPECT_TRUE(
+ inertial.SetMassMatrixRotation(math::Quaterniond::Identity, -1e-6));
+ EXPECT_EQ(moi, inertial.MOI());
+ EXPECT_EQ(inertial.MassMatrix().OffDiagonalMoments(), math::Vector3d::Zero);
+}
+
+/////////////////////////////////////////////////
+TEST(Inertiald_Test, Diagonalize)
+{
+ Diagonalize(12, math::Vector3d(2, 3, 4), math::Vector3d::Zero);
+ Diagonalize(12, math::Vector3d(3, 2, 4), math::Vector3d::Zero);
+ Diagonalize(12, math::Vector3d(2, 4, 3), math::Vector3d::Zero);
+ Diagonalize(12, math::Vector3d(3, 4, 2), math::Vector3d::Zero);
+ Diagonalize(12, math::Vector3d(4, 2, 3), math::Vector3d::Zero);
+ Diagonalize(12, math::Vector3d(4, 3, 2), math::Vector3d::Zero);
+ Diagonalize(12, math::Vector3d(2, 3, 4), math::Vector3d(0.3, 0.2, 0.1));
+ Diagonalize(12, math::Vector3d(2, 2, 2), math::Vector3d::Zero);
+ Diagonalize(12, math::Vector3d(2, 2, 3), math::Vector3d::Zero);
+ Diagonalize(12, math::Vector3d(2, 3, 2), math::Vector3d::Zero);
+ Diagonalize(12, math::Vector3d(3, 2, 2), math::Vector3d::Zero);
+ Diagonalize(12, math::Vector3d(2, 3, 3), math::Vector3d::Zero);
+ Diagonalize(12, math::Vector3d(3, 2, 3), math::Vector3d::Zero);
+ Diagonalize(12, math::Vector3d(3, 3, 2), math::Vector3d::Zero);
+ Diagonalize(12, math::Vector3d(4, 4, 3), math::Vector3d(-1, 0, 0));
+ Diagonalize(12, math::Vector3d(4, 3, 4), math::Vector3d(0, -1, 0));
+ Diagonalize(12, math::Vector3d(3, 4, 4), math::Vector3d(0, 0, -1));
+ Diagonalize(12, math::Vector3d(4, 4, 5), math::Vector3d(-1, 0, 0));
+ Diagonalize(12, math::Vector3d(5, 4, 4), math::Vector3d(0, 0, -1));
+ Diagonalize(12, math::Vector3d(5.5, 4.125, 4.375),
+ 0.25*math::Vector3d(-sqrt(3), 3.0, -sqrt(3)/2));
+ Diagonalize(12, math::Vector3d(4.125, 5.5, 4.375),
+ 0.25*math::Vector3d(-sqrt(3), -sqrt(3)/2, 3.0));
+}
+/////////////////////////////////////////////////
+TEST(Inertiald_Test, Addition)
+{
+ // Add two half-cubes together
+ {
+ const double mass = 12.0;
+ const math::Vector3d size(1, 1, 1);
+ math::MassMatrix3d cubeMM3;
+ EXPECT_TRUE(cubeMM3.SetFromBox(mass, size));
+ const math::Inertiald cube(cubeMM3, math::Pose3d::Zero);
+ math::MassMatrix3d half;
+ EXPECT_TRUE(half.SetFromBox(0.5*mass, math::Vector3d(0.5, 1, 1)));
+ math::Inertiald left(half, math::Pose3d(-0.25, 0, 0, 0, 0, 0));
+ math::Inertiald right(half, math::Pose3d(0.25, 0, 0, 0, 0, 0));
+ EXPECT_EQ(cube, left + right);
+ EXPECT_EQ(cube, right + left);
+ // test += operator
+ {
+ math::Inertiald tmp = left;
+ tmp += right;
+ EXPECT_EQ(cube, tmp);
+ }
+ {
+ math::Inertiald tmp = right;
+ tmp += left;
+ EXPECT_EQ(cube, tmp);
+ }
+ // Test EquivalentBox
+ {
+ math::Vector3d size2;
+ math::Quaterniond rot2;
+ EXPECT_TRUE((left + right).MassMatrix().EquivalentBox(size2, rot2));
+ EXPECT_EQ(size, size2);
+ EXPECT_EQ(rot2, math::Quaterniond::Identity);
+ }
+ {
+ math::Vector3d size2;
+ math::Quaterniond rot2;
+ EXPECT_TRUE((right + left).MassMatrix().EquivalentBox(size2, rot2));
+ EXPECT_EQ(size, size2);
+ EXPECT_EQ(rot2, math::Quaterniond::Identity);
+ }
+ }
+
+ // Add two rotated half-cubes together
+ {
+ const double mass = 12.0;
+ const math::Vector3d size(1, 1, 1);
+ math::MassMatrix3d cubeMM3;
+ EXPECT_TRUE(cubeMM3.SetFromBox(mass, size));
+ const math::Inertiald cube(cubeMM3, math::Pose3d(0, 0, 0, IGN_PI_4, 0, 0));
+
+ math::MassMatrix3d half;
+ EXPECT_TRUE(half.SetFromBox(0.5*mass, math::Vector3d(0.5, 1, 1)));
+ math::Inertiald left(half, math::Pose3d(-0.25, 0, 0, IGN_PI_4, 0, 0));
+ math::Inertiald right(half, math::Pose3d(0.25, 0, 0, IGN_PI_4, 0, 0));
+
+ // objects won't match exactly
+ // since inertia matrices will all be in base frame
+ // but mass, center of mass, and base-frame MOI should match
+ EXPECT_NE(cube, left + right);
+ EXPECT_NE(cube, right + left);
+ EXPECT_DOUBLE_EQ(cubeMM3.Mass(), (left + right).MassMatrix().Mass());
+ EXPECT_DOUBLE_EQ(cubeMM3.Mass(), (right + left).MassMatrix().Mass());
+ EXPECT_EQ(cube.Pose().Pos(), (left + right).Pose().Pos());
+ EXPECT_EQ(cube.Pose().Pos(), (right + left).Pose().Pos());
+ EXPECT_EQ(cube.MOI(), (left + right).MOI());
+ EXPECT_EQ(cube.MOI(), (right + left).MOI());
+ }
+
+ // Add eight cubes together into larger cube
+ {
+ const double mass = 12.0;
+ const math::Vector3d size(1, 1, 1);
+ math::MassMatrix3d cubeMM3;
+ EXPECT_TRUE(cubeMM3.SetFromBox(mass, size));
+ const math::Inertiald addedCube =
+ math::Inertiald(cubeMM3, math::Pose3d(-0.5, -0.5, -0.5, 0, 0, 0)) +
+ math::Inertiald(cubeMM3, math::Pose3d(-0.5, 0.5, -0.5, 0, 0, 0)) +
+ math::Inertiald(cubeMM3, math::Pose3d(0.5, -0.5, -0.5, 0, 0, 0)) +
+ math::Inertiald(cubeMM3, math::Pose3d(0.5, 0.5, -0.5, 0, 0, 0)) +
+ math::Inertiald(cubeMM3, math::Pose3d(-0.5, -0.5, 0.5, 0, 0, 0)) +
+ math::Inertiald(cubeMM3, math::Pose3d(-0.5, 0.5, 0.5, 0, 0, 0)) +
+ math::Inertiald(cubeMM3, math::Pose3d(0.5, -0.5, 0.5, 0, 0, 0)) +
+ math::Inertiald(cubeMM3, math::Pose3d(0.5, 0.5, 0.5, 0, 0, 0));
+
+ math::MassMatrix3d trueCubeMM3;
+ EXPECT_TRUE(trueCubeMM3.SetFromBox(8*mass, 2*size));
+ EXPECT_EQ(addedCube, math::Inertiald(trueCubeMM3, math::Pose3d::Zero));
+ }
+
+ // Add eight rotated cubes together into larger cube
+ {
+ const double mass = 12.0;
+ const math::Vector3d size(1, 1, 1);
+ math::MassMatrix3d cubeMM3;
+ EXPECT_TRUE(cubeMM3.SetFromBox(mass, size));
+ const math::Inertiald addedCube =
+ math::Inertiald(cubeMM3, math::Pose3d(-0.5, -0.5, -0.5, 0, 0, 0)) +
+ math::Inertiald(cubeMM3, math::Pose3d(-0.5, 0.5, -0.5, IGN_PI_2, 0, 0)) +
+ math::Inertiald(cubeMM3, math::Pose3d(0.5, -0.5, -0.5, 0, IGN_PI_2, 0)) +
+ math::Inertiald(cubeMM3, math::Pose3d(0.5, 0.5, -0.5, 0, 0, IGN_PI_2)) +
+ math::Inertiald(cubeMM3, math::Pose3d(-0.5, -0.5, 0.5, IGN_PI, 0, 0)) +
+ math::Inertiald(cubeMM3, math::Pose3d(-0.5, 0.5, 0.5, 0, IGN_PI, 0)) +
+ math::Inertiald(cubeMM3, math::Pose3d(0.5, -0.5, 0.5, 0, 0, IGN_PI)) +
+ math::Inertiald(cubeMM3, math::Pose3d(0.5, 0.5, 0.5, 0, 0, 0));
+
+ math::MassMatrix3d trueCubeMM3;
+ EXPECT_TRUE(trueCubeMM3.SetFromBox(8*mass, 2*size));
+ EXPECT_EQ(addedCube, math::Inertiald(trueCubeMM3, math::Pose3d::Zero));
+ }
+}
+
+/////////////////////////////////////////////////
+// Addition operator has different behavior if mass is non-positive
+TEST(Inertiald_Test, AdditionInvalid)
+{
+ // inertias all zero
+ const math::MassMatrix3d m0(0.0, math::Vector3d::Zero, math::Vector3d::Zero);
+ EXPECT_FALSE(m0.IsPositive());
+ EXPECT_FALSE(m0.IsValid());
+
+ // both inertials with zero mass
+ {
+ math::Inertiald left(m0, math::Pose3d(-1, 0, 0, 0, 0, 0));
+ math::Inertiald right(m0, math::Pose3d(1, 0, 0, 0, 0, 0));
+
+ // expect sum to equal left argument
+ EXPECT_EQ(left, left + right);
+ EXPECT_EQ(right, right + left);
+ {
+ math::Inertiald tmp = left;
+ tmp += right;
+ EXPECT_EQ(tmp, left);
+ }
+ {
+ math::Inertiald tmp = right;
+ tmp += left;
+ EXPECT_EQ(tmp, right);
+ }
+ }
+
+ // one inertial with zero inertias should not affect the sum
+ {
+ math::MassMatrix3d m(12.0,
+ math::Vector3d(2, 3, 4),
+ math::Vector3d(0.1, 0.2, 0.3));
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_TRUE(m.IsValid());
+
+ math::Inertiald i(m, math::Pose3d(-1, 0, 0, 0, 0, 0));
+ math::Inertiald i0(m0, math::Pose3d(1, 0, 0, 0, 0, 0));
+
+ // expect i0 to not affect the sum
+ EXPECT_EQ(i, i + i0);
+ EXPECT_EQ(i, i0 + i);
+ {
+ math::Inertiald tmp = i;
+ tmp += i0;
+ EXPECT_EQ(tmp, i);
+ }
+ {
+ math::Inertiald tmp = i0;
+ tmp += i;
+ EXPECT_EQ(tmp, i);
+ }
+
+ EXPECT_TRUE((i + i0).MassMatrix().IsPositive());
+ EXPECT_TRUE((i0 + i).MassMatrix().IsPositive());
+ EXPECT_TRUE((i + i0).MassMatrix().IsValid());
+ EXPECT_TRUE((i0 + i).MassMatrix().IsValid());
+ }
+}
diff --git a/src/Kmeans.cc b/src/Kmeans.cc
new file mode 100644
index 0000000..a524c23
--- /dev/null
+++ b/src/Kmeans.cc
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2014 Open Source Robotics Foundation
+ *
+ * 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 <iostream>
+#include <ignition/math/Kmeans.hh>
+#include <ignition/math/Rand.hh>
+#include "KmeansPrivate.hh"
+
+using namespace ignition;
+using namespace math;
+
+//////////////////////////////////////////////////
+Kmeans::Kmeans(const std::vector<Vector3d> &_obs)
+: dataPtr(new KmeansPrivate)
+{
+ this->Observations(_obs);
+}
+
+//////////////////////////////////////////////////
+Kmeans::~Kmeans()
+{
+ delete this->dataPtr;
+ this->dataPtr = NULL;
+}
+
+//////////////////////////////////////////////////
+std::vector<Vector3d> Kmeans::Observations() const
+{
+ return this->dataPtr->obs;
+}
+
+//////////////////////////////////////////////////
+bool Kmeans::Observations(const std::vector<Vector3d> &_obs)
+{
+ if (_obs.empty())
+ {
+ std::cerr << "Kmeans::SetObservations() error: Observations vector is empty"
+ << std::endl;
+ return false;
+ }
+ this->dataPtr->obs = _obs;
+ return true;
+}
+
+//////////////////////////////////////////////////
+bool Kmeans::AppendObservations(const std::vector<Vector3d> &_obs)
+{
+ if (_obs.empty())
+ {
+ std::cerr << "Kmeans::AppendObservations() error: input vector is empty"
+ << std::endl;
+ return false;
+ }
+ this->dataPtr->obs.insert(this->dataPtr->obs.end(), _obs.begin(), _obs.end());
+ return true;
+}
+
+//////////////////////////////////////////////////
+bool Kmeans::Cluster(int _k,
+ std::vector<Vector3d> &_centroids,
+ std::vector<unsigned int> &_labels)
+{
+ // Sanity check.
+ if (this->dataPtr->obs.empty())
+ {
+ std::cerr << "Kmeans error: The set of observations is empty" << std::endl;
+ return false;
+ }
+
+ if (_k <= 0)
+ {
+ std::cerr << "Kmeans error: The number of clusters has to"
+ << " be positive but its value is [" << _k << "]"
+ << std::endl;
+ return false;
+ }
+
+ if (_k > static_cast<int>(this->dataPtr->obs.size()))
+ {
+ std::cerr << "Kmeans error: The number of clusters [" << _k << "] has to be"
+ << " lower or equal to the number of observations ["
+ << this->dataPtr->obs.size() << "]" << std::endl;
+ return false;
+ }
+
+ size_t changed = 0;
+
+ // Initialize the size of the vectors;
+ this->dataPtr->centroids.clear();
+ this->dataPtr->labels.resize(this->dataPtr->obs.size());
+ this->dataPtr->sums.resize(_k);
+ this->dataPtr->counters.resize(_k);
+
+ for (auto i = 0; i < _k; ++i)
+ {
+ // Choose a random observation and make sure it has not been chosen before.
+ // Note: This is not really random but it's faster than choosing a random
+ // one and verifying that it was not taken before.
+ this->dataPtr->centroids.push_back(this->dataPtr->obs[i]);
+ }
+
+ // Initialize labels.
+ for (auto i = 0u; i < this->dataPtr->obs.size(); ++i)
+ this->dataPtr->labels[i] = 0;
+
+ do
+ {
+ // Reset sums and counters.
+ for (auto i = 0u; i < this->dataPtr->centroids.size(); ++i)
+ {
+ this->dataPtr->sums[i] = Vector3d::Zero;
+ this->dataPtr->counters[i] = 0;
+ }
+ changed = 0;
+
+ for (auto i = 0u; i < this->dataPtr->obs.size(); ++i)
+ {
+ // Update the labels containing the closest centroid for each point.
+ auto label = this->ClosestCentroid(this->dataPtr->obs[i]);
+ if (this->dataPtr->labels[i] != label)
+ {
+ this->dataPtr->labels[i] = label;
+ changed++;
+ }
+ this->dataPtr->sums[label] += this->dataPtr->obs[i];
+ this->dataPtr->counters[label]++;
+ }
+
+ // Update the centroids.
+ for (auto i = 0u; i < this->dataPtr->centroids.size(); ++i)
+ {
+ this->dataPtr->centroids[i] =
+ this->dataPtr->sums[i] / this->dataPtr->counters[i];
+ }
+ }
+ while (changed > (this->dataPtr->obs.size() >> 10));
+
+ _centroids = this->dataPtr->centroids;
+ _labels = this->dataPtr->labels;
+ return true;
+}
+
+//////////////////////////////////////////////////
+unsigned int Kmeans::ClosestCentroid(const Vector3d &_p) const
+{
+ double min = HUGE_VAL;
+ unsigned int minIdx = 0;
+ for (auto i = 0u; i < this->dataPtr->centroids.size(); ++i)
+ {
+ double d = _p.Distance(this->dataPtr->centroids[i]);
+ if (d < min)
+ {
+ min = d;
+ minIdx = i;
+ }
+ }
+ return minIdx;
+}
diff --git a/src/KmeansPrivate.hh b/src/KmeansPrivate.hh
new file mode 100644
index 0000000..8c1cbbb
--- /dev/null
+++ b/src/KmeansPrivate.hh
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_KMEANSPRIVATE_HH_
+#define IGNITION_MATH_KMEANSPRIVATE_HH_
+
+#include <vector>
+#include <ignition/math/Vector3.hh>
+#include <ignition/math/Helpers.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \internal
+ /// \brief Private data for Kmeans class
+ class KmeansPrivate
+ {
+ /// \brief Observations.
+ public: std::vector<Vector3d> obs;
+
+ /// \brief Centroids.
+ public: std::vector<Vector3d> centroids;
+
+ /// \brief Each element stores the cluster to which observation i belongs.
+ public: std::vector<unsigned int> labels;
+
+ /// \brief Used to calculate the centroid of each partition.
+ public: std::vector<Vector3d> sums;
+
+ /// \brief Counts the number of observations contained in each partition.
+ public: std::vector<unsigned int> counters;
+ };
+ }
+}
+#endif
+
+
diff --git a/src/Kmeans_TEST.cc b/src/Kmeans_TEST.cc
new file mode 100644
index 0000000..4478f26
--- /dev/null
+++ b/src/Kmeans_TEST.cc
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2014 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+#include <vector>
+#include "ignition/math/Kmeans.hh"
+
+using namespace ignition;
+
+//////////////////////////////////////////////////
+TEST(KmeansTest, Kmeans)
+{
+ // Create some observations.
+ std::vector<math::Vector3d> obs;
+ obs.push_back(math::Vector3d(1.0, 1.0, 0.0));
+ obs.push_back(math::Vector3d(1.1, 1.0, 0.0));
+ obs.push_back(math::Vector3d(1.2, 1.0, 0.0));
+ obs.push_back(math::Vector3d(1.3, 1.0, 0.0));
+ obs.push_back(math::Vector3d(1.4, 1.0, 0.0));
+ obs.push_back(math::Vector3d(5.0, 1.0, 0.0));
+ obs.push_back(math::Vector3d(5.1, 1.0, 0.0));
+ obs.push_back(math::Vector3d(5.2, 1.0, 0.0));
+ obs.push_back(math::Vector3d(5.3, 1.0, 0.0));
+ obs.push_back(math::Vector3d(5.4, 1.0, 0.0));
+
+ // Initialize Kmeans with two partitions.
+ math::Kmeans kmeans(obs);
+
+ // ::GetObservations()
+ std::vector<math::Vector3d> obsCopy;
+ obsCopy = kmeans.Observations();
+ for (size_t i = 0; i < obsCopy.size(); ++i)
+ EXPECT_EQ(obsCopy[i], obs[i]);
+
+ // ::SetObservations()
+ for (auto &elem : obsCopy)
+ elem += math::Vector3d(0.1, 0.2, 0.0);
+
+ EXPECT_TRUE(kmeans.Observations(obsCopy));
+
+ obsCopy = kmeans.Observations();
+ for (size_t i = 0; i < obsCopy.size(); ++i)
+ EXPECT_EQ(obsCopy[i], obs[i] + math::Vector3d(0.1, 0.2, 0.0));
+ EXPECT_TRUE(kmeans.Observations(obs));
+
+ // ::Cluster()
+ std::vector<math::Vector3d> centroids;
+ std::vector<unsigned int> labels;
+ EXPECT_TRUE(kmeans.Cluster(2, centroids, labels));
+
+ // Check that there are two centroids.
+ EXPECT_EQ(centroids.size(), 2u);
+
+ // Check that the observations are clustered properly.
+ EXPECT_EQ(labels[0], labels[1]);
+ EXPECT_EQ(labels[1], labels[2]);
+ EXPECT_EQ(labels[2], labels[3]);
+ EXPECT_EQ(labels[3], labels[4]);
+
+ EXPECT_NE(labels[4], labels[5]);
+
+ EXPECT_EQ(labels[5], labels[6]);
+ EXPECT_EQ(labels[6], labels[7]);
+ EXPECT_EQ(labels[7], labels[8]);
+ EXPECT_EQ(labels[8], labels[9]);
+
+ // Check the centroids.
+ math::Vector3d expectedCentroid1(1.2, 1.0, 0.0);
+ math::Vector3d expectedCentroid2(5.2, 1.0, 0.0);
+ if (centroids[0] == expectedCentroid1)
+ EXPECT_EQ(centroids[1], expectedCentroid2);
+ else if (centroids[0] == expectedCentroid2)
+ EXPECT_EQ(centroids[1], expectedCentroid1);
+ else
+ FAIL();
+
+ // Try to use an empty observation vector.
+ obsCopy.clear();
+ EXPECT_FALSE(kmeans.Observations(obsCopy));
+
+ // Try to call 'Cluster()' with an empty vector.
+ math::Kmeans kmeansEmpty(obsCopy);
+ EXPECT_FALSE(kmeansEmpty.Cluster(2, centroids, labels));
+
+ // Try to use a non positive k.
+ EXPECT_FALSE(kmeans.Cluster(0, centroids, labels));
+
+ // Try to use a k > num_observations.
+ EXPECT_FALSE(kmeans.Cluster(static_cast<int>(obs.size() + 1),
+ centroids, labels));
+}
+
+//////////////////////////////////////////////////
+TEST(KmeansTest, Append)
+{
+ // Create some observations.
+ std::vector<math::Vector3d> obs, obs2, obsTotal;
+
+ obs.push_back(math::Vector3d(1.0, 1.0, 0.0));
+ obs.push_back(math::Vector3d(1.1, 1.0, 0.0));
+ obs.push_back(math::Vector3d(1.2, 1.0, 0.0));
+ obs.push_back(math::Vector3d(1.3, 1.0, 0.0));
+ obs.push_back(math::Vector3d(1.4, 1.0, 0.0));
+
+ obs2.push_back(math::Vector3d(5.0, 1.0, 0.0));
+ obs2.push_back(math::Vector3d(5.1, 1.0, 0.0));
+ obs2.push_back(math::Vector3d(5.2, 1.0, 0.0));
+ obs2.push_back(math::Vector3d(5.3, 1.0, 0.0));
+ obs2.push_back(math::Vector3d(5.4, 1.0, 0.0));
+
+ obsTotal.insert(obsTotal.end(), obs.begin(), obs.end());
+ obsTotal.insert(obsTotal.end(), obs2.begin(), obs2.end());
+
+ // Initialize Kmeans with two partitions.
+ math::Kmeans kmeans(obs);
+
+ kmeans.AppendObservations(obs2);
+
+ std::vector<math::Vector3d> obsCopy;
+ obsCopy = kmeans.Observations();
+
+ for (size_t i = 0; i < obsCopy.size(); ++i)
+ EXPECT_EQ(obsTotal[i], obsCopy[i]);
+
+ // Append an empty vector.
+ std::vector<math::Vector3d> emptyVector;
+ EXPECT_FALSE(kmeans.AppendObservations(emptyVector));
+}
diff --git a/src/Line2_TEST.cc b/src/Line2_TEST.cc
new file mode 100644
index 0000000..6ac2654
--- /dev/null
+++ b/src/Line2_TEST.cc
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2014 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Line2.hh"
+#include "ignition/math/Helpers.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(Line2Test, Constructor)
+{
+ math::Line2d lineA(0, 0, 10, 10);
+ EXPECT_DOUBLE_EQ(lineA[0].X(), 0.0);
+ EXPECT_DOUBLE_EQ(lineA[0].Y(), 0.0);
+ EXPECT_DOUBLE_EQ(lineA[1].X(), 10.0);
+ EXPECT_DOUBLE_EQ(lineA[1].Y(), 10.0);
+
+ math::Line2d lineB(math::Vector2d(1, 2), math::Vector2d(3, 4));
+ EXPECT_DOUBLE_EQ(lineB[0].X(), 1.0);
+ EXPECT_DOUBLE_EQ(lineB[0].Y(), 2.0);
+ EXPECT_DOUBLE_EQ(lineB[1].X(), 3.0);
+ EXPECT_DOUBLE_EQ(lineB[1].Y(), 4.0);
+
+ EXPECT_NO_THROW(lineB[2].X());
+ EXPECT_DOUBLE_EQ(lineB[2].X(), lineB[1].X());
+ EXPECT_NO_THROW(lineA[0].X());
+}
+
+/////////////////////////////////////////////////
+TEST(Line2Test, Length)
+{
+ math::Line2d lineA(0, 0, 10, 10);
+ EXPECT_NEAR(lineA.Length(), sqrt(200), 1e-10);
+}
+
+#ifdef _MSC_VER
+#pragma warning(push)
+// C4723: potential divide by 0
+#pragma warning(disable : 4723)
+#endif
+/////////////////////////////////////////////////
+TEST(Line2Test, Slope)
+{
+ {
+ math::Line2d line(0, 0, 10, 10);
+ EXPECT_NEAR(line.Slope(), 1.0, 1e-10);
+ }
+
+ {
+ math::Line2d line(0, 0, 0, 10);
+ EXPECT_TRUE(math::isnan(line.Slope()));
+ }
+
+ {
+ math::Line2d line(-10, 0, 100, 0);
+ EXPECT_DOUBLE_EQ(line.Slope(), 0.0);
+ }
+}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+/////////////////////////////////////////////////
+TEST(Line2Test, ParallelLine)
+{
+ {
+ // Line is always parallel with itself
+ math::Line2d line(0, 0, 10, 0);
+ EXPECT_TRUE(line.Parallel(line, 1e-10));
+ }
+
+ {
+ // Degenerate line segment
+ // Still expect Line is parallel with itself
+ math::Line2d line(0, 0, 0, 0);
+ EXPECT_TRUE(line.Parallel(line, 1e-10));
+ }
+
+ math::Line2d lineA(0, 0, 10, 0);
+ math::Line2d lineB(0, 0, 10, 0);
+ EXPECT_TRUE(lineA.Parallel(lineB, 1e-10));
+
+ lineB.Set(0, 0, 0, 10);
+ EXPECT_FALSE(lineA.Parallel(lineB));
+
+ lineB.Set(0, 10, 10, 10);
+ EXPECT_TRUE(lineA.Parallel(lineB));
+
+ lineB.Set(0, 10, 10, 10.00001);
+ EXPECT_FALSE(lineA.Parallel(lineB, 1e-10));
+ EXPECT_FALSE(lineA.Parallel(lineB));
+ EXPECT_TRUE(lineA.Parallel(lineB, 1e-3));
+}
+
+/////////////////////////////////////////////////
+TEST(Line2Test, CollinearLine)
+{
+ {
+ // Line is always collinear with itself
+ math::Line2d line(0, 0, 10, 0);
+ EXPECT_TRUE(line.Collinear(line, 1e-10));
+ }
+
+ math::Line2d lineA(0, 0, 10, 0);
+ math::Line2d lineB(0, 0, 10, 0);
+ EXPECT_TRUE(lineA.Collinear(lineB, 1e-10));
+
+ lineB.Set(0, 10, 10, 10);
+ EXPECT_FALSE(lineA.Collinear(lineB));
+
+ lineB.Set(9, 0, 10, 0.00001);
+ EXPECT_FALSE(lineA.Collinear(lineB, 1e-10));
+ EXPECT_FALSE(lineA.Collinear(lineB));
+ EXPECT_TRUE(lineA.Collinear(lineB, 1e-3));
+}
+
+/////////////////////////////////////////////////
+TEST(Line2Test, CollinearPoint)
+{
+ math::Line2d lineA(0, 0, 10, 0);
+ math::Vector2d pt(0, 0);
+ EXPECT_TRUE(lineA.Collinear(pt));
+ {
+ math::Line2d ptLine(pt, pt);
+ EXPECT_TRUE(lineA.Collinear(ptLine));
+ }
+
+ pt.Set(1000, 0);
+ EXPECT_TRUE(lineA.Collinear(pt, 1e-10));
+ {
+ math::Line2d ptLine(pt, pt);
+ EXPECT_TRUE(lineA.Parallel(ptLine));
+ EXPECT_FALSE(lineA.Intersect(ptLine));
+ EXPECT_FALSE(lineA.Collinear(ptLine, 1e-10));
+
+ pt.Set(10, 0);
+ ptLine.Set(pt, pt);
+ EXPECT_TRUE(lineA.Collinear(ptLine, 1e-10));
+ }
+
+ pt.Set(0, 0.00001);
+ EXPECT_FALSE(lineA.Collinear(pt));
+ EXPECT_TRUE(lineA.Collinear(pt, 1e-3));
+ {
+ math::Line2d ptLine(pt, pt);
+ EXPECT_FALSE(lineA.Collinear(ptLine));
+ EXPECT_TRUE(lineA.Parallel(ptLine));
+ EXPECT_FALSE(lineA.Intersect(ptLine));
+ EXPECT_TRUE(lineA.Intersect(ptLine, 1e-2));
+ EXPECT_TRUE(lineA.Collinear(ptLine, 1e-3));
+ }
+
+ pt.Set(0, -0.00001);
+ EXPECT_FALSE(lineA.Collinear(pt));
+ EXPECT_TRUE(lineA.Collinear(pt, 1e-3));
+ {
+ math::Line2d ptLine(pt, pt);
+ EXPECT_FALSE(lineA.Collinear(ptLine));
+ EXPECT_TRUE(lineA.Collinear(ptLine, 1e-4));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(Line2Test, Intersect)
+{
+ math::Vector2d pt;
+
+ // Parallel horizontal lines
+ math::Line2d lineA(1, 1, 2, 1);
+ math::Line2d lineB(1, 2, 2, 2);
+ EXPECT_FALSE(lineA.Intersect(lineB, pt));
+
+ // Parallel vertical lines
+ lineA.Set(1, 1, 1, 10);
+ lineB.Set(2, 1, 2, 10);
+ EXPECT_FALSE(lineA.Intersect(lineB, pt));
+
+ // Two lines that form an inverted T with a gap
+ lineA.Set(1, 1, 1, 10);
+ lineB.Set(0, 0, 2, 0);
+ EXPECT_FALSE(lineA.Intersect(lineB, pt));
+
+ // Two lines that form a T with a gap
+ lineA.Set(1, 1, 1, 10);
+ lineB.Set(0, 10.1, 2, 10.1);
+ EXPECT_FALSE(lineA.Intersect(lineB, pt));
+
+ // Two lines that form an inverted T with a gap
+ lineA.Set(0, -10, 0, 10);
+ lineB.Set(1, 0, 10, 0);
+ EXPECT_FALSE(lineA.Intersect(lineB, pt));
+
+ // Two lines that form a T with a gap
+ lineA.Set(0, -10, 0, 10);
+ lineB.Set(-1, 0, -10, 0);
+ EXPECT_FALSE(lineA.Intersect(lineB, pt));
+
+ // Two collinear lines, one starts where the other stopped
+ lineA.Set(1, 1, 1, 10);
+ lineB.Set(1, 10, 1, 11);
+ EXPECT_TRUE(lineA.Intersect(lineB, pt));
+ EXPECT_EQ(pt, math::Vector2d(1, 10));
+
+ // Two collinear lines, one overlaps the other
+ lineA.Set(0, 0, 0, 10);
+ lineB.Set(0, 9, 0, 11);
+ EXPECT_TRUE(lineA.Intersect(lineB, pt));
+ EXPECT_EQ(pt, math::Vector2d(0, 9));
+
+ // Two collinear lines, one overlaps the other
+ lineA.Set(0, 0, 0, 10);
+ lineB.Set(0, -10, 0, 1);
+ EXPECT_TRUE(lineA.Intersect(lineB, pt));
+ EXPECT_EQ(pt, math::Vector2d(0, 1));
+
+ // Two intersecting lines
+ lineA.Set(0, 0, 10, 10);
+ lineB.Set(0, 10, 10, 0);
+ EXPECT_TRUE(lineA.Intersect(lineB, pt));
+ EXPECT_EQ(pt, math::Vector2d(5, 5));
+}
+
+/////////////////////////////////////////////////
+TEST(Line2Test, Equality)
+{
+ math::Line2d lineA(1, 1, 2, 1);
+ math::Line2d lineB(1, 2, 2, 2);
+
+ EXPECT_TRUE(lineA != lineB);
+ EXPECT_TRUE(lineA == lineA);
+
+ lineB.Set(1, 1, 2, 1.1);
+ EXPECT_FALSE(lineA == lineB);
+
+ lineB.Set(1, 1, 2.1, 1);
+ EXPECT_FALSE(lineA == lineB);
+
+ lineB.Set(1, 1.1, 2, 1);
+ EXPECT_FALSE(lineA == lineB);
+
+ lineB.Set(1.1, 1, 2, 1);
+ EXPECT_FALSE(lineA == lineB);
+}
+
+/////////////////////////////////////////////////
+TEST(Line2Test, OperatorStreamOut)
+{
+ math::Line2d line(0, 1, 2, 3);
+ std::ostringstream stream;
+ stream << line;
+ EXPECT_EQ(stream.str(), "0 1 2 3");
+}
diff --git a/src/Line3_TEST.cc b/src/Line3_TEST.cc
new file mode 100644
index 0000000..05a0605
--- /dev/null
+++ b/src/Line3_TEST.cc
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Line3.hh"
+#include "ignition/math/Helpers.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(Line3Test, Constructor)
+{
+ math::Line3d lineA(0, 0, 10, 10);
+ EXPECT_DOUBLE_EQ(lineA[0].X(), 0.0);
+ EXPECT_DOUBLE_EQ(lineA[0].Y(), 0.0);
+ EXPECT_DOUBLE_EQ(lineA[0].Z(), 0.0);
+ EXPECT_DOUBLE_EQ(lineA[1].X(), 10.0);
+ EXPECT_DOUBLE_EQ(lineA[1].Y(), 10.0);
+ EXPECT_DOUBLE_EQ(lineA[1].Z(), 0.0);
+
+ math::Line3d lineB(math::Vector3d(1, 2, 3), math::Vector3d(4, 5, 6));
+ EXPECT_DOUBLE_EQ(lineB[0].X(), 1.0);
+ EXPECT_DOUBLE_EQ(lineB[0].Y(), 2.0);
+ EXPECT_DOUBLE_EQ(lineB[0].Z(), 3.0);
+ EXPECT_DOUBLE_EQ(lineB[1].X(), 4.0);
+ EXPECT_DOUBLE_EQ(lineB[1].Y(), 5.0);
+ EXPECT_DOUBLE_EQ(lineB[1].Z(), 6.0);
+
+ math::Line3d lineC(0, 0, 5, 10, 10, 6);
+ EXPECT_DOUBLE_EQ(lineC[0].X(), 0.0);
+ EXPECT_DOUBLE_EQ(lineC[0].Y(), 0.0);
+ EXPECT_DOUBLE_EQ(lineC[0].Z(), 5.0);
+ EXPECT_DOUBLE_EQ(lineC[1].X(), 10.0);
+ EXPECT_DOUBLE_EQ(lineC[1].Y(), 10.0);
+ EXPECT_DOUBLE_EQ(lineC[1].Z(), 6.0);
+
+ EXPECT_NO_THROW(lineB[2].X());
+ EXPECT_DOUBLE_EQ(lineB[2].X(), lineB[1].X());
+ EXPECT_NO_THROW(lineA[0].X());
+}
+
+TEST(Line3Test, Set)
+{
+ math::Line3d lineA;
+ lineA.Set(1, 1, 2, 2);
+ EXPECT_DOUBLE_EQ(lineA[0].X(), 1.0);
+ EXPECT_DOUBLE_EQ(lineA[0].Y(), 1.0);
+ EXPECT_DOUBLE_EQ(lineA[0].Z(), 0.0);
+ EXPECT_DOUBLE_EQ(lineA[1].X(), 2.0);
+ EXPECT_DOUBLE_EQ(lineA[1].Y(), 2.0);
+ EXPECT_DOUBLE_EQ(lineA[1].Z(), 0.0);
+
+ lineA.Set(10, 11, 12, 13, 14, 15);
+ EXPECT_DOUBLE_EQ(lineA[0].X(), 10.0);
+ EXPECT_DOUBLE_EQ(lineA[0].Y(), 11.0);
+ EXPECT_DOUBLE_EQ(lineA[0].Z(), 12.0);
+ EXPECT_DOUBLE_EQ(lineA[1].X(), 13.0);
+ EXPECT_DOUBLE_EQ(lineA[1].Y(), 14.0);
+ EXPECT_DOUBLE_EQ(lineA[1].Z(), 15.0);
+
+ lineA.SetA(math::Vector3<double>(0, -1, -2));
+ EXPECT_DOUBLE_EQ(lineA[0].X(), 0.0);
+ EXPECT_DOUBLE_EQ(lineA[0].Y(), -1.0);
+ EXPECT_DOUBLE_EQ(lineA[0].Z(), -2.0);
+ EXPECT_DOUBLE_EQ(lineA[1].X(), 13.0);
+ EXPECT_DOUBLE_EQ(lineA[1].Y(), 14.0);
+ EXPECT_DOUBLE_EQ(lineA[1].Z(), 15.0);
+
+ lineA.SetB(math::Vector3<double>(5, 6, 7));
+ EXPECT_DOUBLE_EQ(lineA[0].X(), 0.0);
+ EXPECT_DOUBLE_EQ(lineA[0].Y(), -1.0);
+ EXPECT_DOUBLE_EQ(lineA[0].Z(), -2.0);
+ EXPECT_DOUBLE_EQ(lineA[1].X(), 5.0);
+ EXPECT_DOUBLE_EQ(lineA[1].Y(), 6.0);
+ EXPECT_DOUBLE_EQ(lineA[1].Z(), 7.0);
+}
+
+/////////////////////////////////////////////////
+TEST(Line3Test, Length)
+{
+ math::Line3d lineA(0, 0, 0, 10, 10, 10);
+ EXPECT_NEAR(lineA.Length(), sqrt(300), 1e-10);
+}
+
+/////////////////////////////////////////////////
+TEST(Line3Test, Equality)
+{
+ math::Line3d lineA(1, 1, 1, 2, 1, 2);
+ math::Line3d lineB(1, 2, 3, 2, 2, 4);
+
+ EXPECT_TRUE(lineA != lineB);
+ EXPECT_TRUE(lineA == lineA);
+
+ lineB.Set(1, 1, 1, 2, 1.1, 2);
+ EXPECT_FALSE(lineA == lineB);
+
+ lineB.Set(1, 1, 1, 2.1, 1, 2);
+ EXPECT_FALSE(lineA == lineB);
+
+ lineB.Set(1, 1, 1.1, 2, 1, 2);
+ EXPECT_FALSE(lineA == lineB);
+
+ lineB.Set(1.1, 1, 1, 2, 1, 2);
+ EXPECT_FALSE(lineA == lineB);
+}
+
+/////////////////////////////////////////////////
+TEST(Line3Test, OperatorStreamOut)
+{
+ math::Line3d line(0, 1, 4, 2, 3, 7);
+ std::ostringstream stream;
+ stream << line;
+ EXPECT_EQ(stream.str(), "0 1 4 2 3 7");
+}
+
+/////////////////////////////////////////////////
+TEST(Line3Test, CopyConstructor)
+{
+ math::Line3d lineA(0, 1, 4, 2, 3, 7);
+ math::Line3d lineB(lineA);
+
+ EXPECT_EQ(lineA, lineB);
+}
+
+/////////////////////////////////////////////////
+TEST(Line3Test, OperatorAssign)
+{
+ math::Line3d lineA(0, 1, 4, 2, 3, 7);
+ math::Line3d lineB = lineA;
+
+ EXPECT_EQ(lineA, lineB);
+}
+
+/////////////////////////////////////////////////
+TEST(Line3Test, Direction)
+{
+ math::Line3d lineA(1, 1, 1, 0, 0, 0);
+ math::Line3d lineB(2, 2, 2, 0, 0, 0);
+ math::Line3d lineC(0, 0, 0, 1, 1, 1);
+ EXPECT_TRUE(lineA.Direction() == (lineA[1] - lineA[0]).Normalize());
+ EXPECT_TRUE(lineA.Direction() == lineB.Direction());
+ EXPECT_FALSE(lineA.Direction() == lineC.Direction());
+
+ lineA.Set(1, 1, 2, 1, 1, 10);
+ EXPECT_TRUE(lineA.Direction() == math::Vector3d::UnitZ);
+
+ lineA.Set(1, 5, 1, 1, 1, 1);
+ EXPECT_TRUE(lineA.Direction() == -math::Vector3d::UnitY);
+
+ lineA.Set(1, 1, 1, 7, 1, 1);
+ EXPECT_TRUE(lineA.Direction() == math::Vector3d::UnitX);
+}
+
+/////////////////////////////////////////////////
+TEST(Line3Test, Within)
+{
+ math::Line3d line(0, 0, 0, 1, 1, 1);
+ EXPECT_TRUE(line.Within(math::Vector3d(0, 0, 0)));
+ EXPECT_TRUE(line.Within(math::Vector3d(1, 1, 1)));
+ EXPECT_TRUE(line.Within(math::Vector3d(0.5, 0.5, 0.5)));
+
+ EXPECT_FALSE(line.Within(math::Vector3d(-0.5, 0.5, 0.5)));
+ EXPECT_FALSE(line.Within(math::Vector3d(0.5, -0.5, 0.5)));
+ EXPECT_FALSE(line.Within(math::Vector3d(0.5, 0.5, -0.5)));
+}
+
+/////////////////////////////////////////////////
+TEST(Line3Test, Distance)
+{
+ math::Line3d line(0, 0, 0, 0, 1, 0);
+ math::Line3d result;
+
+ EXPECT_TRUE(line.Distance(math::Line3d(1, 0.5, 0, -1, 0.5, 0), result));
+ EXPECT_DOUBLE_EQ(result.Length(), 0);
+ EXPECT_EQ(result, math::Line3d(0, 0.5, 0, 0, 0.5, 0));
+
+ EXPECT_TRUE(line.Distance(math::Line3d(1, 0, 0, -1, 0, 0), result));
+ EXPECT_DOUBLE_EQ(result.Length(), 0);
+ EXPECT_EQ(result, math::Line3d(0, 0, 0, 0, 0, 0));
+
+ EXPECT_TRUE(line.Distance(math::Line3d(1, 1.1, 0, -1, 1.1, 0), result));
+ EXPECT_NEAR(result.Length(), 0.1, 1e-4);
+ EXPECT_EQ(result, math::Line3d(0, 1, 0, 0, 1.1, 0));
+
+ EXPECT_TRUE(line.Distance(math::Line3d(1, 0.5, 0.4, -1, 0.5, 0.4), result));
+ EXPECT_NEAR(result.Length(), 0.4, 1e-4);
+ EXPECT_EQ(result, math::Line3d(0, 0.5, 0, 0, 0.5, 0.4));
+
+ EXPECT_TRUE(line.Distance(math::Line3d(0, 0.5, 1, 1, 0.5, 0), result));
+ EXPECT_NEAR(result.Length(), sin(IGN_PI / 4), 1e-4);
+ EXPECT_EQ(result, math::Line3d(0, 0.5, 0, 0.5, 0.5, 0.5));
+
+ // Expect true when lines are parallel
+ EXPECT_TRUE(line.Distance(math::Line3d(2, 0, 0, 2, 1, 0), result));
+ EXPECT_EQ(result[0], line[0]);
+ EXPECT_EQ(result[1], math::Vector3d(2, 0, 0));
+
+ EXPECT_TRUE(line.Distance(math::Line3d(2, 1, 0, 2, 0, 0), result));
+ EXPECT_EQ(result[0], line[0]);
+ EXPECT_EQ(result[1], math::Vector3d(2, 0, 0));
+
+ EXPECT_TRUE(line.Distance(math::Line3d(1, 1, 0, 1, 2, 0), result));
+ EXPECT_EQ(result[0], line[1]);
+ EXPECT_EQ(result[1], math::Vector3d(1, 1, 0));
+
+ EXPECT_TRUE(line.Distance(math::Line3d(1, 2, 0, 1, 1, 0), result));
+ EXPECT_EQ(result[0], line[1]);
+ EXPECT_EQ(result[1], math::Vector3d(1, 1, 0));
+
+ // Expect false when the passed in line is a point
+ EXPECT_FALSE(line.Distance(math::Line3d(2, 0, 0, 2, 0, 0), result));
+
+ // Expect false when the first line is a point.
+ line.Set(0, 0, 0, 0, 0, 0);
+ EXPECT_FALSE(line.Distance(math::Line3d(2, 0, 0, 2, 1, 0), result));
+}
+
+/////////////////////////////////////////////////
+TEST(Line3Test, Intersect)
+{
+ math::Line3d line(0, 0, 0, 0, 1, 0);
+ math::Vector3d pt;
+
+ EXPECT_TRUE(line.Intersect(math::Line3d(1, 0.5, 0, -1, 0.5, 0)));
+ EXPECT_TRUE(line.Intersect(math::Line3d(1, 0.5, 0, -1, 0.5, 0), pt));
+ EXPECT_EQ(pt, math::Vector3d(0, 0.5, 0));
+
+ EXPECT_TRUE(line.Intersect(math::Line3d(1, 0, 0, -1, 0, 0)));
+ EXPECT_TRUE(line.Intersect(math::Line3d(1, 0, 0, -1, 0, 0), pt));
+ EXPECT_EQ(pt, math::Vector3d(0, 0, 0));
+
+ EXPECT_TRUE(line.Intersect(math::Line3d(1, 1, 0, -1, 1, 0)));
+ EXPECT_TRUE(line.Intersect(math::Line3d(1, 1, 0, -1, 1, 0), pt));
+ EXPECT_EQ(pt, math::Vector3d(0, 1, 0));
+
+ EXPECT_TRUE(line.Intersect(math::Line3d(0, 0.5, -1, 0, 0.5, 1)));
+ EXPECT_TRUE(line.Intersect(math::Line3d(0, 0.5, -1, 0, 0.5, 1), pt));
+ EXPECT_EQ(pt, math::Vector3d(0, 0.5, 0));
+
+ EXPECT_TRUE(line.Intersect(math::Line3d(-1, 0.5, -1, 1, 0.5, 1)));
+ EXPECT_TRUE(line.Intersect(math::Line3d(-1, 0.5, -1, 1, 0.5, 1), pt));
+ EXPECT_EQ(pt, math::Vector3d(0, 0.5, 0));
+
+ EXPECT_FALSE(line.Intersect(math::Line3d(1, 1.1, 0, -1, 1.1, 0)));
+ EXPECT_FALSE(line.Intersect(math::Line3d(1, -0.1, 0, -1, -0.1, 0)));
+
+ EXPECT_FALSE(line.Intersect(math::Line3d(0.1, 0.1, 0, 0.6, 0.6, 0)));
+ EXPECT_FALSE(line.Intersect(math::Line3d(-0.1, 0, 0, -0.1, 1, 0)));
+
+ EXPECT_TRUE(line.Intersect(math::Line3d(0, -1, 0, 0, 0.1, 0)));
+ EXPECT_TRUE(line.Intersect(math::Line3d(0, 1, 0, 0, 1.1, 0)));
+}
+
+/////////////////////////////////////////////////
+TEST(Line3Test, Parallel)
+{
+ math::Line3d line(0, 0, 0, 0, 1, 0);
+ EXPECT_TRUE(line.Parallel(math::Line3d(1, 0, 0, 1, 1, 0)));
+ EXPECT_TRUE(line.Parallel(math::Line3d(1, 1, 0, 1, 0, 0)));
+ EXPECT_TRUE(line.Parallel(math::Line3d(0, 0, 0, 0, 10, 0)));
+ EXPECT_TRUE(line.Parallel(math::Line3d(-100, 100, 20, -100, 200, 20)));
+
+ EXPECT_FALSE(line.Parallel(math::Line3d(1, 0, 0, 1, 1, 1)));
+ EXPECT_FALSE(line.Parallel(math::Line3d(1, 0, 0, 2, 0, 0)));
+ EXPECT_FALSE(line.Parallel(math::Line3d(1, 0, 1, 2, 0, 1)));
+}
+
+/////////////////////////////////////////////////
+TEST(Line3Test, Coplanar)
+{
+ math::Line3d line(0, 0, 0, 0, 1, 0);
+ EXPECT_TRUE(line.Coplanar(math::Line3d(1, 0, 0, 1, 1, 0)));
+ EXPECT_TRUE(line.Coplanar(math::Line3d(0, 0, 0, 0, 10, 0)));
+ EXPECT_TRUE(line.Coplanar(math::Line3d(-100, 100, 20, -100, 200, 20)));
+
+ EXPECT_FALSE(line.Coplanar(math::Line3d(1, 0, 0, 1, 1, 1)));
+ EXPECT_FALSE(line.Coplanar(math::Line3d(1, 0, 1, 2, 0, 0)));
+}
diff --git a/src/MassMatrix3_TEST.cc b/src/MassMatrix3_TEST.cc
new file mode 100644
index 0000000..b56094e
--- /dev/null
+++ b/src/MassMatrix3_TEST.cc
@@ -0,0 +1,858 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+#include <cmath>
+
+#include "ignition/math/Helpers.hh"
+#include "ignition/math/MassMatrix3.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(MassMatrix3dTest, Constructors)
+{
+ // Simple constructor, test default values
+ {
+ math::MassMatrix3d m;
+ EXPECT_DOUBLE_EQ(m.Mass(), 0.0);
+ EXPECT_DOUBLE_EQ(m.IXX(), 0.0);
+ EXPECT_DOUBLE_EQ(m.IYY(), 0.0);
+ EXPECT_DOUBLE_EQ(m.IZZ(), 0.0);
+ EXPECT_DOUBLE_EQ(m.IXY(), 0.0);
+ EXPECT_DOUBLE_EQ(m.IXZ(), 0.0);
+ EXPECT_DOUBLE_EQ(m.IYZ(), 0.0);
+ EXPECT_EQ(m.DiagonalMoments(), math::Vector3d::Zero);
+ EXPECT_EQ(m.OffDiagonalMoments(), math::Vector3d::Zero);
+ EXPECT_EQ(m.MOI(), math::Matrix3d::Zero);
+ EXPECT_FALSE(m.IsPositive());
+ EXPECT_FALSE(m.IsValid());
+ }
+
+ // Constructor with default arguments
+ // Should match simple constructor and with copy constructor
+ {
+ math::MassMatrix3d m(0, math::Vector3d::Zero, math::Vector3d::Zero);
+ EXPECT_EQ(m, math::MassMatrix3d());
+ EXPECT_EQ(m, math::MassMatrix3d(m));
+ EXPECT_FALSE(m.IsPositive());
+ EXPECT_FALSE(m.IsValid());
+ }
+
+ // Constructor with non-default arguments
+ {
+ const double mass = 5.0;
+ const math::Vector3d Ixxyyzz(2.0, 3.0, 4.0);
+ const math::Vector3d Ixyxzyz(0.2, 0.3, 0.4);
+ const math::Matrix3d MOI(2.0, 0.2, 0.3,
+ 0.2, 3.0, 0.4,
+ 0.3, 0.4, 4.0);
+ math::MassMatrix3d m(mass, Ixxyyzz, Ixyxzyz);
+
+ // Should not match simple constructor
+ EXPECT_NE(m, math::MassMatrix3d());
+
+ // Should match with copy constructor
+ EXPECT_EQ(m, math::MassMatrix3d(m));
+
+ // Test accessors
+ EXPECT_DOUBLE_EQ(m.Mass(), mass);
+ EXPECT_DOUBLE_EQ(m.IXX(), Ixxyyzz[0]);
+ EXPECT_DOUBLE_EQ(m.IYY(), Ixxyyzz[1]);
+ EXPECT_DOUBLE_EQ(m.IZZ(), Ixxyyzz[2]);
+ EXPECT_DOUBLE_EQ(m.IXY(), Ixyxzyz[0]);
+ EXPECT_DOUBLE_EQ(m.IXZ(), Ixyxzyz[1]);
+ EXPECT_DOUBLE_EQ(m.IYZ(), Ixyxzyz[2]);
+ EXPECT_EQ(m.DiagonalMoments(), Ixxyyzz);
+ EXPECT_EQ(m.OffDiagonalMoments(), Ixyxzyz);
+ EXPECT_EQ(m.MOI(), MOI);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_TRUE(m.IsValid());
+
+ // Test assignment operator
+ math::MassMatrix3d m2;
+ EXPECT_NE(m, m2);
+ m2 = m;
+ EXPECT_EQ(m, m2);
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(MassMatrix3dTest, Setters)
+{
+ const double mass = 5.0;
+ const math::Vector3d Ixxyyzz(2.0, 3.0, 4.0);
+ const math::Vector3d Ixyxzyz(0.2, 0.3, 0.4);
+ const math::Matrix3d MOI(2.0, 0.2, 0.3,
+ 0.2, 3.0, 0.4,
+ 0.3, 0.4, 4.0);
+
+ // Scalar setters with simple constructor
+ // MassMatrix3 won't be valid until enough properties are set
+ {
+ math::MassMatrix3d m;
+ EXPECT_FALSE(m.IsPositive());
+ EXPECT_FALSE(m.IsValid());
+
+ // Initially invalid
+ EXPECT_FALSE(m.Mass(mass));
+ EXPECT_FALSE(m.IXX(Ixxyyzz[0]));
+ EXPECT_FALSE(m.IYY(Ixxyyzz[1]));
+
+ // Valid once enough properties are set
+ EXPECT_TRUE(m.IZZ(Ixxyyzz[2]));
+ EXPECT_TRUE(m.IXY(Ixyxzyz[0]));
+ EXPECT_TRUE(m.IXZ(Ixyxzyz[1]));
+ EXPECT_TRUE(m.IYZ(Ixyxzyz[2]));
+
+ // Verify values
+ EXPECT_DOUBLE_EQ(m.Mass(), mass);
+ EXPECT_DOUBLE_EQ(m.IXX(), Ixxyyzz[0]);
+ EXPECT_DOUBLE_EQ(m.IYY(), Ixxyyzz[1]);
+ EXPECT_DOUBLE_EQ(m.IZZ(), Ixxyyzz[2]);
+ EXPECT_DOUBLE_EQ(m.IXY(), Ixyxzyz[0]);
+ EXPECT_DOUBLE_EQ(m.IXZ(), Ixyxzyz[1]);
+ EXPECT_DOUBLE_EQ(m.IYZ(), Ixyxzyz[2]);
+ EXPECT_EQ(m.DiagonalMoments(), Ixxyyzz);
+ EXPECT_EQ(m.OffDiagonalMoments(), Ixyxzyz);
+ EXPECT_EQ(m.MOI(), MOI);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_TRUE(m.IsValid());
+
+ // Invalid again if an invalid inertia is set
+ EXPECT_FALSE(m.Mass(-1));
+ }
+
+ // Test vector setters for moment of inertia
+ {
+ math::MassMatrix3d m;
+ EXPECT_FALSE(m.IsPositive());
+ EXPECT_FALSE(m.IsValid());
+
+ // Initially invalid
+ EXPECT_FALSE(m.Mass(mass));
+
+ // Valid once enough properties are set
+ EXPECT_TRUE(m.DiagonalMoments(Ixxyyzz));
+ EXPECT_TRUE(m.OffDiagonalMoments(Ixyxzyz));
+
+ // Verify values
+ EXPECT_DOUBLE_EQ(m.Mass(), mass);
+ EXPECT_DOUBLE_EQ(m.IXX(), Ixxyyzz[0]);
+ EXPECT_DOUBLE_EQ(m.IYY(), Ixxyyzz[1]);
+ EXPECT_DOUBLE_EQ(m.IZZ(), Ixxyyzz[2]);
+ EXPECT_DOUBLE_EQ(m.IXY(), Ixyxzyz[0]);
+ EXPECT_DOUBLE_EQ(m.IXZ(), Ixyxzyz[1]);
+ EXPECT_DOUBLE_EQ(m.IYZ(), Ixyxzyz[2]);
+ EXPECT_EQ(m.DiagonalMoments(), Ixxyyzz);
+ EXPECT_EQ(m.OffDiagonalMoments(), Ixyxzyz);
+ EXPECT_EQ(m.MOI(), MOI);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_TRUE(m.IsValid());
+
+ // Invalid if an invalid inertia is set
+ EXPECT_FALSE(m.IXX(-1));
+ }
+
+ // Test Matrix3 setter for moment of inertia
+ {
+ math::MassMatrix3d m;
+ EXPECT_FALSE(m.IsPositive());
+ EXPECT_FALSE(m.IsValid());
+
+ // Initially invalid
+ EXPECT_FALSE(m.Mass(mass));
+
+ // Valid once enough properties are set
+ EXPECT_TRUE(m.MOI(MOI));
+
+ // Verify values
+ EXPECT_DOUBLE_EQ(m.Mass(), mass);
+ EXPECT_DOUBLE_EQ(m.IXX(), Ixxyyzz[0]);
+ EXPECT_DOUBLE_EQ(m.IYY(), Ixxyyzz[1]);
+ EXPECT_DOUBLE_EQ(m.IZZ(), Ixxyyzz[2]);
+ EXPECT_DOUBLE_EQ(m.IXY(), Ixyxzyz[0]);
+ EXPECT_DOUBLE_EQ(m.IXZ(), Ixyxzyz[1]);
+ EXPECT_DOUBLE_EQ(m.IYZ(), Ixyxzyz[2]);
+ EXPECT_EQ(m.DiagonalMoments(), Ixxyyzz);
+ EXPECT_EQ(m.OffDiagonalMoments(), Ixyxzyz);
+ EXPECT_EQ(m.MOI(), MOI);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_TRUE(m.IsValid());
+
+ // Invalid if an excessive off-diagonal inertia is set
+ EXPECT_FALSE(m.IXY(1e3));
+ }
+
+ // // Test atomic InertiaMatrix setter
+ {
+ math::MassMatrix3d m;
+ EXPECT_FALSE(m.IsPositive());
+ EXPECT_FALSE(m.IsValid());
+
+ // Initially invalid
+ EXPECT_FALSE(m.Mass(mass));
+
+ // Valid once enough properties are set
+ EXPECT_TRUE(m.InertiaMatrix(2, 3, 4, 0.2, 0.3, 0.4));
+
+ // Verify values
+ EXPECT_DOUBLE_EQ(m.Mass(), mass);
+ EXPECT_DOUBLE_EQ(m.IXX(), Ixxyyzz[0]);
+ EXPECT_DOUBLE_EQ(m.IYY(), Ixxyyzz[1]);
+ EXPECT_DOUBLE_EQ(m.IZZ(), Ixxyyzz[2]);
+ EXPECT_DOUBLE_EQ(m.IXY(), Ixyxzyz[0]);
+ EXPECT_DOUBLE_EQ(m.IXZ(), Ixyxzyz[1]);
+ EXPECT_DOUBLE_EQ(m.IYZ(), Ixyxzyz[2]);
+ EXPECT_EQ(m.DiagonalMoments(), Ixxyyzz);
+ EXPECT_EQ(m.OffDiagonalMoments(), Ixyxzyz);
+ EXPECT_EQ(m.MOI(), MOI);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_TRUE(m.IsValid());
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(MassMatrix3dTest, CoverageExtra)
+{
+ // getting full destructor coverage
+ math::MassMatrix3d *p = new math::MassMatrix3d;
+ EXPECT_TRUE(p != NULL);
+ delete p;
+}
+
+/////////////////////////////////////////////////
+TEST(MassMatrix3dTest, PrincipalMoments)
+{
+ // Diagonal inertia moments (1, 1, 1)
+ {
+ math::MassMatrix3d m(1.0, math::Vector3d::One, math::Vector3d::Zero);
+ EXPECT_EQ(m.PrincipalMoments(), math::Vector3d::One);
+
+ // Minor perturbations of product moments
+ // shouldn't affect PrincipalMoments, given the tolerance
+ // of the Vector3 equality operator
+ EXPECT_TRUE(m.IXY(1e-10));
+ EXPECT_TRUE(m.IXZ(2e-10));
+ EXPECT_TRUE(m.IYZ(3e-10));
+ EXPECT_EQ(m.PrincipalMoments(), math::Vector3d::One);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_TRUE(m.IsValid());
+ }
+
+ // Non-equal eigen-moments
+ {
+ const math::Vector3d Ixxyyzz(2.0, 3.0, 4.0);
+ math::MassMatrix3d m(1.0, Ixxyyzz, math::Vector3d::Zero);
+ EXPECT_TRUE(m.DiagonalMoments(Ixxyyzz));
+ EXPECT_EQ(m.PrincipalMoments(), Ixxyyzz);
+
+ // Minor perturbation of product moments
+ EXPECT_TRUE(m.IXY(1e-10));
+ EXPECT_TRUE(m.IXZ(2e-10));
+ EXPECT_TRUE(m.IYZ(3e-10));
+ EXPECT_EQ(m.PrincipalMoments(), Ixxyyzz);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_TRUE(m.IsValid());
+ }
+
+ // Non-trivial off-diagonal product moments
+ // Symmetric positive definite matrix from
+ // Strang's Intro to Linear Algebra textbook
+ // This isn't actually a valid inertia matrix though,
+ // since it doesn't satisfy the triangle inequality
+ // 2-sqrt(2) + 2 ~= 2.59
+ // 2+sqrt(2) ~= 3.41
+ {
+ const math::Vector3d Ixxyyzz(2.0, 2.0, 2.0);
+ const math::Vector3d Ixyxzyz(-1.0, 0, -1.0);
+ math::MassMatrix3d m(1.0, Ixxyyzz, Ixyxzyz);
+ const math::Vector3d Ieigen(2-IGN_SQRT2, 2, 2+IGN_SQRT2);
+ EXPECT_EQ(m.PrincipalMoments(), Ieigen);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_FALSE(m.IsValid());
+ }
+
+ // Non-trivial off-diagonal product moments
+ // variant of previous example that is valid inertia matrix
+ {
+ const math::Vector3d Ixxyyzz(4.0, 4.0, 4.0);
+ const math::Vector3d Ixyxzyz(-1.0, 0, -1.0);
+ math::MassMatrix3d m(1.0, Ixxyyzz, Ixyxzyz);
+ const math::Vector3d Ieigen(4-IGN_SQRT2, 4, 4+IGN_SQRT2);
+ EXPECT_EQ(m.PrincipalMoments(), Ieigen);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_TRUE(m.IsValid());
+ }
+
+ // Degenerate matrix with eigenvalue of 0
+ // not positive definite
+ {
+ const math::Vector3d Ixxyyzz(1.0, 1.0, 1.0);
+ const math::Vector3d Ixyxzyz(1.0, 0, 0);
+ math::MassMatrix3d m(1.0, Ixxyyzz, Ixyxzyz);
+ const math::Vector3d Ieigen(0, 1, 2);
+ EXPECT_EQ(m.PrincipalMoments(), Ieigen);
+ EXPECT_FALSE(m.IsPositive());
+ EXPECT_FALSE(m.IsValid());
+ }
+
+ // Matrix with large condition number
+ // barely positive definite
+ // invalid inertia matrix since it doesn't satisfy triangle inequality
+ // 5e-6 + 1.0 < 2+5e-6
+ {
+ const math::Vector3d Ixxyyzz(1.0, 1.00001, 1.0);
+ const math::Vector3d Ixyxzyz(1.0, 0, 0);
+ math::MassMatrix3d m(1.0, Ixxyyzz, Ixyxzyz);
+ const math::Vector3d Ieigen(5e-6, 1.0, 2 + 5e-6);
+ EXPECT_EQ(m.PrincipalMoments(), Ieigen);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_FALSE(m.IsValid());
+ }
+
+ // Another matrix with large condition number
+ // invalid inertia matrix since it doesn't satisfy triangle inequality
+ // 0.98 + 1e8-1e3 < 1e8+1e3
+ // 0.98 < 2e3
+ {
+ const math::Vector3d Ixxyyzz(1e8, 1e8, 1);
+ const math::Vector3d Ixyxzyz(1e3, 1e3, 1e3);
+ math::MassMatrix3d m(1.0, Ixxyyzz, Ixyxzyz);
+ const math::Vector3d Ieigen(0.98, 1e8-1e3, 1e8+1e3);
+ // the accuracy is approximately 2e-2
+ EXPECT_TRUE(m.PrincipalMoments().Equal(Ieigen, 2.5e-2));
+ EXPECT_FALSE(m.PrincipalMoments().Equal(Ieigen, 1.5e-2));
+ // the default tolerance for == is 1e-6
+ // so this should resolve as not equal
+ EXPECT_NE(m.PrincipalMoments(), Ieigen);
+ EXPECT_TRUE(m.IsPositive());
+ EXPECT_FALSE(m.IsValid());
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(MassMatrix3dTest, PrincipalAxesOffsetIdentity)
+{
+ // Identity inertia matrix, expect unit quaternion
+ math::MassMatrix3d m(1.0, math::Vector3d::One, math::Vector3d::Zero);
+ EXPECT_EQ(m.PrincipalAxesOffset(), math::Quaterniond());
+
+ // Scale the diagonal terms
+ EXPECT_TRUE(m.DiagonalMoments(3.5 * math::Vector3d::One));
+ EXPECT_TRUE(m.OffDiagonalMoments(math::Vector3d::Zero));
+ EXPECT_TRUE(m.IsValid());
+ EXPECT_EQ(m.PrincipalAxesOffset(), math::Quaterniond::Identity);
+}
+
+/////////////////////////////////////////////////
+/// \brief Helper function for verifying principal moments
+/// and axes offset by reconstructing the moment of inertia matrix
+/// from the eigenvectors and diagonalized matrix.
+/// \param[in] _m mass matrix to verify
+/// \param[in] _tolerance relative tolerance to use
+void VerifyPrincipalMomentsAndAxes(const math::MassMatrix3d &_m,
+ const double _tolerance = 1e-6)
+{
+ auto q = _m.PrincipalAxesOffset(_tolerance);
+ auto R = math::Matrix3d(q);
+ EXPECT_FALSE(math::equal(q.W(), 0.0, 1e-6) && math::equal(q.X(), 0.0, 1e-6) &&
+ math::equal(q.Y(), 0.0, 1e-6) && math::equal(q.Z(), 0.0, 1e-6));
+ auto moments = _m.PrincipalMoments(_tolerance);
+ math::Matrix3d L(moments[0], 0, 0,
+ 0, moments[1], 0,
+ 0, 0, moments[2]);
+ EXPECT_EQ(_m.MOI(), R * L * R.Transposed());
+}
+
+/////////////////////////////////////////////////
+/// \brief Helper function for testing diagonal inertia matrices.
+/// Expect the following:
+/// * that principal moments match the diagonal values,
+/// * that mass matrix is valid,
+/// * that principal axes have no offset (identity quaternion)
+/// * that reconstructed moment of inertia matrix matches the original
+/// \param[in] _moments Diagonal/principal moments of inertia.
+void VerifyDiagonalMomentsAndAxes(const math::Vector3d &_moments)
+{
+ math::MassMatrix3d m(1.0, math::Vector3d::Zero, math::Vector3d::Zero);
+ EXPECT_TRUE(m.DiagonalMoments(_moments));
+ EXPECT_EQ(m.PrincipalMoments(), m.DiagonalMoments());
+ EXPECT_TRUE(m.IsValid());
+ // Expect unit quaternion
+ EXPECT_EQ(m.PrincipalAxesOffset(), math::Quaterniond::Identity);
+ VerifyPrincipalMomentsAndAxes(m);
+
+ // Try with negative tolerance, expect sorted principal moments
+ math::Vector3d sortedMoments;
+ {
+ double m0 = _moments[0];
+ double m1 = _moments[1];
+ double m2 = _moments[2];
+ math::sort3(m0, m1, m2);
+ sortedMoments.Set(m0, m1, m2);
+ }
+ const double tolerance = -1e-6;
+ EXPECT_EQ(m.PrincipalMoments(tolerance), sortedMoments);
+ VerifyPrincipalMomentsAndAxes(m, tolerance);
+}
+
+/////////////////////////////////////////////////
+TEST(MassMatrix3dTest, PrincipalAxesOffsetDiagonal)
+{
+ // all repeated moments [3, 3, 3]
+ VerifyDiagonalMomentsAndAxes(math::Vector3d(3.0, 3.0, 3.0));
+ // repeated moments [2, 3, 3]
+ VerifyDiagonalMomentsAndAxes(math::Vector3d(2.0, 3.0, 3.0));
+ VerifyDiagonalMomentsAndAxes(math::Vector3d(3.0, 2.0, 3.0));
+ VerifyDiagonalMomentsAndAxes(math::Vector3d(3.0, 3.0, 2.0));
+ // repeated moments [2, 2, 3]
+ VerifyDiagonalMomentsAndAxes(math::Vector3d(3.0, 2.0, 2.0));
+ VerifyDiagonalMomentsAndAxes(math::Vector3d(2.0, 3.0, 2.0));
+ VerifyDiagonalMomentsAndAxes(math::Vector3d(2.0, 2.0, 3.0));
+ // non-repeated moments
+ VerifyDiagonalMomentsAndAxes(math::Vector3d(2.0, 3.0, 4.0));
+ VerifyDiagonalMomentsAndAxes(math::Vector3d(4.0, 2.0, 3.0));
+ VerifyDiagonalMomentsAndAxes(math::Vector3d(3.0, 4.0, 2.0));
+ VerifyDiagonalMomentsAndAxes(math::Vector3d(2.0, 4.0, 3.0));
+ VerifyDiagonalMomentsAndAxes(math::Vector3d(3.0, 2.0, 4.0));
+ VerifyDiagonalMomentsAndAxes(math::Vector3d(4.0, 3.0, 2.0));
+}
+
+/////////////////////////////////////////////////
+/// \brief Helper function for testing non-diagonal inertia matrices.
+/// Expect the following:
+/// * that principal moments match the supplied values,
+/// * that mass matrix is valid,
+/// * that principal axes have an offset (non-identity quaternion)
+/// * that reconstructed moment of inertia matrix matches the original
+/// \param[in] _principalMoments Expected principal moments of inertia
+/// \param[in] _ixxyyzz Diagonal moments of inertia.
+/// \param[in] _ixyxzyz Off-diagonal moments of inertia.
+/// \param[in] _tolerance Absolute tolerance for eigenvalue expectation.
+void VerifyNondiagonalMomentsAndAxes(const math::Vector3d &_principalMoments,
+ const math::Vector3d &_ixxyyzz,
+ const math::Vector3d &_ixyxzyz,
+ const double _tolerance = 1e-6)
+{
+ math::MassMatrix3d m(1.0, _ixxyyzz, _ixyxzyz);
+ // EXPECT_EQ with default tolerance of 1e-6
+ // this outputs more useful error messages
+ EXPECT_EQ(m.PrincipalMoments(_tolerance), _principalMoments);
+ // also check equality with custom tolerance for small moments
+ EXPECT_TRUE(
+ m.PrincipalMoments(_tolerance).Equal(_principalMoments, _tolerance));
+ EXPECT_TRUE(m.IsValid());
+ // Expect non-unit quaternion
+ EXPECT_NE(m.PrincipalAxesOffset(_tolerance), math::Quaterniond());
+ VerifyPrincipalMomentsAndAxes(m, _tolerance);
+
+ // Try also with negated tolerance
+ EXPECT_TRUE(
+ m.PrincipalMoments(-_tolerance).Equal(_principalMoments, _tolerance));
+ VerifyPrincipalMomentsAndAxes(m, -_tolerance);
+}
+
+/////////////////////////////////////////////////
+TEST(MassMatrix3dTest, PrincipalAxesOffsetRepeat)
+{
+ // Principal moments: [3, 3, 5]
+ // Non-zero Ixy
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 3, 5),
+ math::Vector3d(4, 4, 3), math::Vector3d(-1, 0, 0));
+ // Non-zero Ixz
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 3, 5),
+ math::Vector3d(4, 3, 4), math::Vector3d(0, -1, 0));
+ // Non-zero Iyz
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 3, 5),
+ math::Vector3d(3, 4, 4), math::Vector3d(0, 0, -1));
+
+ // Principal moments: [3, 5, 5]
+ // Non-zero Ixy
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 5, 5),
+ math::Vector3d(4, 4, 5), math::Vector3d(-1, 0, 0));
+ // Non-zero Ixz
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 5, 5),
+ math::Vector3d(4, 5, 4), math::Vector3d(0, -1, 0));
+ // Non-zero Iyz
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 5, 5),
+ math::Vector3d(5, 4, 4), math::Vector3d(0, 0, -1));
+
+ // Principal moments: [4, 5, 5]
+ // Rotated by [45, 45, 0] degrees
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(4, 5, 5),
+ math::Vector3d(4.5, 4.75, 4.75),
+ 0.25*math::Vector3d(-IGN_SQRT2, IGN_SQRT2, 1));
+ // Rotated by [-45, 45, 0] degrees
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(4, 5, 5),
+ math::Vector3d(4.5, 4.75, 4.75),
+ 0.25*math::Vector3d(IGN_SQRT2, IGN_SQRT2, -1));
+ // Rotated by [45, -45, 0] degrees
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(4, 5, 5),
+ math::Vector3d(4.5, 4.75, 4.75),
+ 0.25*math::Vector3d(IGN_SQRT2, -IGN_SQRT2, 1));
+ // Rotated by [-45, -45, 0] degrees
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(4, 5, 5),
+ math::Vector3d(4.5, 4.75, 4.75),
+ 0.25*math::Vector3d(-IGN_SQRT2, -IGN_SQRT2, -1));
+
+ // Principal moments: [4, 4, 5]
+ // Rotated by [45, 45, 45] degrees
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(4, 4, 5),
+ math::Vector3d(4.5, 4.25, 4.25),
+ 0.25*math::Vector3d(-IGN_SQRT2, IGN_SQRT2, -1));
+ // different rotation
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(4, 4, 5),
+ math::Vector3d(4.5, 4.25, 4.25),
+ 0.25*math::Vector3d(IGN_SQRT2, IGN_SQRT2, 1));
+ // different rotation
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(4, 4, 5),
+ math::Vector3d(4.5, 4.25, 4.25),
+ 0.25*math::Vector3d(-IGN_SQRT2, -IGN_SQRT2, 1));
+ // different rotation
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(4, 4, 5),
+ math::Vector3d(4.5, 4.25, 4.25),
+ 0.25*math::Vector3d(IGN_SQRT2, -IGN_SQRT2, -1));
+
+ // Principal moments [4e-9, 4e-9, 5e-9]
+ // Rotated by [45, 45, 45] degrees
+ // use tolerance of 1e-15
+ VerifyNondiagonalMomentsAndAxes(1e-9 * math::Vector3d(4, 4, 5),
+ 1e-9 * math::Vector3d(4.5, 4.25, 4.25),
+ 0.25e-9*math::Vector3d(-IGN_SQRT2, IGN_SQRT2, -1), 1e-15);
+ // different rotation
+ VerifyNondiagonalMomentsAndAxes(1e-9 * math::Vector3d(4, 4, 5),
+ 1e-9 * math::Vector3d(4.5, 4.25, 4.25),
+ 0.25e-9*math::Vector3d(IGN_SQRT2, IGN_SQRT2, 1));
+ // different rotation
+ VerifyNondiagonalMomentsAndAxes(1e-9 * math::Vector3d(4, 4, 5),
+ 1e-9 * math::Vector3d(4.5, 4.25, 4.25),
+ 0.25e-9*math::Vector3d(-IGN_SQRT2, -IGN_SQRT2, 1));
+ // different rotation
+ VerifyNondiagonalMomentsAndAxes(1e-9 * math::Vector3d(4, 4, 5),
+ 1e-9 * math::Vector3d(4.5, 4.25, 4.25),
+ 0.25e-9*math::Vector3d(IGN_SQRT2, -IGN_SQRT2, -1), 1e-15);
+
+ // Principal moments [4, 4, 6]
+ // rotate by 30, 60, 0 degrees
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(4, 4, 6),
+ math::Vector3d(5.5, 4.125, 4.375),
+ 0.25*math::Vector3d(-sqrt(3), 3.0, -sqrt(3)/2));
+
+ // different rotation
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(4, 4, 6),
+ math::Vector3d(4.125, 5.5, 4.375),
+ 0.25*math::Vector3d(-sqrt(3), -sqrt(3)/2, 3.0));
+}
+
+/////////////////////////////////////////////////
+TEST(MassMatrix3dTest, PrincipalAxesOffsetNoRepeat)
+{
+ // Non-diagonal inertia matrix with f1 = 0
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 4, 6),
+ math::Vector3d(3.0, 5.0, 5.0),
+ math::Vector3d(0, 0, 1));
+ // Non-diagonal inertia matrix with f1 = 0
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 4, 6),
+ math::Vector3d(3.0, 5.0, 5.0),
+ math::Vector3d(0, 0, -1));
+
+ // Non-diagonal inertia matrix with f2 = 0
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 4, 6),
+ math::Vector3d(5.0, 4.0, 4.0),
+ math::Vector3d(-1, 1, 0));
+ // Non-diagonal inertia matrix with f2 = 0
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 4, 6),
+ math::Vector3d(5.0, 4.0, 4.0),
+ math::Vector3d(1, -1, 0));
+ // Non-diagonal inertia matrix with f2 = 0
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 4, 6),
+ math::Vector3d(5.0, 4.0, 4.0),
+ math::Vector3d(-1, -1, 0));
+ // Non-diagonal inertia matrix with f2 = 0
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 4, 6),
+ math::Vector3d(5.0, 4.0, 4.0),
+ math::Vector3d(1, 1, 0));
+
+ // Similar non-diagonal inertia matrix with f2 != 0
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 4, 6),
+ math::Vector3d(4.0, 4.0, 5.0),
+ math::Vector3d(0, 1, 1));
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 4, 6),
+ math::Vector3d(4.0, 4.0, 5.0),
+ math::Vector3d(0, -1, 1));
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 4, 6),
+ math::Vector3d(4.0, 4.0, 5.0),
+ math::Vector3d(0, 1, -1));
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(3, 4, 6),
+ math::Vector3d(4.0, 4.0, 5.0),
+ math::Vector3d(0, -1, -1));
+
+ // Test case for v = 0
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(2.5, 3.5, 4.0),
+ math::Vector3d(4.0, 3.0, 3.0),
+ math::Vector3d(0.0, 0, -0.5));
+
+ // Tri-diagonal matrix with identical diagonal terms
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(4-IGN_SQRT2, 4, 4+IGN_SQRT2),
+ math::Vector3d(4.0, 4.0, 4.0),
+ math::Vector3d(-1.0, 0, -1.0));
+ // small magnitude, use tolerance of 1e-15
+ VerifyNondiagonalMomentsAndAxes(
+ 1e-9 * math::Vector3d(4-IGN_SQRT2, 4, 4+IGN_SQRT2),
+ 1e-9 * math::Vector3d(4.0, 4.0, 4.0),
+ 1e-9 * math::Vector3d(-1.0, 0, -1.0), 1e-15);
+
+ // Tri-diagonal matrix with unique diagonal terms
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(5-sqrt(3), 5, 5+sqrt(3)),
+ math::Vector3d(4.0, 5.0, 6.0),
+ math::Vector3d(-1.0, 0, -1.0));
+ // small magnitude, use tolerance of 1e-15
+ VerifyNondiagonalMomentsAndAxes(1e-9*math::Vector3d(5-sqrt(3), 5, 5+sqrt(3)),
+ 1e-9 * math::Vector3d(4.0, 5.0, 6.0),
+ 1e-9 * math::Vector3d(-1.0, 0, -1.0), 1e-15);
+
+ // Nonzero values for all off-axis terms
+ VerifyNondiagonalMomentsAndAxes(math::Vector3d(10, 12, 14),
+ math::Vector3d(13, 11.75, 11.25),
+ math::Vector3d(-0.5*sqrt(3), 1.5, 0.25*sqrt(3)));
+
+ // Nonzero values for all off-axis terms
+ VerifyNondiagonalMomentsAndAxes(
+ math::Vector3d(6.6116, 8.2393186767, 13.983881323),
+ math::Vector3d(11.6116, 8.6116, 8.6116),
+ math::Vector3d(2, 2, 2));
+}
+
+/////////////////////////////////////////////////
+TEST(MassMatrix3dTest, EquivalentBox)
+{
+ // Default mass matrix with non-positive inertia
+ {
+ math::MassMatrix3d m;
+ math::Vector3d size;
+ math::Quaterniond rot;
+
+ // size is all zeros, so SetFromBox should fail
+ EXPECT_FALSE(m.SetFromBox(0.0, size, rot));
+ EXPECT_FALSE(m.SetFromBox(size, rot));
+
+ // even if mass is valid, it should not be set if size is invalid
+ EXPECT_FALSE(m.SetFromBox(1.0, size, rot));
+ EXPECT_DOUBLE_EQ(m.Mass(), 0.0);
+
+ // equivalent box should not be findable
+ EXPECT_FALSE(m.EquivalentBox(size, rot));
+ }
+
+ // Moment of inertia matrix that doesn't satisfy triangle inequality
+ {
+ const math::Vector3d ixxyyzz(2.0, 2.0, 2.0);
+ const math::Vector3d ixyxzyz(-1.0, 0, -1.0);
+ math::MassMatrix3d m(1.0, ixxyyzz, ixyxzyz);
+ math::Vector3d size;
+ math::Quaterniond rot;
+ EXPECT_FALSE(m.EquivalentBox(size, rot));
+ }
+
+ // Identity inertia matrix
+ // expect cube with side length sqrt(6)
+ {
+ const double mass = 1.0;
+ math::MassMatrix3d m(mass, math::Vector3d::One, math::Vector3d::Zero);
+ math::Vector3d size;
+ math::Vector3d sizeTrue(sqrt(6) * math::Vector3d::One);
+ math::Quaterniond rot;
+ math::Quaterniond rotTrue(math::Quaterniond::Identity);
+ EXPECT_TRUE(m.EquivalentBox(size, rot));
+ EXPECT_EQ(size, sizeTrue);
+ EXPECT_EQ(rot, rotTrue);
+
+ // create new MassMatrix3d
+ // it initially has zero mass, so SetFromBox(size, rot) will fail
+ math::MassMatrix3d m2;
+ EXPECT_FALSE(m2.SetFromBox(sizeTrue, rotTrue));
+ EXPECT_TRUE(m2.SetFromBox(mass, sizeTrue, rotTrue));
+ EXPECT_EQ(m, m2);
+ }
+
+ // unit box with mass 1.0
+ {
+ const double mass = 1.0;
+ const math::Vector3d size(1, 1, 1);
+ double ixx = mass/12 * (std::pow(size.Y(), 2) + std::pow(size.Z(), 2));
+ double iyy = mass/12 * (std::pow(size.Z(), 2) + std::pow(size.X(), 2));
+ double izz = mass/12 * (std::pow(size.X(), 2) + std::pow(size.Y(), 2));
+ math::Vector3d ixxyyzz(ixx, iyy, izz);
+ math::MassMatrix3d m(mass, ixxyyzz, math::Vector3d::Zero);
+ math::Vector3d size2;
+ math::Quaterniond rot;
+ EXPECT_TRUE(m.EquivalentBox(size2, rot));
+ EXPECT_EQ(size, size2);
+ EXPECT_EQ(rot, math::Quaterniond::Identity);
+
+ math::MassMatrix3d m2;
+ EXPECT_TRUE(m2.SetFromBox(mass, size, rot));
+ EXPECT_EQ(m, m2);
+ }
+
+ // box 1x4x9
+ {
+ const double mass = 12.0;
+ const math::Vector3d ixxyyzz(97, 82, 17);
+ math::MassMatrix3d m(mass, ixxyyzz, math::Vector3d::Zero);
+ math::Vector3d size;
+ math::Quaterniond rot;
+ EXPECT_TRUE(m.EquivalentBox(size, rot));
+ EXPECT_EQ(size, math::Vector3d(1, 4, 9));
+ EXPECT_EQ(rot, math::Quaterniond::Identity);
+
+ math::MassMatrix3d m2;
+ EXPECT_TRUE(m2.SetFromBox(mass, size, rot));
+ EXPECT_EQ(m, m2);
+ }
+
+ // box 1x4x9 rotated by 90 degrees around Z
+ {
+ const double mass = 12.0;
+ const math::Vector3d ixxyyzz(82, 17, 97);
+ math::MassMatrix3d m(mass, ixxyyzz, math::Vector3d::Zero);
+ math::Vector3d size;
+ math::Quaterniond rot;
+ EXPECT_TRUE(m.EquivalentBox(size, rot, -1e-6));
+ EXPECT_EQ(size, math::Vector3d(9, 4, 1));
+ EXPECT_EQ(rot, math::Quaterniond(0, 0, IGN_PI/2));
+
+ math::MassMatrix3d m2;
+ EXPECT_TRUE(m2.SetFromBox(mass, size, rot));
+ EXPECT_EQ(m, m2);
+ }
+
+ // box 1x4x9 rotated by 45 degrees around Z
+ {
+ const double mass = 12.0;
+ const math::Vector3d ixxyyzz(49.5, 49.5, 97);
+ const math::Vector3d ixyxzyz(-32.5, 0.0, 0.0);
+ math::MassMatrix3d m(mass, ixxyyzz, ixyxzyz);
+ math::Vector3d size;
+ math::Quaterniond rot;
+ EXPECT_TRUE(m.EquivalentBox(size, rot));
+ EXPECT_EQ(size, math::Vector3d(9, 4, 1));
+ // There are multiple correct rotations due to box symmetry
+ EXPECT_TRUE(rot == math::Quaterniond(0, 0, IGN_PI/4) ||
+ rot == math::Quaterniond(IGN_PI, 0, IGN_PI/4));
+
+ math::MassMatrix3d m2;
+ EXPECT_TRUE(m2.SetFromBox(mass, size, rot));
+ EXPECT_EQ(m, m2);
+ }
+
+ // long slender box
+ {
+ const double mass = 12.0;
+ const math::Vector3d ixxyyzz(1, 1, 2e-6);
+ math::MassMatrix3d m(mass, ixxyyzz, math::Vector3d::Zero);
+ math::Vector3d size;
+ math::Quaterniond rot;
+ EXPECT_TRUE(m.EquivalentBox(size, rot));
+ EXPECT_EQ(size, math::Vector3d(1e-3, 1e-3, 1));
+ EXPECT_EQ(rot, math::Quaterniond::Identity);
+
+ math::MassMatrix3d m2;
+ EXPECT_TRUE(m2.SetFromBox(mass, size, rot));
+ EXPECT_EQ(m, m2);
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(MassMatrix3dTest, SetFromCylinderZ)
+{
+ const math::Quaterniond q0 = math::Quaterniond::Identity;
+
+ // Default mass matrix with non-positive inertia
+ {
+ math::MassMatrix3d m;
+
+ // input is all zeros, so SetFromCylinderZ should fail
+ EXPECT_FALSE(m.SetFromCylinderZ(0, 0, 0, q0));
+ EXPECT_FALSE(m.SetFromCylinderZ(0, 0, q0));
+
+ // even if some parameters are valid, none should be set if they
+ // are not all valid
+ EXPECT_FALSE(m.SetFromCylinderZ(1, 0, 0, q0));
+ EXPECT_FALSE(m.SetFromCylinderZ(1, 1, 0, q0));
+ EXPECT_FALSE(m.SetFromCylinderZ(1, 0, 1, q0));
+ EXPECT_DOUBLE_EQ(m.Mass(), 0.0);
+ }
+
+ // unit cylinder with mass 1.0
+ {
+ const double mass = 1.0;
+ const double length = 1.0;
+ const double radius = 0.5;
+ math::MassMatrix3d m;
+ EXPECT_TRUE(m.SetFromCylinderZ(mass, length, radius, q0));
+
+ double ixx = mass / 12.0 * (3*std::pow(radius, 2) + std::pow(length, 2));
+ double iyy = ixx;
+ double izz = mass / 2.0 * std::pow(radius, 2);
+ const math::Vector3d ixxyyzz(ixx, iyy, izz);
+ EXPECT_EQ(m.DiagonalMoments(), ixxyyzz);
+ EXPECT_EQ(m.OffDiagonalMoments(), math::Vector3d::Zero);
+
+ // double the length and radius
+ EXPECT_TRUE(m.SetFromCylinderZ(mass, 2*length, 2*radius, q0));
+ EXPECT_EQ(m.DiagonalMoments(), 4*ixxyyzz);
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(MassMatrix3dTest, SetFromSphere)
+{
+ // Default mass matrix with non-positive inertia
+ {
+ math::MassMatrix3d m;
+
+ // input is all zeros, so SetFromSphere should fail
+ EXPECT_FALSE(m.SetFromSphere(0.0, 0.0));
+ EXPECT_FALSE(m.SetFromSphere(0.0));
+
+ // even if mass is valid, it should not be set if radius is invalid
+ EXPECT_FALSE(m.SetFromSphere(1.0, 0.0));
+ EXPECT_DOUBLE_EQ(m.Mass(), 0.0);
+ }
+
+ // unit sphere with mass 1.0
+ {
+ const double mass = 1.0;
+ const double radius = 0.5;
+ math::MassMatrix3d m;
+ EXPECT_TRUE(m.SetFromSphere(mass, radius));
+
+ double ixx = 0.4 * mass * std::pow(radius, 2);
+ double iyy = ixx;
+ double izz = ixx;
+ const math::Vector3d ixxyyzz(ixx, iyy, izz);
+ EXPECT_EQ(m.DiagonalMoments(), ixxyyzz);
+ EXPECT_EQ(m.OffDiagonalMoments(), math::Vector3d::Zero);
+
+ // double the radius
+ EXPECT_TRUE(m.SetFromSphere(mass, 2*radius));
+ EXPECT_EQ(m.DiagonalMoments(), 4*ixxyyzz);
+ }
+}
+
diff --git a/src/Matrix3_TEST.cc b/src/Matrix3_TEST.cc
new file mode 100644
index 0000000..ddcdbf6
--- /dev/null
+++ b/src/Matrix3_TEST.cc
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Helpers.hh"
+#include "ignition/math/Matrix3.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(Matrix3dTest, Matrix3d)
+{
+ {
+ math::Matrix3d matrix;
+ EXPECT_TRUE(matrix == math::Matrix3d(0, 0, 0, 0, 0, 0, 0, 0, 0));
+ }
+
+ {
+ math::Matrix3d matrix(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ EXPECT_TRUE(matrix == math::Matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9));
+
+ math::Matrix3d matrix1(matrix);
+ EXPECT_TRUE(matrix1 == math::Matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9));
+ }
+
+ math::Matrix3d matrix;
+ matrix.Axes(math::Vector3d(1, 1, 1), math::Vector3d(2, 2, 2),
+ math::Vector3d(3, 3, 3));
+ EXPECT_TRUE(matrix == math::Matrix3d(1, 2, 3, 1, 2, 3, 1, 2, 3));
+
+ matrix.Axis(math::Vector3d(1, 1, 1), IGN_PI);
+ EXPECT_TRUE(matrix == math::Matrix3d(1, 2, 2, 2, 1, 2, 2, 2, 1));
+
+ matrix.Col(0, math::Vector3d(3, 4, 5));
+ EXPECT_TRUE(matrix == math::Matrix3d(3, 2, 2, 4, 1, 2, 5, 2, 1));
+
+ EXPECT_NO_THROW(matrix.Col(3, math::Vector3d(1, 1, 1)));
+ EXPECT_TRUE(matrix == math::Matrix3d(3, 2, 1, 4, 1, 1, 5, 2, 1));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix3dTest, NoIndexException)
+{
+ math::Matrix3d mat = math::Matrix3d::Zero;
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; j < 3; ++j)
+ EXPECT_NO_THROW(mat(i, j));
+
+ EXPECT_NO_THROW(math::equal(mat(3, 0), 0.0));
+ EXPECT_NO_THROW(math::equal(mat(0, 3), 0.0));
+ EXPECT_NO_THROW(math::equal(mat(3, 3), 0.0));
+
+ EXPECT_NO_THROW(mat(3, 0) = 0);
+ EXPECT_NO_THROW(mat(0, 3) = 0);
+ EXPECT_NO_THROW(mat(3, 3) = 0);
+
+ const math::Matrix3d constMat(math::Matrix3d::Zero);
+
+ EXPECT_NO_THROW(math::equal(constMat(3, 0), 0.0));
+ EXPECT_NO_THROW(math::equal(constMat(0, 3), 0.0));
+ EXPECT_NO_THROW(math::equal(constMat(3, 3), 0.0));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix3dTest, OperatorSubtract)
+{
+ math::Matrix3d matZero = math::Matrix3d::Zero;
+ math::Matrix3d matIdent = math::Matrix3d::Identity;
+
+ math::Matrix3d mat = matIdent - matZero;
+ EXPECT_EQ(mat, matIdent);
+
+ math::Matrix3d matA(1, 2, 3,
+ 4, 5, 6,
+ 7, 8, 9);
+
+ math::Matrix3d matB(10, 20, 30,
+ 40, 50, 60,
+ 70, 80, 90);
+
+ mat = matB - matA;
+ EXPECT_EQ(mat, math::Matrix3d(9, 18, 27, 36, 45, 54, 63, 72, 81));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix3dTest, OperatorAdd)
+{
+ math::Matrix3d matZero = math::Matrix3d::Zero;
+ math::Matrix3d matIdent = math::Matrix3d::Identity;
+
+ math::Matrix3d mat = matIdent + matZero;
+ EXPECT_EQ(mat, matIdent);
+
+ math::Matrix3d matA(1, 2, 3,
+ 4, 5, 6,
+ 7, 8, 9);
+
+ math::Matrix3d matB(10, 20, 30,
+ 40, 50, 60,
+ 70, 80, 90);
+
+ mat = matB + matA;
+ EXPECT_EQ(mat, math::Matrix3d(11, 22, 33, 44, 55, 66, 77, 88, 99));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix3dTest, OperatorMul)
+{
+ math::Matrix3d matZero = math::Matrix3d::Zero;
+ math::Matrix3d matIdent = math::Matrix3d::Identity;
+
+ math::Matrix3d mat = matIdent * matZero;
+ EXPECT_EQ(mat, matZero);
+
+ math::Matrix3d matA(1, 2, 3,
+ 4, 5, 6,
+ 7, 8, 9);
+
+ math::Matrix3d matB(10, 20, 30,
+ 40, 50, 60,
+ 70, 80, 90);
+
+ mat = matA * matB;
+ EXPECT_EQ(mat, math::Matrix3d(300, 360, 420,
+ 660, 810, 960,
+ 1020, 1260, 1500));
+
+ mat = matB * matA;
+ EXPECT_EQ(mat, math::Matrix3d(300, 360, 420,
+ 660, 810, 960,
+ 1020, 1260, 1500));
+
+ mat = mat * 2.0;
+ EXPECT_EQ(mat, math::Matrix3d(600, 720, 840,
+ 1320, 1620, 1920,
+ 2040, 2520, 3000));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix3dTest, OperatorStreamOut)
+{
+ math::Matrix3d matA(1, 2, 3,
+ 4, 5, 6,
+ 7, 8, 9);
+
+ std::ostringstream stream;
+ stream << matA;
+ EXPECT_EQ(stream.str(), "1 2 3 4 5 6 7 8 9");
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix3dTest, OperatorStreamIn)
+{
+ math::Matrix3d mat;
+ EXPECT_EQ(mat, math::Matrix3d::Zero);
+
+ std::istringstream stream("1 2 3 4 5 6 7 8 9");
+ stream >> mat;
+ EXPECT_EQ(mat, math::Matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix3dTest, Vector3Multiplication)
+{
+ {
+ // Multiply arbitrary matrix by zeros of different sizes
+ math::Matrix3d matrix(1, 2, 3, 4, 5, 6, 7, 8, 9);
+
+ // Scalar 0
+ EXPECT_EQ(math::Matrix3d::Zero, matrix * 0);
+ EXPECT_EQ(math::Matrix3d::Zero, 0 * matrix);
+
+ // Vector3::Zero
+ EXPECT_EQ(math::Vector3d::Zero, matrix * math::Vector3d::Zero);
+ EXPECT_EQ(math::Vector3d::Zero, math::Vector3d::Zero * matrix);
+ // left multiply with Vector3 not implemented
+
+ // Matrix3::Zero
+ EXPECT_EQ(math::Matrix3d::Zero, matrix * math::Matrix3d::Zero);
+ EXPECT_EQ(math::Matrix3d::Zero, math::Matrix3d::Zero * matrix);
+ }
+
+ {
+ // Multiply arbitrary matrix by identity values
+ math::Matrix3d matrix(1, 2, 3, 4, 5, 6, 7, 8, 9);
+
+ // scalar 1.0
+ EXPECT_EQ(matrix, matrix * 1.0);
+ EXPECT_EQ(matrix, 1.0 * matrix);
+
+ // Vector3::Unit[X|Y|Z]
+ // right multiply
+ EXPECT_EQ(math::Vector3d(matrix(0, 0), matrix(1, 0), matrix(2, 0)),
+ matrix * math::Vector3d::UnitX);
+ EXPECT_EQ(math::Vector3d(matrix(0, 1), matrix(1, 1), matrix(2, 1)),
+ matrix * math::Vector3d::UnitY);
+ EXPECT_EQ(math::Vector3d(matrix(0, 2), matrix(1, 2), matrix(2, 2)),
+ matrix * math::Vector3d::UnitZ);
+ // left multiply
+ EXPECT_EQ(math::Vector3d(matrix(0, 0), matrix(0, 1), matrix(0, 2)),
+ math::Vector3d::UnitX * matrix);
+ EXPECT_EQ(math::Vector3d(matrix(1, 0), matrix(1, 1), matrix(1, 2)),
+ math::Vector3d::UnitY * matrix);
+ EXPECT_EQ(math::Vector3d(matrix(2, 0), matrix(2, 1), matrix(2, 2)),
+ math::Vector3d::UnitZ * matrix);
+
+ // Matrix3::IDENTITY
+ EXPECT_EQ(matrix, matrix * math::Matrix3d::Identity);
+ EXPECT_EQ(matrix, math::Matrix3d::Identity * matrix);
+ }
+
+ {
+ // Multiply arbitrary matrix by itself
+ math::Matrix3d matrix(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ math::Matrix3d matrix2(30, 36, 42,
+ 66, 81, 96,
+ 102, 126, 150);
+
+ EXPECT_EQ(matrix * matrix, matrix2);
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix3dTest, NotEqual)
+{
+ {
+ math::Matrix3d matrix1;
+ math::Matrix3d matrix2;
+ EXPECT_TRUE(matrix1 == matrix2);
+ EXPECT_FALSE(matrix1 != matrix2);
+ }
+
+ {
+ math::Matrix3d matrix1(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ math::Matrix3d matrix2(matrix1);
+
+ EXPECT_FALSE(matrix1 != matrix1);
+
+ matrix2(0, 0) = 1.00001;
+ EXPECT_TRUE(matrix1 != matrix2);
+
+ matrix2(0, 0) = 1.000001;
+ EXPECT_FALSE(matrix1 != matrix2);
+ }
+}
+
+/////////////////////////////////////////////////
+// Test Equal function with specified tolerance
+TEST(Matrix3Test, EqualTolerance)
+{
+ EXPECT_FALSE(math::Matrix3d::Zero.Equal(math::Matrix3d::Identity, 1e-6));
+ EXPECT_FALSE(math::Matrix3d::Zero.Equal(math::Matrix3d::Identity, 1e-3));
+ EXPECT_FALSE(math::Matrix3d::Zero.Equal(math::Matrix3d::Identity, 1e-1));
+ EXPECT_TRUE(math::Matrix3d::Zero.Equal(math::Matrix3d::Identity, 1));
+ EXPECT_TRUE(math::Matrix3d::Zero.Equal(math::Matrix3d::Identity, 1.1));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix3dTest, Inverse)
+{
+ // Inverse of identity matrix is itself
+ EXPECT_EQ(math::Matrix3d::Identity, math::Matrix3d::Identity.Inverse());
+
+ // Matrix multiplied by its inverse results in the identity matrix
+ math::Matrix3d matrix1(-2, 4, 0, 0.1, 9, 55, -7, 1, 26);
+ math::Matrix3d matrix2 = matrix1.Inverse();
+ EXPECT_EQ(matrix1 * matrix2, math::Matrix3d::Identity);
+ EXPECT_EQ(matrix2 * matrix1, math::Matrix3d::Identity);
+
+ // Inverse of inverse results in the same matrix
+ EXPECT_EQ((matrix1.Inverse()).Inverse(), matrix1);
+
+ // Invert multiplication by scalar
+ double scalar = 2.5;
+ EXPECT_EQ((matrix1 * scalar).Inverse(), matrix1.Inverse() * (1.0/scalar));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix3dTest, Determinant)
+{
+ // |Zero matrix| = 0.0
+ EXPECT_DOUBLE_EQ(0.0, math::Matrix3d::Zero.Determinant());
+
+ // |Identity matrix| = 1.0
+ EXPECT_DOUBLE_EQ(1.0, math::Matrix3d::Identity.Determinant());
+
+ // Determinant of arbitrary matrix
+ math::Matrix3d m(-2, 4, 0, 0.1, 9, 55, -7, 1, 26);
+ EXPECT_DOUBLE_EQ(-1908.4, m.Determinant());
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix3dTest, Transpose)
+{
+ // Transpose of zero matrix is itself
+ EXPECT_EQ(math::Matrix3d::Zero, math::Matrix3d::Zero.Transposed());
+
+ // Transpose of identity matrix is itself
+ EXPECT_EQ(math::Matrix3d::Identity, math::Matrix3d::Identity.Transposed());
+
+ // Matrix and expected transpose
+ math::Matrix3d m(-2, 4, 0,
+ 0.1, 9, 55,
+ -7, 1, 26);
+ math::Matrix3d mT(-2, 0.1, -7,
+ 4, 9, 1,
+ 0, 55, 26);
+ EXPECT_NE(m, mT);
+ EXPECT_EQ(m.Transposed(), mT);
+ EXPECT_DOUBLE_EQ(m.Determinant(), m.Transposed().Determinant());
+
+ mT.Transpose();
+ EXPECT_EQ(m, mT);
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix3dTest, From2Axes)
+{
+ math::Vector3d v1(1.0, 0.0, 0.0);
+ math::Vector3d v2(0.0, 1.0, 0.0);
+
+ math::Matrix3d m1;
+ m1.From2Axes(v1, v2);
+
+ math::Matrix3d m2;
+ m2.From2Axes(v2, v1);
+
+ math::Matrix3d m1Correct(0, -1, 0,
+ 1, 0, 0,
+ 0, 0, 1);
+ math::Matrix3d m2Correct(m1Correct);
+ m2Correct.Transpose();
+
+ EXPECT_NE(m1, m2);
+ EXPECT_EQ(m1Correct, m1);
+ EXPECT_EQ(m2Correct, m2);
+ EXPECT_EQ(math::Matrix3d::Identity, m1 * m2);
+ EXPECT_EQ(v2, m1 * v1);
+ EXPECT_EQ(v1, m2 * v2);
+
+ // rotation about 45 degrees
+ v1.Set(1.0, 0.0, 0.0);
+ v2.Set(1.0, 1.0, 0.0);
+ m2.From2Axes(v1, v2);
+ // m1 is 90 degrees rotation
+ EXPECT_EQ(m1, m2*m2);
+
+ // with non-unit vectors
+ v1.Set(0.5, 0.5, 0);
+ v2.Set(-0.5, 0.5, 0);
+
+ m1.From2Axes(v1, v2);
+ m2.From2Axes(v2, v1);
+
+ EXPECT_NE(m1, m2);
+ EXPECT_EQ(m1Correct, m1);
+ EXPECT_EQ(m2Correct, m2);
+ EXPECT_EQ(math::Matrix3d::Identity, m1 * m2);
+ EXPECT_EQ(v2, m1 * v1);
+ EXPECT_EQ(v1, m2 * v2);
+
+ // For zero-length vectors, a unit matrix is returned
+ v1.Set(0, 0, 0);
+ v2.Set(-0.5, 0.5, 0);
+ m1.From2Axes(v1, v2);
+ EXPECT_EQ(math::Matrix3d::Identity, m1);
+
+ // For zero-length vectors, a unit matrix is returned
+ v1.Set(-0.5, 0.5, 0);
+ v2.Set(0, 0, 0);
+ m1.From2Axes(v1, v2);
+ EXPECT_EQ(math::Matrix3d::Identity, m1);
+
+ // Parallel vectors
+ v1.Set(1, 0, 0);
+ v2.Set(2, 0, 0);
+ m1.From2Axes(v1, v2);
+ EXPECT_EQ(math::Matrix3d::Identity, m1);
+
+ // Opposite vectors
+ v1.Set(1, 0, 0);
+ v2.Set(-2, 0, 0);
+ m1.From2Axes(v1, v2);
+ EXPECT_EQ(math::Matrix3d::Zero - math::Matrix3d::Identity, m1);
+}
diff --git a/src/Matrix4_TEST.cc b/src/Matrix4_TEST.cc
new file mode 100644
index 0000000..37ffc05
--- /dev/null
+++ b/src/Matrix4_TEST.cc
@@ -0,0 +1,656 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Pose3.hh"
+#include "ignition/math/Quaternion.hh"
+#include "ignition/math/Matrix4.hh"
+#include "ignition/math/Vector3.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, Construct)
+{
+ math::Matrix4d mat;
+ for (int i = 0; i < 4; ++i)
+ {
+ for (int j = 0; j < 4; ++j)
+ {
+ EXPECT_DOUBLE_EQ(mat(i, i), 0.0);
+ }
+ }
+
+ math::Matrix4d mat2(mat);
+ for (int i = 0; i < 4; ++i)
+ {
+ for (int j = 0; j < 4; ++j)
+ {
+ EXPECT_DOUBLE_EQ(mat2(i, i), 0.0);
+ }
+ }
+ EXPECT_TRUE(mat2 == mat);
+
+
+ // Set individual values.
+ math::Matrix4d mat3(0.0, 1.0, 2.0, 3.0,
+ 4.0, 5.0, 6.0, 7.0,
+ 8.0, 9.0, 10.0, 11.0,
+ 12.0, 13.0, 14.0, 15.0);
+
+ math::Matrix4d mat4;
+ mat4 = mat3;
+ EXPECT_EQ(mat4, mat3);
+
+ EXPECT_DOUBLE_EQ(mat3(0, 0), 0.0);
+ EXPECT_DOUBLE_EQ(mat3(0, 1), 1.0);
+ EXPECT_DOUBLE_EQ(mat3(0, 2), 2.0);
+ EXPECT_DOUBLE_EQ(mat3(0, 3), 3.0);
+ EXPECT_DOUBLE_EQ(mat3(1, 0), 4.0);
+ EXPECT_DOUBLE_EQ(mat3(1, 1), 5.0);
+ EXPECT_DOUBLE_EQ(mat3(1, 2), 6.0);
+ EXPECT_DOUBLE_EQ(mat3(1, 3), 7.0);
+ EXPECT_DOUBLE_EQ(mat3(2, 0), 8.0);
+ EXPECT_DOUBLE_EQ(mat3(2, 1), 9.0);
+ EXPECT_DOUBLE_EQ(mat3(2, 2), 10.0);
+ EXPECT_DOUBLE_EQ(mat3(2, 3), 11.0);
+ EXPECT_DOUBLE_EQ(mat3(3, 0), 12.0);
+ EXPECT_DOUBLE_EQ(mat3(3, 1), 13.0);
+ EXPECT_DOUBLE_EQ(mat3(3, 2), 14.0);
+ EXPECT_DOUBLE_EQ(mat3(3, 3), 15.0);
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, ConstructFromPose3d)
+{
+ {
+ math::Vector3d trans(1, 2, 3);
+ math::Quaterniond qt(0.1, 0.2, 0.3);
+ math::Pose3d pose(trans, qt);
+ math::Matrix4d mat(pose);
+
+ EXPECT_EQ(pose, mat.Pose());
+ EXPECT_EQ(trans, mat.Translation());
+ EXPECT_EQ(qt, mat.Rotation());
+ EXPECT_EQ(pose.Inverse(), mat.Inverse().Pose());
+ }
+
+ // Zero values
+ {
+ math::Vector3d trans(0, 0, 0);
+ math::Quaterniond qt(0, 0, 0);
+ math::Pose3d pose(trans, qt);
+ math::Matrix4d mat(pose);
+
+ EXPECT_EQ(pose, mat.Pose());
+ EXPECT_EQ(trans, mat.Translation());
+ EXPECT_EQ(qt, mat.Rotation());
+ EXPECT_EQ(pose.Inverse(), mat.Inverse().Pose());
+ }
+
+ // Rotate pitch by pi/2 so yaw coincides with roll causing a gimbal lock
+ {
+ math::Vector3d trans(3, 2, 1);
+ math::Quaterniond qt(0, IGN_PI/2, 0);
+ math::Pose3d pose(trans, qt);
+ math::Matrix4d mat(pose);
+
+ EXPECT_EQ(pose, mat.Pose());
+ EXPECT_EQ(trans, mat.Translation());
+ EXPECT_EQ(qt, mat.Rotation());
+ EXPECT_EQ(pose.Inverse(), mat.Inverse().Pose());
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, CoverageExtra)
+{
+ // getting full destructor coverage
+ math::Matrix4d *p = new math::Matrix4d;
+ EXPECT_NE(p, nullptr);
+ delete p;
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, Scale)
+{
+ math::Matrix4d mat, mat2;
+ mat.Scale(math::Vector3d(1, 2, 3));
+ mat2.Scale(1, 2, 3);
+
+ EXPECT_EQ(mat, mat2);
+
+ EXPECT_DOUBLE_EQ(mat(0, 0), 1.0);
+ EXPECT_DOUBLE_EQ(mat(1, 1), 2.0);
+ EXPECT_DOUBLE_EQ(mat(2, 2), 3.0);
+ EXPECT_DOUBLE_EQ(mat(3, 3), 1.0);
+
+ EXPECT_DOUBLE_EQ(mat2(0, 0), 1.0);
+ EXPECT_DOUBLE_EQ(mat2(1, 1), 2.0);
+ EXPECT_DOUBLE_EQ(mat2(2, 2), 3.0);
+ EXPECT_DOUBLE_EQ(mat2(3, 3), 1.0);
+
+ EXPECT_EQ(mat.Scale(), mat2.Scale());
+ EXPECT_EQ(mat.Scale(), math::Vector3d(1, 2, 3));
+
+ for (int i = 0; i < 4; ++i)
+ {
+ for (int j = 0; j < 4; ++j)
+ {
+ if (i != j)
+ {
+ EXPECT_DOUBLE_EQ(mat(i, j), 0.0);
+ EXPECT_DOUBLE_EQ(mat2(i, j), 0.0);
+ }
+ else if (i == 3 && j == 3)
+ {
+ EXPECT_DOUBLE_EQ(mat(i, j), 1.0);
+ EXPECT_DOUBLE_EQ(mat2(i, j), 1.0);
+ }
+ }
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, MultiplyV)
+{
+ math::Matrix4d mat;
+ math::Vector3d vec(-1.2, 2.3, 10.5);
+
+ for (int i = 0; i < 4; ++i)
+ {
+ for (int j = 0; j < 4; ++j)
+ {
+ mat(i, j) = i-j;
+ }
+ }
+
+ EXPECT_EQ(mat * vec, math::Vector3d(-26.3, -13.7, -1.1));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, Multiply4)
+{
+ math::Matrix4d mat, mat1;
+
+ for (int i = 0; i < 4; ++i)
+ {
+ for (int j = 0; j < 4; ++j)
+ {
+ mat(i, j) = i-j;
+ mat1(j, i) = i+j;
+ }
+ }
+
+ math::Matrix4d mat3(
+ -14, -20, -26, -32,
+ -8, -10, -12, -14,
+ -2, 0, 2, 4,
+ 4, 10, 16, 22);
+
+ math::Matrix4d mat2 = mat * mat1;
+ EXPECT_EQ(mat2, mat3);
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, Inverse)
+{
+ math::Matrix4d mat(2, 3, 1, 5,
+ 1, 0, 3, 1,
+ 0, 2, -3, 2,
+ 0, 2, 3, 1);
+
+ math::Matrix4d mat1 = mat.Inverse();
+ EXPECT_EQ(mat1, math::Matrix4d(18, -35, -28, 1,
+ 9, -18, -14, 1,
+ -2, 4, 3, 0,
+ -12, 24, 19, -1));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, GetAsPose3d)
+{
+ math::Matrix4d mat(2, 3, 1, 5,
+ 1, 0, 3, 1,
+ 0, 2, -3, 2,
+ 0, 2, 3, 1);
+ math::Pose3d pose = mat.Pose();
+
+ EXPECT_EQ(pose,
+ math::Pose3d(5, 1, 2, -0.204124, 1.22474, 0.816497, 0.204124));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, Translate)
+{
+ math::Matrix4d mat, mat2;
+ mat.Translate(math::Vector3d(1, 2, 3));
+ mat2.Translate(1, 2, 3);
+
+ EXPECT_EQ(mat, mat2);
+
+ EXPECT_DOUBLE_EQ(mat(0, 3), 1.0);
+ EXPECT_DOUBLE_EQ(mat(1, 3), 2.0);
+ EXPECT_DOUBLE_EQ(mat(2, 3), 3.0);
+
+ EXPECT_DOUBLE_EQ(mat2(0, 3), 1.0);
+ EXPECT_DOUBLE_EQ(mat2(1, 3), 2.0);
+ EXPECT_DOUBLE_EQ(mat2(2, 3), 3.0);
+
+ EXPECT_EQ(mat.Translation(), mat2.Translation());
+ EXPECT_EQ(mat.Translation(), math::Vector3d(1, 2, 3));
+
+ for (int i = 0; i < 4; ++i)
+ {
+ for (int j = 0; j < 2; ++j)
+ {
+ EXPECT_DOUBLE_EQ(mat(i, j), 0.0);
+ EXPECT_DOUBLE_EQ(mat2(i, j), 0.0);
+ }
+ }
+ EXPECT_DOUBLE_EQ(mat(3, 3), 0.0);
+ EXPECT_DOUBLE_EQ(mat2(3, 3), 0.0);
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, RotationDiagZero)
+{
+ math::Matrix4d mat;
+ mat(0, 0) = 0;
+ mat(0, 1) = 0.2;
+ mat(0, 2) = 0.3;
+ mat(0, 3) = 0.4;
+
+ mat(1, 0) = 0.5;
+ mat(1, 1) = 0;
+ mat(1, 2) = 0.7;
+ mat(1, 3) = 0.8;
+
+ mat(2, 0) = 0.9;
+ mat(2, 1) = 1.0;
+ mat(2, 2) = 0;
+ mat(2, 3) = 1.2;
+
+ mat(3, 0) = 1.3;
+ mat(3, 1) = 1.4;
+ mat(3, 2) = 1.5;
+ mat(3, 3) = 1.0;
+
+ math::Quaterniond quat = mat.Rotation();
+ EXPECT_NEAR(quat.X(), 0.5, 1e-6);
+ EXPECT_NEAR(quat.Y(), 0.35, 1e-6);
+ EXPECT_NEAR(quat.Z(), 0.6, 1e-6);
+ EXPECT_NEAR(quat.W(), 0.15, 1e-6);
+
+ math::Vector3d euler = mat.EulerRotation(true);
+ EXPECT_EQ(euler, math::Vector3d(1.5708, -1.11977, 1.5708));
+
+ euler = mat.EulerRotation(false);
+ EXPECT_EQ(euler, math::Vector3d(-1.5708, 4.26136, -1.5708));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, RotationDiagLessThanZero)
+{
+ math::Matrix4d mat;
+ mat(0, 0) = -0.1;
+ mat(0, 1) = 0.2;
+ mat(0, 2) = 0.3;
+ mat(0, 3) = 0.4;
+
+ mat(1, 0) = 0.5;
+ mat(1, 1) = 0;
+ mat(1, 2) = 0.7;
+ mat(1, 3) = 0.8;
+
+ mat(2, 0) = 0.9;
+ mat(2, 1) = 1.0;
+ mat(2, 2) = 0;
+ mat(2, 3) = 1.2;
+
+ mat(3, 0) = 1.3;
+ mat(3, 1) = 1.4;
+ mat(3, 2) = 1.5;
+ mat(3, 3) = 1.0;
+
+ {
+ math::Quaterniond quat = mat.Rotation();
+ EXPECT_NEAR(quat.X(), 0.333712, 1e-6);
+ EXPECT_NEAR(quat.Y(), 0.524404, 1e-6);
+ EXPECT_NEAR(quat.Z(), 0.810443, 1e-6);
+ EXPECT_NEAR(quat.W(), -0.286039, 1e-6);
+
+ math::Vector3d euler = mat.EulerRotation(true);
+ EXPECT_EQ(euler, math::Vector3d(1.5708, -1.11977, 1.76819));
+
+ euler = mat.EulerRotation(false);
+ EXPECT_EQ(euler, math::Vector3d(-1.5708, 4.26136, -1.3734));
+ }
+
+
+ {
+ mat(0, 0) = -0.1;
+ mat(1, 1) = -0.2;
+ mat(2, 2) = 0.0;
+
+ math::Quaterniond quat = mat.Rotation();
+ EXPECT_NEAR(quat.X(), 0.526235, 1e-6);
+ EXPECT_NEAR(quat.Y(), 0.745499, 1e-6);
+ EXPECT_NEAR(quat.Z(), 0.570088, 1e-6);
+ EXPECT_NEAR(quat.W(), 0.131559, 1e-6);
+
+ math::Vector3d euler = mat.EulerRotation(true);
+ EXPECT_EQ(euler, math::Vector3d(1.5708, -1.11977, 1.76819));
+
+ euler = mat.EulerRotation(false);
+ EXPECT_EQ(euler, math::Vector3d(-1.5708, 4.26136, -1.3734));
+ }
+}
+
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, Rotation)
+{
+ math::Matrix4d mat;
+ mat(0, 0) = 0.1;
+ mat(0, 1) = 0.2;
+ mat(0, 2) = 0.3;
+ mat(0, 3) = 0.4;
+
+ mat(1, 0) = 0.5;
+ mat(1, 1) = 0.6;
+ mat(1, 2) = 0.7;
+ mat(1, 3) = 0.8;
+
+ mat(2, 0) = 0.9;
+ mat(2, 1) = 1.0;
+ mat(2, 2) = 1.1;
+ mat(2, 3) = 1.2;
+
+ mat(3, 0) = 1.3;
+ mat(3, 1) = 1.4;
+ mat(3, 2) = 1.5;
+ mat(3, 3) = 1.6;
+
+ math::Quaterniond quat = mat.Rotation();
+ EXPECT_NEAR(quat.X(), 0.0896421, 1e-6);
+ EXPECT_NEAR(quat.Y(), -0.179284, 1e-6);
+ EXPECT_NEAR(quat.Z(), 0.0896421, 1e-6);
+ EXPECT_NEAR(quat.W(), 0.83666, 1e-6);
+
+ math::Vector3d euler = mat.EulerRotation(true);
+ EXPECT_EQ(euler, math::Vector3d(0.737815, -1.11977, 1.3734));
+
+ euler = mat.EulerRotation(false);
+ EXPECT_EQ(euler, math::Vector3d(-2.40378, 4.26136, -1.76819));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, EulerRotation2)
+{
+ math::Matrix4d mat;
+ mat(0, 0) = 0.1;
+ mat(0, 1) = 0.2;
+ mat(0, 2) = 0.3;
+ mat(0, 3) = 0.4;
+
+ mat(1, 0) = 0.5;
+ mat(1, 1) = 0.6;
+ mat(1, 2) = 0.7;
+ mat(1, 3) = 0.8;
+
+ mat(2, 0) = 1.9;
+ mat(2, 1) = 1.2;
+ mat(2, 2) = 1.1;
+ mat(2, 3) = 1.2;
+
+ mat(3, 0) = 1.3;
+ mat(3, 1) = 1.4;
+ mat(3, 2) = 1.5;
+ mat(3, 3) = 1.6;
+
+ math::Vector3d euler = mat.EulerRotation(true);
+ EXPECT_EQ(euler, math::Vector3d(-2.55359, -1.5708, 0));
+
+ euler = mat.EulerRotation(false);
+ EXPECT_EQ(euler, math::Vector3d(-2.55359, -1.5708, 0));
+
+ mat(2, 0) = -1.2;
+ euler = mat.EulerRotation(true);
+ EXPECT_EQ(euler, math::Vector3d(0.588003, 1.5708, 0));
+
+ euler = mat.EulerRotation(false);
+ EXPECT_EQ(euler, math::Vector3d(0.588003, 1.5708, 0));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, AffineTransform)
+{
+ math::Matrix4d mat = math::Matrix4d::Zero;
+ math::Vector3d vec(1, 2, 3);
+
+ math::Vector3d v;
+ EXPECT_NO_THROW(mat.TransformAffine(vec, v));
+ EXPECT_FALSE(mat.TransformAffine(vec, v));
+
+ mat = math::Matrix4d::Identity;
+ EXPECT_NO_THROW(mat.TransformAffine(vec, v));
+ EXPECT_TRUE(mat.TransformAffine(vec, v));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, NoIndexException)
+{
+ math::Matrix4d mat = math::Matrix4d::Zero;
+ for (int i = 0; i < 4; ++i)
+ for (int j = 0; j < 4; ++j)
+ EXPECT_NO_THROW(mat(i, j));
+
+ EXPECT_NO_THROW(math::equal(mat(4, 0), 0.0));
+ EXPECT_NO_THROW(math::equal(mat(0, 4), 0.0));
+ EXPECT_NO_THROW(math::equal(mat(4, 4), 0.0));
+
+ EXPECT_NO_THROW(mat(4, 0) = 0);
+ EXPECT_NO_THROW(mat(0, 4) = 0);
+ EXPECT_NO_THROW(mat(4, 4) = 0);
+
+ const math::Matrix4d constMat(math::Matrix4d::Zero);
+
+ EXPECT_NO_THROW(math::equal(constMat(4, 0), 0.0));
+ EXPECT_NO_THROW(math::equal(constMat(0, 4), 0.0));
+ EXPECT_NO_THROW(math::equal(constMat(4, 4), 0.0));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, OperatorStreamOut)
+{
+ math::Matrix4d matA(1, 2, 3, 4,
+ 5, 6, 7, 8,
+ 9, 10, 11, 12,
+ 13, 14, 15, 16);
+
+ std::ostringstream stream;
+ stream << matA;
+ EXPECT_EQ(stream.str(), "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16");
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, OperatorStreamIn)
+{
+ math::Matrix4d mat;
+ EXPECT_EQ(mat, math::Matrix4d::Zero);
+
+ std::istringstream stream("1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16");
+ stream >> mat;
+ EXPECT_EQ(mat, math::Matrix4d(1, 2, 3, 4,
+ 5, 6, 7, 8,
+ 9, 10, 11, 12,
+ 13, 14, 15, 16));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, NotEqual)
+{
+ {
+ math::Matrix4d matrix1;
+ math::Matrix4d matrix2;
+ EXPECT_TRUE(matrix1 == matrix2);
+ EXPECT_FALSE(matrix1 != matrix2);
+ }
+
+ {
+ math::Matrix4d matrix1(1, 2, 3, 4,
+ 5, 6, 7, 8,
+ 9, 10, 11, 12,
+ 13, 14, 15, 16);
+ math::Matrix4d matrix2(matrix1);
+
+ EXPECT_FALSE(matrix1 != matrix1);
+
+ matrix2(0, 0) = 1.00001;
+ EXPECT_TRUE(matrix1 != matrix2);
+
+ matrix2(0, 0) = 1.000001;
+ EXPECT_FALSE(matrix1 != matrix2);
+ }
+}
+
+/////////////////////////////////////////////////
+// Test Equal function with specified tolerance
+TEST(Matrix4Test, EqualTolerance)
+{
+ EXPECT_FALSE(math::Matrix4d::Zero.Equal(math::Matrix4d::Identity, 1e-6));
+ EXPECT_FALSE(math::Matrix4d::Zero.Equal(math::Matrix4d::Identity, 1e-3));
+ EXPECT_FALSE(math::Matrix4d::Zero.Equal(math::Matrix4d::Identity, 1e-1));
+ EXPECT_TRUE(math::Matrix4d::Zero.Equal(math::Matrix4d::Identity, 1));
+ EXPECT_TRUE(math::Matrix4d::Zero.Equal(math::Matrix4d::Identity, 1.1));
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, Determinant)
+{
+ // |Zero matrix| = 0.0
+ EXPECT_DOUBLE_EQ(0.0, math::Matrix4d::Zero.Determinant());
+
+ // |Identity matrix| = 1.0
+ EXPECT_DOUBLE_EQ(1.0, math::Matrix4d::Identity.Determinant());
+
+ // Determinant of arbitrary matrix
+ math::Matrix4d m(2, 3, 0.1, -5, 1, 0, 3.2, 1,
+ 0, 2, -3, 2.1, 0, 2, 3.2, 1);
+ EXPECT_DOUBLE_EQ(129.82, m.Determinant());
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, Transpose)
+{
+ // Transpose of zero matrix is itself
+ EXPECT_EQ(math::Matrix4d::Zero, math::Matrix4d::Zero.Transposed());
+
+ // Transpose of identity matrix is itself
+ EXPECT_EQ(math::Matrix4d::Identity, math::Matrix4d::Identity.Transposed());
+
+ // Matrix and expected transpose
+ math::Matrix4d m(-2, 4, 0, -3.5,
+ 0.1, 9, 55, 1.2,
+ -7, 1, 26, 11.5,
+ .2, 3, -5, -0.1);
+ math::Matrix4d mT(-2, 0.1, -7, .2,
+ 4, 9, 1, 3,
+ 0, 55, 26, -5,
+ -3.5, 1.2, 11.5, -0.1);
+ EXPECT_NE(m, mT);
+ EXPECT_EQ(m.Transposed(), mT);
+ EXPECT_DOUBLE_EQ(m.Determinant(), m.Transposed().Determinant());
+
+ mT.Transpose();
+ EXPECT_EQ(m, mT);
+}
+
+/////////////////////////////////////////////////
+TEST(Matrix4dTest, LookAt)
+{
+ EXPECT_EQ(math::Matrix4d::LookAt(-math::Vector3d::UnitX,
+ math::Vector3d::Zero).Pose(),
+ math::Pose3d(-1, 0, 0, 0, 0, 0));
+
+ EXPECT_EQ(math::Matrix4d::LookAt(math::Vector3d(3, 2, 0),
+ math::Vector3d(0, 2, 0)).Pose(),
+ math::Pose3d(3, 2, 0, 0, 0, IGN_PI));
+
+ EXPECT_EQ(math::Matrix4d::LookAt(math::Vector3d(1, 6, 1),
+ math::Vector3d::One).Pose(),
+ math::Pose3d(1, 6, 1, 0, 0, -IGN_PI_2));
+
+ EXPECT_EQ(math::Matrix4d::LookAt(math::Vector3d(-1, -1, 0),
+ math::Vector3d(1, 1, 0)).Pose(),
+ math::Pose3d(-1, -1, 0, 0, 0, IGN_PI_4));
+
+ // Default up is Z
+ EXPECT_EQ(math::Matrix4d::LookAt(math::Vector3d(0.1, -5, 222),
+ math::Vector3d(999, -0.6, 0)),
+ math::Matrix4d::LookAt(math::Vector3d(0.1, -5, 222),
+ math::Vector3d(999, -0.6, 0),
+ math::Vector3d::UnitZ));
+
+ // up == zero, default up = +Z
+ EXPECT_EQ(math::Matrix4d::LookAt(math::Vector3d(1.23, 456, 0.7),
+ math::Vector3d(0, 8.9, -10),
+ math::Vector3d::Zero),
+ math::Matrix4d::LookAt(math::Vector3d(1.23, 456, 0.7),
+ math::Vector3d(0, 8.9, -10)));
+
+ // up == +X, default up = +Z
+ EXPECT_EQ(math::Matrix4d::LookAt(math::Vector3d(0.25, 9, -5),
+ math::Vector3d(-6, 0, 0.4),
+ math::Vector3d::UnitX),
+ math::Matrix4d::LookAt(math::Vector3d(0.25, 9, -5),
+ math::Vector3d(-6, 0, 0.4)));
+
+ // up == -X, default up = +Z
+ EXPECT_EQ(math::Matrix4d::LookAt(math::Vector3d(0, 0, 0.2),
+ math::Vector3d(-8, 0, -6),
+ -math::Vector3d::UnitX),
+ math::Matrix4d::LookAt(math::Vector3d(0, 0, 0.2),
+ math::Vector3d(-8, 0, -6)));
+
+ // eye == target, default direction = +X
+ EXPECT_EQ(math::Matrix4d::LookAt(math::Vector3d::One,
+ math::Vector3d::One),
+ math::Matrix4d::LookAt(math::Vector3d::One,
+ math::Vector3d(1.0001, 1, 1)));
+
+ // Not possible to keep _up on +Z
+ EXPECT_EQ(math::Matrix4d::LookAt(math::Vector3d(-1, 0, 10),
+ math::Vector3d(-1, 0, 0)),
+ math::Matrix4d::LookAt(math::Vector3d(-1, 0, 10),
+ math::Vector3d(-1, 0, 0),
+ -math::Vector3d::UnitX));
+
+ // Different ups
+ EXPECT_EQ(math::Matrix4d::LookAt(math::Vector3d::One,
+ math::Vector3d(0, 1, 1),
+ math::Vector3d::UnitY).Pose(),
+ math::Pose3d(1, 1, 1, IGN_PI_2, 0, IGN_PI));
+
+ EXPECT_EQ(math::Matrix4d::LookAt(math::Vector3d::One,
+ math::Vector3d(0, 1, 1),
+ math::Vector3d(0, 1, 1)).Pose(),
+ math::Pose3d(1, 1, 1, IGN_PI_4, 0, IGN_PI));
+}
+
diff --git a/src/OrientedBox_TEST.cc b/src/OrientedBox_TEST.cc
new file mode 100644
index 0000000..f89c878
--- /dev/null
+++ b/src/OrientedBox_TEST.cc
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+#include <cmath>
+
+#include "ignition/math/Angle.hh"
+#include "ignition/math/OrientedBox.hh"
+
+using namespace ignition;
+using namespace math;
+
+auto g_tolerance = 1e-6;
+
+/////////////////////////////////////////////////
+TEST(OrientedBoxTest, EmptyConstructorNew)
+{
+ OrientedBoxd *box = nullptr;
+
+ {
+ box = new OrientedBoxd;
+ EXPECT_TRUE(box != nullptr);
+ }
+
+ EXPECT_TRUE(box->Size() == Vector3d::Zero);
+ EXPECT_TRUE(box->Pose() == Pose3d::Zero);
+
+ {
+ delete box;
+ box = nullptr;
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(OrientedBoxTest, EmptyConstructor)
+{
+ OrientedBoxd box;
+ EXPECT_TRUE(box.Size() == Vector3d::Zero);
+ EXPECT_TRUE(box.Pose() == Pose3d::Zero);
+}
+
+/////////////////////////////////////////////////
+TEST(OrientedBoxTest, SizeOnlyConstructor)
+{
+ OrientedBoxd box(Vector3d(1, 2, 3));
+ EXPECT_EQ(box.Size(), Vector3d(1, 2, 3));
+ EXPECT_EQ(box.Pose(), Pose3d::Zero);
+}
+
+/////////////////////////////////////////////////
+TEST(OrientedBoxTest, NegativeSizeConstructor)
+{
+ OrientedBoxd box(Vector3d(-1, 0, -3));
+ EXPECT_EQ(box.Size(), Vector3d(1, 0, 3));
+}
+
+/////////////////////////////////////////////////
+TEST(OrientedBoxTest, SizePoseConstructor)
+{
+ OrientedBoxi box(Vector3i(1, 2, 3), Pose3i(-1, -2, -3, 0, 1, 2));
+ EXPECT_EQ(box.Size(), Vector3i(1, 2, 3));
+ EXPECT_EQ(box.Pose(), Pose3i(-1, -2, -3, 0, 1, 2));
+}
+
+/////////////////////////////////////////////////
+TEST(OrientedBoxTest, CopyConstructor)
+{
+ OrientedBoxf box1(Vector3f(0.1f, 0.2f, 0.3f),
+ Pose3f(-0.1f, -0.2f, 0.0f, 1.1f, 1.2f, 1.3f));
+ OrientedBoxf box2(box1);
+
+ EXPECT_EQ(box2.Size(), Vector3f(0.1f, 0.2f, 0.3f));
+ EXPECT_EQ(box2.Pose(), Pose3f(-0.1f, -0.2f, 0.0f, 1.1f, 1.2f, 1.3f));
+}
+
+/////////////////////////////////////////////////
+TEST(OrientedBoxTest, Length)
+{
+ OrientedBoxd box(Vector3d(0.1, -2.1, 0.0));
+ EXPECT_DOUBLE_EQ(box.XLength(), 0.1);
+ EXPECT_DOUBLE_EQ(box.YLength(), 2.1);
+ EXPECT_DOUBLE_EQ(box.ZLength(), 0.0);
+}
+
+/////////////////////////////////////////////////
+TEST(OrientedBoxTest, OperatorEqual)
+{
+ OrientedBoxd box = OrientedBoxd(Vector3d(1, 1, 1));
+ OrientedBoxd box2 = OrientedBoxd(Vector3d(1, 1, 1),
+ Pose3d(1, 2, 3, 4, 5, 6));
+ OrientedBoxd box3 = OrientedBoxd(Vector3d(0, 0, 0),
+ Pose3d(1, 2, 3, 4, 5, 6));
+ EXPECT_TRUE(box == OrientedBoxd(Vector3d(1, 1, 1)));
+ EXPECT_FALSE(box == box2);
+ EXPECT_FALSE(box3 == box);
+}
+
+/////////////////////////////////////////////////
+TEST(OrientedBoxTest, ContainsZeroBox)
+{
+ OrientedBoxd box;
+
+ EXPECT_TRUE(box.Contains(Vector3d(0, 0, 0)));
+ EXPECT_FALSE(box.Contains(Vector3d(0, 0, 0.0001)));
+}
+
+/////////////////////////////////////////////////
+TEST(OrientedBoxTest, ContainsZeroPose)
+{
+ OrientedBoxd box(Vector3d(1, 2, 3));
+
+ // Vertices
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5, -1.0, -1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5, -1.0, +1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5, +1.0, -1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5, +1.0, +1.5)));
+
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5, -1.0, -1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5, -1.0, +1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5, +1.0, -1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5, +1.0, +1.5)));
+
+ // Edges
+ EXPECT_TRUE(box.Contains(Vector3d(0.0, -1.0, -1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(0.0, -1.0, +1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(0.0, +1.0, -1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(0.0, +1.0, +1.5)));
+
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5, -1.0, 0.0)));
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5, +1.0, 0.0)));
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5, -1.0, 0.0)));
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5, +1.0, 0.0)));
+
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5, 0.0, -1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5, 0.0, +1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5, 0.0, -1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5, 0.0, +1.5)));
+
+ // Inside
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5+g_tolerance,
+ -1.0+g_tolerance,
+ -1.5+g_tolerance)));
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5+g_tolerance,
+ -1.0+g_tolerance,
+ +1.5-g_tolerance)));
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5+g_tolerance,
+ +1.0-g_tolerance,
+ -1.5+g_tolerance)));
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5+g_tolerance,
+ +1.0-g_tolerance,
+ +1.5-g_tolerance)));
+
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5-g_tolerance,
+ -1.0+g_tolerance,
+ -1.5+g_tolerance)));
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5-g_tolerance,
+ -1.0+g_tolerance,
+ +1.5-g_tolerance)));
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5-g_tolerance,
+ +1.0-g_tolerance,
+ -1.5+g_tolerance)));
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5-g_tolerance,
+ +1.0-g_tolerance,
+ +1.5-g_tolerance)));
+
+ // Outside
+ EXPECT_FALSE(box.Contains(Vector3d(-0.5-g_tolerance,
+ -1.0-g_tolerance,
+ -1.5-g_tolerance)));
+ EXPECT_FALSE(box.Contains(Vector3d(-0.5-g_tolerance,
+ -1.0-g_tolerance,
+ +1.5+g_tolerance)));
+ EXPECT_FALSE(box.Contains(Vector3d(-0.5-g_tolerance,
+ +1.0+g_tolerance,
+ -1.5-g_tolerance)));
+ EXPECT_FALSE(box.Contains(Vector3d(-0.5-g_tolerance,
+ +1.0+g_tolerance,
+ +1.5+g_tolerance)));
+
+ EXPECT_FALSE(box.Contains(Vector3d(+0.5-g_tolerance,
+ -1.0-g_tolerance,
+ -1.5-g_tolerance)));
+ EXPECT_FALSE(box.Contains(Vector3d(+0.5-g_tolerance,
+ -1.0-g_tolerance,
+ +1.5+g_tolerance)));
+ EXPECT_FALSE(box.Contains(Vector3d(+0.5-g_tolerance,
+ +1.0+g_tolerance,
+ -1.5-g_tolerance)));
+ EXPECT_FALSE(box.Contains(Vector3d(+0.5-g_tolerance,
+ +1.0+g_tolerance,
+ +1.5+g_tolerance)));
+}
+
+/////////////////////////////////////////////////
+TEST(OrientedBoxTest, ContainsOrientedPosition)
+{
+ OrientedBoxd box(Vector3d(1, 2, 3),
+ Pose3d(10, 20, 30, 0, 0, 0));
+
+ // Vertices
+ EXPECT_TRUE(box.Contains(Vector3d(10-0.5, 20-1.0, 30-1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(10-0.5, 20-1.0, 30+1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(10-0.5, 20+1.0, 30-1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(10-0.5, 20+1.0, 30+1.5)));
+
+ EXPECT_TRUE(box.Contains(Vector3d(10+0.5, 20-1.0, 30-1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(10+0.5, 20-1.0, 30+1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(10+0.5, 20+1.0, 30-1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(10+0.5, 20+1.0, 30+1.5)));
+
+ // Edges
+ EXPECT_TRUE(box.Contains(Vector3d(10.0, 20-1.0, 30-1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(10.0, 20-1.0, 30+1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(10.0, 20+1.0, 30-1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(10.0, 20+1.0, 30+1.5)));
+
+ EXPECT_TRUE(box.Contains(Vector3d(10-0.5, 20-1.0, 30.0)));
+ EXPECT_TRUE(box.Contains(Vector3d(10-0.5, 20+1.0, 30.0)));
+ EXPECT_TRUE(box.Contains(Vector3d(10+0.5, 20-1.0, 30.0)));
+ EXPECT_TRUE(box.Contains(Vector3d(10+0.5, 20+1.0, 30.0)));
+
+ EXPECT_TRUE(box.Contains(Vector3d(10-0.5, 20.0, 30-1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(10-0.5, 20.0, 30+1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(10+0.5, 20.0, 30-1.5)));
+ EXPECT_TRUE(box.Contains(Vector3d(10+0.5, 20.0, 30+1.5)));
+
+ // Inside
+ EXPECT_TRUE(box.Contains(Vector3d(10, 20, 30)));
+ EXPECT_TRUE(box.Contains(Vector3d(10-0.25, 20-0.5, 30-0.75)));
+ EXPECT_TRUE(box.Contains(Vector3d(10+0.25, 20+0.5, 30+0.75)));
+
+ // Outside
+ EXPECT_FALSE(box.Contains(Vector3d(10-1.0, 20-1.0, 30-1.5)));
+ EXPECT_FALSE(box.Contains(Vector3d(10-0.5, 20-2.0, 30-1.5)));
+ EXPECT_FALSE(box.Contains(Vector3d(10-0.5, 20-1.0, 30-2.0)));
+
+ EXPECT_FALSE(box.Contains(Vector3d(10+1.0, 20+1.0, 30+1.5)));
+ EXPECT_FALSE(box.Contains(Vector3d(10+0.5, 20+2.0, 30+1.5)));
+ EXPECT_FALSE(box.Contains(Vector3d(10+0.5, 20+1.0, 30+2.0)));
+}
+
+/////////////////////////////////////////////////
+TEST(OrientedBoxTest, ContainsOrientedRotation)
+{
+ // Rotate PI/2 about +x: swap Z and Y
+ OrientedBoxd box(Vector3d(1, 2, 3), Pose3d(0, 0, 0, IGN_PI*0.5, 0, 0));
+
+ // Doesn't contain non-rotated vertices
+ EXPECT_FALSE(box.Contains(Vector3d(-0.5, -1.0, -1.5)));
+ EXPECT_FALSE(box.Contains(Vector3d(-0.5, -1.0, +1.5)));
+ EXPECT_FALSE(box.Contains(Vector3d(-0.5, +1.0, -1.5)));
+ EXPECT_FALSE(box.Contains(Vector3d(-0.5, +1.0, +1.5)));
+
+ EXPECT_FALSE(box.Contains(Vector3d(+0.5, -1.0, -1.5)));
+ EXPECT_FALSE(box.Contains(Vector3d(+0.5, -1.0, +1.5)));
+ EXPECT_FALSE(box.Contains(Vector3d(+0.5, +1.0, -1.5)));
+ EXPECT_FALSE(box.Contains(Vector3d(+0.5, +1.0, +1.5)));
+
+ // Inside
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5+g_tolerance,
+ -1.5+g_tolerance,
+ -1.0+g_tolerance)));
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5+g_tolerance,
+ -1.5+g_tolerance,
+ +1.0-g_tolerance)));
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5+g_tolerance,
+ +1.5-g_tolerance,
+ -1.0+g_tolerance)));
+ EXPECT_TRUE(box.Contains(Vector3d(-0.5+g_tolerance,
+ +1.5-g_tolerance,
+ +1.0-g_tolerance)));
+
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5-g_tolerance,
+ -1.5+g_tolerance,
+ -1.0+g_tolerance)));
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5-g_tolerance,
+ -1.5+g_tolerance,
+ +1.0-g_tolerance)));
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5-g_tolerance,
+ +1.5-g_tolerance, -1.0+g_tolerance)));
+ EXPECT_TRUE(box.Contains(Vector3d(+0.5-g_tolerance,
+ +1.5-g_tolerance,
+ +1.0-g_tolerance)));
+
+ // Outside
+ EXPECT_FALSE(box.Contains(Vector3d(-0.5-g_tolerance,
+ -1.5-g_tolerance,
+ -1.0-g_tolerance)));
+ EXPECT_FALSE(box.Contains(Vector3d(-0.5-g_tolerance,
+ -1.5-g_tolerance,
+ +1.0+g_tolerance)));
+ EXPECT_FALSE(box.Contains(Vector3d(-0.5-g_tolerance,
+ +1.5+g_tolerance,
+ -1.0-g_tolerance)));
+ EXPECT_FALSE(box.Contains(Vector3d(-0.5-g_tolerance,
+ +1.5+g_tolerance,
+ +1.0+g_tolerance)));
+
+ EXPECT_FALSE(box.Contains(Vector3d(+0.5-g_tolerance,
+ -1.5-g_tolerance,
+ -1.0-g_tolerance)));
+ EXPECT_FALSE(box.Contains(Vector3d(+0.5-g_tolerance,
+ -1.5-g_tolerance,
+ +1.0+g_tolerance)));
+ EXPECT_FALSE(box.Contains(Vector3d(+0.5-g_tolerance,
+ +1.5+g_tolerance,
+ -1.0-g_tolerance)));
+ EXPECT_FALSE(box.Contains(Vector3d(+0.5-g_tolerance,
+ +1.5+g_tolerance,
+ +1.0+g_tolerance)));
+}
+
+/////////////////////////////////////////////////
+TEST(OrientedBoxTest, OperatorStreamOut)
+{
+ OrientedBoxd b(Vector3d(0.1, 1.2, 2.3),
+ Pose3d(3.4, 4.5, 5.6, 0.0, -0.1, 0.2));
+ std::ostringstream stream;
+ stream << b;
+ EXPECT_EQ(stream.str(), "Size[0.1 1.2 2.3] Pose[3.4 4.5 5.6 0 -0.1 0.2]");
+}
+
diff --git a/src/PID.cc b/src/PID.cc
new file mode 100644
index 0000000..c1fad87
--- /dev/null
+++ b/src/PID.cc
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2016 Open Source Robotics Foundation
+ *
+ * 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 <chrono>
+#include <cmath>
+#include "ignition/math/Helpers.hh"
+#include "ignition/math/PID.hh"
+
+using namespace ignition;
+using namespace math;
+
+/////////////////////////////////////////////////
+PID::PID(const double _p, const double _i, const double _d,
+ const double _imax, const double _imin, const double _cmdMax,
+ const double _cmdMin, const double _cmdOffset)
+: pGain(_p), iGain(_i), dGain(_d), iMax(_imax), iMin(_imin),
+ cmdMax(_cmdMax), cmdMin(_cmdMin), cmdOffset(_cmdOffset)
+{
+ this->Reset();
+}
+
+/////////////////////////////////////////////////
+void PID::Init(const double _p, const double _i, const double _d,
+ const double _imax, const double _imin, const double _cmdMax,
+ const double _cmdMin, const double _cmdOffset)
+{
+ this->pGain = _p;
+ this->iGain = _i;
+ this->dGain = _d;
+ this->iMax = _imax;
+ this->iMin = _imin;
+ this->cmdMax = _cmdMax;
+ this->cmdMin = _cmdMin;
+ this->cmdOffset = _cmdOffset;
+
+ this->Reset();
+}
+
+/////////////////////////////////////////////////
+PID &PID::operator=(const PID &_p)
+{
+ if (this == &_p)
+ return *this;
+
+ this->pGain = _p.pGain;
+ this->iGain = _p.iGain;
+ this->dGain = _p.dGain;
+ this->iMax = _p.iMax;
+ this->iMin = _p.iMin;
+ this->cmdMax = _p.cmdMax;
+ this->cmdMin = _p.cmdMin;
+ this->cmdOffset = _p.cmdOffset;
+ this->pErrLast = _p.pErrLast;
+ this->pErr = _p.pErr;
+ this->iErr = _p.iErr;
+ this->dErr = _p.dErr;
+ this->cmd = _p.cmd;
+
+ return *this;
+}
+
+/////////////////////////////////////////////////
+void PID::SetPGain(const double _p)
+{
+ this->pGain = _p;
+}
+
+/////////////////////////////////////////////////
+void PID::SetIGain(const double _i)
+{
+ this->iGain = _i;
+}
+
+/////////////////////////////////////////////////
+void PID::SetDGain(const double _d)
+{
+ this->dGain = _d;
+}
+
+/////////////////////////////////////////////////
+void PID::SetIMax(const double _i)
+{
+ this->iMax = _i;
+}
+
+/////////////////////////////////////////////////
+void PID::SetIMin(const double _i)
+{
+ this->iMin = _i;
+}
+
+/////////////////////////////////////////////////
+void PID::SetCmdMax(const double _c)
+{
+ this->cmdMax = _c;
+}
+
+/////////////////////////////////////////////////
+void PID::SetCmdMin(const double _c)
+{
+ this->cmdMin = _c;
+}
+
+/////////////////////////////////////////////////
+void PID::SetCmdOffset(const double _c)
+{
+ this->cmdOffset = _c;
+}
+
+/////////////////////////////////////////////////
+void PID::Reset()
+{
+ this->pErrLast = 0.0;
+ this->pErr = 0.0;
+ this->iErr = 0.0;
+ this->dErr = 0.0;
+ this->cmd = 0.0;
+}
+
+/////////////////////////////////////////////////
+double PID::Update(const double _error,
+ const std::chrono::duration<double> &_dt)
+{
+ if (_dt == std::chrono::duration<double>(0) ||
+ ignition::math::isnan(_error) || std::isinf(_error))
+ {
+ return 0.0;
+ }
+
+ double pTerm, dTerm;
+ this->pErr = _error;
+
+ // Calculate proportional contribution to command
+ pTerm = this->pGain * this->pErr;
+
+ // Calculate the integral error
+ this->iErr = this->iErr + this->iGain * _dt.count() * this->pErr;
+
+ // Check the integral limits
+ // If enabled, this will limit iErr so that the limit is meaningful
+ // in the output
+ if (this->iMax >= this->iMin)
+ this->iErr = clamp(this->iErr, this->iMin, this->iMax);
+
+ // Calculate the derivative error
+ if (_dt != std::chrono::duration<double>(0))
+ {
+ this->dErr = (this->pErr - this->pErrLast) / _dt.count();
+ this->pErrLast = this->pErr;
+ }
+
+ // Calculate derivative contribution to command
+ dTerm = this->dGain * this->dErr;
+ this->cmd = this->cmdOffset -pTerm - this->iErr - dTerm;
+
+ // Check the command limits
+ if (this->cmdMax >= this->cmdMin)
+ this->cmd = clamp(this->cmd, this->cmdMin, this->cmdMax);
+
+ return this->cmd;
+}
+
+/////////////////////////////////////////////////
+void PID::SetCmd(const double _cmd)
+{
+ this->cmd = _cmd;
+}
+
+/////////////////////////////////////////////////
+double PID::Cmd() const
+{
+ return this->cmd;
+}
+
+/////////////////////////////////////////////////
+void PID::Errors(double &_pe, double &_ie, double &_de) const
+{
+ _pe = this->pErr;
+ _ie = this->iErr;
+ _de = this->dErr;
+}
+
+/////////////////////////////////////////////////
+double PID::PGain() const
+{
+ return this->pGain;
+}
+
+/////////////////////////////////////////////////
+double PID::IGain() const
+{
+ return this->iGain;
+}
+
+/////////////////////////////////////////////////
+double PID::DGain() const
+{
+ return this->dGain;
+}
+
+/////////////////////////////////////////////////
+double PID::IMax() const
+{
+ return this->iMax;
+}
+
+/////////////////////////////////////////////////
+double PID::IMin() const
+{
+ return this->iMin;
+}
+
+/////////////////////////////////////////////////
+double PID::CmdMax() const
+{
+ return this->cmdMax;
+}
+
+/////////////////////////////////////////////////
+double PID::CmdMin() const
+{
+ return this->cmdMin;
+}
+
+/////////////////////////////////////////////////
+double PID::CmdOffset() const
+{
+ return this->cmdOffset;
+}
diff --git a/src/PID_TEST.cc b/src/PID_TEST.cc
new file mode 100644
index 0000000..1a01583
--- /dev/null
+++ b/src/PID_TEST.cc
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2016 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/PID.hh"
+#include "ignition/math/Helpers.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(PidTest, ConstructorDefault)
+{
+ const math::PID pid;
+ EXPECT_DOUBLE_EQ(0.0, pid.PGain());
+ EXPECT_DOUBLE_EQ(0.0, pid.IGain());
+ EXPECT_DOUBLE_EQ(0.0, pid.DGain());
+ EXPECT_DOUBLE_EQ(-1.0, pid.IMax());
+ EXPECT_DOUBLE_EQ(0.0, pid.IMin());
+ EXPECT_DOUBLE_EQ(-1.0, pid.CmdMax());
+ EXPECT_DOUBLE_EQ(0.0, pid.CmdMin());
+ EXPECT_DOUBLE_EQ(0.0, pid.CmdOffset());
+ EXPECT_DOUBLE_EQ(0.0, pid.Cmd());
+
+ double pe, ie, de;
+ pid.Errors(pe, ie, de);
+ EXPECT_DOUBLE_EQ(pe, 0.0);
+ EXPECT_DOUBLE_EQ(ie, 0.0);
+ EXPECT_DOUBLE_EQ(de, 0.0);
+}
+
+/////////////////////////////////////////////////
+TEST(PidTest, CoverageExtra)
+{
+ // getting full destructor coverage
+ math::PID *p = new math::PID;
+ EXPECT_TRUE(p != NULL);
+ delete p;
+}
+
+/////////////////////////////////////////////////
+TEST(PidTest, SetValues)
+{
+ const math::PID pid2(1.0, 2.1, -4.5, 10.5, 1.4, 45, -35, 1.3);
+ EXPECT_DOUBLE_EQ(1.0, pid2.PGain());
+ EXPECT_DOUBLE_EQ(2.1, pid2.IGain());
+ EXPECT_DOUBLE_EQ(-4.5, pid2.DGain());
+ EXPECT_DOUBLE_EQ(10.5, pid2.IMax());
+ EXPECT_DOUBLE_EQ(1.4, pid2.IMin());
+ EXPECT_DOUBLE_EQ(45, pid2.CmdMax());
+ EXPECT_DOUBLE_EQ(-35, pid2.CmdMin());
+ EXPECT_DOUBLE_EQ(1.3, pid2.CmdOffset());
+ EXPECT_DOUBLE_EQ(0.0, pid2.Cmd());
+
+ // Test Set*() functions
+ {
+ const double cmd = 10.4;
+ math::PID pid;
+ pid.SetPGain(pid2.PGain());
+ pid.SetIGain(pid2.IGain());
+ pid.SetDGain(pid2.DGain());
+ pid.SetIMax(pid2.IMax());
+ pid.SetIMin(pid2.IMin());
+ pid.SetCmdMax(pid2.CmdMax());
+ pid.SetCmdMin(pid2.CmdMin());
+ pid.SetCmdOffset(pid2.CmdOffset());
+ pid.SetCmd(cmd);
+
+ EXPECT_DOUBLE_EQ(pid.PGain(), pid2.PGain());
+ EXPECT_DOUBLE_EQ(pid.IGain(), pid2.IGain());
+ EXPECT_DOUBLE_EQ(pid.DGain(), pid2.DGain());
+ EXPECT_DOUBLE_EQ(pid.IMax(), pid2.IMax());
+ EXPECT_DOUBLE_EQ(pid.IMin(), pid2.IMin());
+ EXPECT_DOUBLE_EQ(pid.CmdMax(), pid2.CmdMax());
+ EXPECT_DOUBLE_EQ(pid.CmdMin(), pid2.CmdMin());
+ EXPECT_DOUBLE_EQ(pid.CmdOffset(), pid2.CmdOffset());
+ EXPECT_DOUBLE_EQ(pid.Cmd(), cmd);
+ }
+
+ // Assignment operator
+ {
+ math::PID pid;
+ pid = pid2;
+ EXPECT_DOUBLE_EQ(pid.PGain(), pid2.PGain());
+ EXPECT_DOUBLE_EQ(pid.IGain(), pid2.IGain());
+ EXPECT_DOUBLE_EQ(pid.DGain(), pid2.DGain());
+ EXPECT_DOUBLE_EQ(pid.IMax(), pid2.IMax());
+ EXPECT_DOUBLE_EQ(pid.IMin(), pid2.IMin());
+ EXPECT_DOUBLE_EQ(pid.CmdMax(), pid2.CmdMax());
+ EXPECT_DOUBLE_EQ(pid.CmdMin(), pid2.CmdMin());
+ EXPECT_DOUBLE_EQ(pid.CmdOffset(), pid2.CmdOffset());
+ EXPECT_DOUBLE_EQ(pid.Cmd(), pid2.Cmd());
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(PidTest, EqualOperatorCornerCase)
+{
+ math::PID pid(1.0, 2.1, -4.5, 10.5, 1.4, 45, -35, 1.23);
+ EXPECT_DOUBLE_EQ(pid.PGain(), 1.0);
+ EXPECT_DOUBLE_EQ(pid.IGain(), 2.1);
+ EXPECT_DOUBLE_EQ(pid.DGain(), -4.5);
+ EXPECT_DOUBLE_EQ(pid.IMax(), 10.5);
+ EXPECT_DOUBLE_EQ(pid.IMin(), 1.4);
+ EXPECT_DOUBLE_EQ(pid.CmdMax(), 45.0);
+ EXPECT_DOUBLE_EQ(pid.CmdMin(), -35.0);
+ EXPECT_DOUBLE_EQ(pid.CmdOffset(), 1.23);
+ EXPECT_DOUBLE_EQ(pid.Cmd(), 0.0);
+
+ pid = pid;
+
+ EXPECT_DOUBLE_EQ(pid.PGain(), 1.0);
+ EXPECT_DOUBLE_EQ(pid.IGain(), 2.1);
+ EXPECT_DOUBLE_EQ(pid.DGain(), -4.5);
+ EXPECT_DOUBLE_EQ(pid.IMax(), 10.5);
+ EXPECT_DOUBLE_EQ(pid.IMin(), 1.4);
+ EXPECT_DOUBLE_EQ(pid.CmdMax(), 45.0);
+ EXPECT_DOUBLE_EQ(pid.CmdMin(), -35.0);
+ EXPECT_DOUBLE_EQ(pid.CmdOffset(), 1.23);
+ EXPECT_DOUBLE_EQ(pid.Cmd(), 0.0);
+}
+
+/////////////////////////////////////////////////
+TEST(PidTest, Update)
+{
+ math::PID pid;
+ pid.Init(1.0, 0.1, 0.5, 10, 0, 20, -20);
+
+ double result = pid.Update(5.0, std::chrono::duration<double>(0.0));
+ EXPECT_DOUBLE_EQ(result, 0.0);
+
+ result = pid.Update(5.0, std::chrono::duration<double>(10.0));
+ EXPECT_DOUBLE_EQ(result, -10.25);
+
+ double pe, ie, de;
+ pid.Errors(pe, ie, de);
+ EXPECT_DOUBLE_EQ(pe, 5);
+ EXPECT_DOUBLE_EQ(ie, 5);
+ EXPECT_DOUBLE_EQ(de, 0.5);
+
+ // Test max integral term
+ pid.SetIMax(0.2);
+ pid.SetIGain(10.0);
+ result = pid.Update(5.0, std::chrono::duration<double>(10.0));
+ EXPECT_DOUBLE_EQ(result, -5.2);
+ pid.Errors(pe, ie, de);
+ EXPECT_DOUBLE_EQ(pe, 5);
+ EXPECT_DOUBLE_EQ(ie, 0.2);
+ EXPECT_DOUBLE_EQ(de, 0.0);
+
+ // Test min integral term
+ pid.SetIMax(20);
+ pid.SetIMin(1.4);
+ pid.SetIGain(0.01);
+ result = pid.Update(5.0, std::chrono::duration<double>(10.0));
+ EXPECT_DOUBLE_EQ(result, -6.4);
+ pid.Errors(pe, ie, de);
+ EXPECT_DOUBLE_EQ(pe, 5);
+ EXPECT_DOUBLE_EQ(ie, 1.4);
+ EXPECT_DOUBLE_EQ(de, 0.0);
+}
+
+/////////////////////////////////////////////////
+/// \brief Helper function for testing PID::Update
+/// \param[in] _pid PID object.
+/// \param[in] _result Expected PID output.
+/// \param[in] _error Error input to Update.
+/// \param[in] _dt Time interval.
+/// \param[in] _pErr Expected proportional error.
+/// \param[in] _iErr Expected integral error.
+/// \param[in] _dErr Expected derivative error.
+void UpdateTest(math::PID &_pid, const double _result, const double _error,
+ const std::chrono::duration<double> &_dt,
+ const double _pErr, const double _iErr, const double _dErr)
+{
+ EXPECT_DOUBLE_EQ(_result, _pid.Update(_error, _dt));
+ double pErr, iErr, dErr;
+ _pid.Errors(pErr, iErr, dErr);
+ EXPECT_DOUBLE_EQ(pErr, _pErr);
+ EXPECT_DOUBLE_EQ(iErr, _iErr);
+ EXPECT_DOUBLE_EQ(dErr, _dErr);
+}
+
+/////////////////////////////////////////////////
+TEST(PidTest, ZeroGains)
+{
+ // controller with zero gains, no command limits
+ math::PID pid;
+
+ std::cerr << "zero inputs, expect zero outputs" << std::endl;
+ // repeat once to test derivative and integral error
+ UpdateTest(pid, 0, 0, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, 0, std::chrono::duration<double>(0), 0, 0, 0);
+
+ std::cerr << "dt = 0, no change since previous state" << std::endl;
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, -1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, -1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+
+ std::cerr << "dt > 0, but gains still zero" << std::endl;
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(1), 1, 0, 1);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(1), 1, 0, 0);
+ UpdateTest(pid, 0, -1, std::chrono::duration<double>(1), -1, 0, -2);
+ UpdateTest(pid, 0, -1, std::chrono::duration<double>(1), -1, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(1), 1, 0, 2);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(1), 1, 0, 0);
+
+ std::cerr << "dt = 0, no change since previous state" << std::endl;
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 1, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 1, 0, 0);
+ UpdateTest(pid, 0, -1, std::chrono::duration<double>(0), 1, 0, 0);
+ UpdateTest(pid, 0, -1, std::chrono::duration<double>(0), 1, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 1, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 1, 0, 0);
+
+ std::cerr << "dt < 0, but gains still zero" << std::endl;
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(-1), 1, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(-1), 1, 0, 0);
+ UpdateTest(pid, 0, -1, std::chrono::duration<double>(-1), -1, 0, 2);
+ UpdateTest(pid, 0, -1, std::chrono::duration<double>(-1), -1, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(-1), 1, 0, -2);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(-1), 1, 0, 0);
+
+ std::cerr << "Reset" << std::endl;
+ pid.Reset();
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+
+ std::cerr << "unclamp Cmd values" << std::endl;
+ // CmdMax defaults to -1.0
+ // setting CmdMin to -10.0, means output should now be -1.0
+ // when time is non-zero
+ pid.SetCmdMin(-10.0);
+ EXPECT_DOUBLE_EQ(-10.0, pid.CmdMin());
+ // command hasn't been updated yet
+ EXPECT_DOUBLE_EQ(0.0, pid.Cmd());
+
+ std::cerr << "dt = 0, still report cmd = 0" << std::endl;
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, -1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, -1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+
+ std::cerr << "dt > 0, report clamped value" << std::endl;
+ UpdateTest(pid, -1, 1, std::chrono::duration<double>(1), 1, 0, 1);
+ UpdateTest(pid, -1, 1, std::chrono::duration<double>(1), 1, 0, 0);
+ UpdateTest(pid, -1, -1, std::chrono::duration<double>(1), -1, 0, -2);
+ UpdateTest(pid, -1, -1, std::chrono::duration<double>(1), -1, 0, 0);
+ UpdateTest(pid, -1, 1, std::chrono::duration<double>(1), 1, 0, 2);
+ UpdateTest(pid, -1, 1, std::chrono::duration<double>(1), 1, 0, 0);
+
+ std::cerr << "Reset" << std::endl;
+ pid.Reset();
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+
+ std::cerr << "unclamp iErr values" << std::endl;
+ // IMax defaults to -1.0
+ // setting IMin to -10.0, means output should now be -1.0
+ // when time is non-zero
+ pid.SetIMin(-10.0);
+ EXPECT_DOUBLE_EQ(-10.0, pid.IMin());
+ // iErr hasn't been updated yet
+ {
+ double pErr, iErr, dErr;
+ pid.Errors(pErr, iErr, dErr);
+ EXPECT_DOUBLE_EQ(0.0, iErr);
+ }
+
+ std::cerr << "dt = 0, still report iErr = 0" << std::endl;
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, -1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, -1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+
+ std::cerr << "dt > 0, report clamped value" << std::endl;
+ UpdateTest(pid, -1, 1, std::chrono::duration<double>(1), 1, -1, 1);
+ UpdateTest(pid, -1, 1, std::chrono::duration<double>(1), 1, -1, 0);
+ UpdateTest(pid, -1, -1, std::chrono::duration<double>(1), -1, -1, -2);
+ UpdateTest(pid, -1, -1, std::chrono::duration<double>(1), -1, -1, 0);
+ UpdateTest(pid, -1, 1, std::chrono::duration<double>(1), 1, -1, 2);
+ UpdateTest(pid, -1, 1, std::chrono::duration<double>(1), 1, -1, 0);
+
+ std::cerr << "Reset" << std::endl;
+ pid.Reset();
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+
+ std::cerr << "set Cmd Offset" << std::endl;
+ pid.SetCmdOffset(-20.0);
+ EXPECT_DOUBLE_EQ(-20.0, pid.CmdOffset());
+ // Cmd hasn't been updated yet
+ EXPECT_DOUBLE_EQ(0.0, pid.Cmd());
+
+ std::cerr << "dt = 0, still return 0" << std::endl;
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, -1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, -1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+ UpdateTest(pid, 0, 1, std::chrono::duration<double>(0), 0, 0, 0);
+
+ std::cerr << "dt > 0, report negative min value" << std::endl;
+ UpdateTest(pid, -10, 1, std::chrono::duration<double>(1), 1, -1, 1);
+ UpdateTest(pid, -10, 1, std::chrono::duration<double>(1), 1, -1, 0);
+ UpdateTest(pid, -10, -1, std::chrono::duration<double>(1), -1, -1, -2);
+ UpdateTest(pid, -10, -1, std::chrono::duration<double>(1), -1, -1, 0);
+ UpdateTest(pid, -10, 1, std::chrono::duration<double>(1), 1, -1, 2);
+ UpdateTest(pid, -10, 1, std::chrono::duration<double>(1), 1, -1, 0);
+}
+
+/////////////////////////////////////////////////
+TEST(PidTest, Pcontrol)
+{
+ math::PID pid(1);
+ std::chrono::duration<double> dt(1);
+ const int N = 5;
+ for (int i = 0; i < N; ++i)
+ {
+ double d = static_cast<double>(i);
+ EXPECT_DOUBLE_EQ(-d, pid.Update(d, std::chrono::duration<double>(1)));
+ }
+
+ pid.SetPGain(2);
+ for (int i = 0; i < N; ++i)
+ {
+ double d = static_cast<double>(i);
+ EXPECT_DOUBLE_EQ(-2*d, pid.Update(d, std::chrono::duration<double>(1)));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(PidTest, Icontrol)
+{
+ math::PID pid(0, 1);
+ std::chrono::duration<double> dt(1);
+ const int N = 5;
+ for (int i = 0; i < N; ++i)
+ {
+ double d = static_cast<double>(i+1);
+ EXPECT_DOUBLE_EQ(-d, pid.Update(1, std::chrono::duration<double>(1)));
+ }
+
+ pid.SetIGain(2);
+ double I0;
+ {
+ double pErr, iErr, dErr;
+ pid.Errors(pErr, iErr, dErr);
+ EXPECT_DOUBLE_EQ(N, iErr);
+ I0 = iErr;
+ }
+
+ // confirm that changing gain doesn't cause jumps in integral control
+ EXPECT_DOUBLE_EQ(-I0, pid.Update(0, std::chrono::duration<double>(1)));
+ EXPECT_DOUBLE_EQ(-I0, pid.Update(0, std::chrono::duration<double>(1)));
+
+ for (int i = 0; i < N; ++i)
+ {
+ double d = static_cast<double>(i+1);
+ EXPECT_DOUBLE_EQ(-I0-2*d, pid.Update(1, std::chrono::duration<double>(1)));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(PidTest, Dcontrol)
+{
+ math::PID pid(0, 0, 1);
+ std::chrono::duration<double> dt(1);
+ EXPECT_DOUBLE_EQ(1, pid.Update(-1, std::chrono::duration<double>(1)));
+ const int N = 5;
+ for (int i = 0; i < N; ++i)
+ {
+ double d = static_cast<double>(i);
+ EXPECT_DOUBLE_EQ(-1, pid.Update(d, std::chrono::duration<double>(1)));
+ }
+
+ pid.SetDGain(2);
+ EXPECT_DOUBLE_EQ(10, pid.Update(-1, std::chrono::duration<double>(1)));
+ for (int i = 0; i < N; ++i)
+ {
+ double d = static_cast<double>(i);
+ EXPECT_DOUBLE_EQ(-2, pid.Update(d, std::chrono::duration<double>(1)));
+ }
+}
diff --git a/src/Plane_TEST.cc b/src/Plane_TEST.cc
new file mode 100644
index 0000000..c32eb3e
--- /dev/null
+++ b/src/Plane_TEST.cc
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Helpers.hh"
+#include "ignition/math/Plane.hh"
+
+using namespace ignition;
+using namespace math;
+
+/////////////////////////////////////////////////
+TEST(PlaneTest, PlaneConstructor)
+{
+ Planed plane(Vector3d(1, 0, 0), 0.1);
+ EXPECT_EQ(plane.Normal(), Vector3d(1, 0, 0));
+ EXPECT_NEAR(plane.Offset(), 0.1, 1e-6);
+}
+
+#ifdef _MSC_VER
+#pragma warning(push)
+// C4723: potential divide by 0
+#pragma warning(disable : 4723)
+#endif
+/////////////////////////////////////////////////
+TEST(PlaneTest, Distance)
+{
+ Planed plane(Vector3d(0, 0, 1), 0.1);
+ EXPECT_NEAR(plane.Distance(Vector3d(0, 0, 0),
+ Vector3d(0, 0, 1)), 0.1, 1e-6);
+
+ EXPECT_NEAR(plane.Distance(Vector3d(0, 0, 0.1),
+ Vector3d(0, 0, 1)), 0, 1e-6);
+
+ EXPECT_NEAR(plane.Distance(Vector3d(0, 0, 0.2),
+ Vector3d(0, 0, 1)), -0.1, 1e-6);
+ EXPECT_NEAR(plane.Distance(Vector3d(0, 0, 0.1),
+ Vector3d(1, 0, 0)), 0, 1e-6);
+}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+/////////////////////////////////////////////////
+TEST(PlaneTest, Plane)
+{
+ {
+ Planed plane;
+ EXPECT_TRUE(equal(plane.Offset(), 0.0));
+ EXPECT_TRUE(plane.Normal() == Vector3d());
+ EXPECT_TRUE(plane.Size() == Vector2d(0, 0));
+ }
+
+ {
+ Planed plane(Vector3d(0, 0, 1), Vector2d(2, 3), 2.0);
+ EXPECT_TRUE(equal(plane.Offset(), 2.0));
+ EXPECT_TRUE(plane.Normal() == Vector3d(0, 0, 1));
+ EXPECT_TRUE(plane.Size() == Vector2d(2, 3));
+
+ EXPECT_DOUBLE_EQ(-1, plane.Distance(Vector3d(0, 0, 1),
+ Vector3d(0, 0, -1)));
+
+ plane.Set(Vector3d(1, 0, 0), Vector2d(1, 1), 1.0);
+ EXPECT_TRUE(equal(plane.Offset(), 1.0));
+ EXPECT_TRUE(plane.Normal() == Vector3d(1, 0, 0));
+ EXPECT_TRUE(plane.Size() == Vector2d(1, 1));
+
+ plane = Planed(Vector3d(0, 1, 0), Vector2d(4, 4), 5.0);
+ EXPECT_TRUE(equal(plane.Offset(), 5.0));
+ EXPECT_TRUE(plane.Normal() == Vector3d(0, 1, 0));
+ EXPECT_TRUE(plane.Size() == Vector2d(4, 4));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(PlaneTest, SidePoint)
+{
+ Planed plane(Vector3d(0, 0, 1), 1);
+
+ // On the negative side of the plane (below the plane)
+ Vector3d point(0, 0, 0);
+ EXPECT_EQ(plane.Side(point), Planed::NEGATIVE_SIDE);
+
+ // Still on the negative side of the plane (below the plane)
+ point.Set(1, 1, 0);
+ EXPECT_EQ(plane.Side(point), Planed::NEGATIVE_SIDE);
+
+ // Above the plane (positive side)
+ point.Set(1, 1, 2);
+ EXPECT_EQ(plane.Side(point), Planed::POSITIVE_SIDE);
+
+ // On the plane
+ point.Set(0, 0, 1);
+ EXPECT_EQ(plane.Side(point), Planed::NO_SIDE);
+
+ // Change the plane, but the point is still on the negative side
+ plane.Set(Vector3d(1, 0, 0), 4);
+ EXPECT_EQ(plane.Side(point), Planed::NEGATIVE_SIDE);
+
+ // Point is now on the positive side
+ point.Set(4.1, 0, 1);
+ EXPECT_EQ(plane.Side(point), Planed::POSITIVE_SIDE);
+}
+
+/////////////////////////////////////////////////
+TEST(PlaneTest, SideBox)
+{
+ Planed plane(Vector3d(0, 0, 1), 1);
+
+ // On the negative side of the plane (below the plane)
+ {
+ Box box(Vector3d(-.5, -.5, -.5), Vector3d(.5, .5, .5));
+ EXPECT_EQ(plane.Side(box), Planed::NEGATIVE_SIDE);
+ }
+
+ // Still on the negative side of the plane (below the plane)
+ {
+ Box box(Vector3d(-10, -10, -10), Vector3d(.9, .9, .9));
+ EXPECT_EQ(plane.Side(box), Planed::NEGATIVE_SIDE);
+ }
+
+ // Above the plane (positive side)
+ {
+ Box box(Vector3d(2, 2, 2), Vector3d(3, 3, 3));
+ EXPECT_EQ(plane.Side(box), Planed::POSITIVE_SIDE);
+ }
+
+ // On both sides the plane
+ {
+ Box box(Vector3d(0, 0, 0), Vector3d(3, 3, 3));
+ EXPECT_EQ(plane.Side(box), Planed::BOTH_SIDE);
+ }
+}
diff --git a/src/Pose_TEST.cc b/src/Pose_TEST.cc
new file mode 100644
index 0000000..cb38c86
--- /dev/null
+++ b/src/Pose_TEST.cc
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Helpers.hh"
+#include "ignition/math/Pose3.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(PoseTest, Pose)
+{
+ {
+ // test hypothesis that if
+ // A is the transform from O to P specified in frame O
+ // B is the transform from P to Q specified in frame P
+ // then, B + A is the transform from O to Q specified in frame O
+ math::Pose3d A(math::Vector3d(1, 0, 0),
+ math::Quaterniond(0, 0, IGN_PI/4.0));
+ math::Pose3d B(math::Vector3d(1, 0, 0),
+ math::Quaterniond(0, 0, IGN_PI/2.0));
+ EXPECT_TRUE(math::equal((B + A).Pos().X(), 1.0 + 1.0/sqrt(2)));
+ EXPECT_TRUE(math::equal((B + A).Pos().Y(), 1.0/sqrt(2)));
+ EXPECT_TRUE(math::equal((B + A).Pos().Z(), 0.0));
+ EXPECT_TRUE(math::equal((B + A).Rot().Euler().X(), 0.0));
+ EXPECT_TRUE(math::equal((B + A).Rot().Euler().Y(), 0.0));
+ EXPECT_TRUE(math::equal((B + A).Rot().Euler().Z(), 3.0*IGN_PI/4.0));
+ }
+ {
+ // If:
+ // A is the transform from O to P in frame O
+ // B is the transform from O to Q in frame O
+ // then -A is transform from P to O specified in frame P
+ math::Pose3d A(math::Vector3d(1, 0, 0),
+ math::Quaterniond(0, 0, IGN_PI/4.0));
+ EXPECT_TRUE(math::equal((math::Pose3d() - A).Pos().X(), -1.0/sqrt(2)));
+ EXPECT_TRUE(math::equal((math::Pose3d() - A).Pos().Y(), 1.0/sqrt(2)));
+ EXPECT_TRUE(math::equal((math::Pose3d() - A).Pos().Z(), 0.0));
+ EXPECT_TRUE(math::equal((math::Pose3d() - A).Rot().Euler().X(), 0.0));
+ EXPECT_TRUE(math::equal((math::Pose3d() - A).Rot().Euler().Y(), 0.0));
+ EXPECT_TRUE(
+ math::equal((math::Pose3d() - A).Rot().Euler().Z(), -IGN_PI/4));
+
+ // test negation operator
+ EXPECT_TRUE(math::equal((-A).Pos().X(), -1.0/sqrt(2)));
+ EXPECT_TRUE(math::equal((-A).Pos().Y(), 1.0/sqrt(2)));
+ EXPECT_TRUE(math::equal((-A).Pos().Z(), 0.0));
+ EXPECT_TRUE(math::equal((-A).Rot().Euler().X(), 0.0));
+ EXPECT_TRUE(math::equal((-A).Rot().Euler().Y(), 0.0));
+ EXPECT_TRUE(math::equal((-A).Rot().Euler().Z(), -IGN_PI/4.0));
+ }
+ {
+ // If:
+ // A is the transform from O to P in frame O
+ // B is the transform from O to Q in frame O
+ // B - A is the transform from P to Q in frame P
+ math::Pose3d A(math::Vector3d(1, 0, 0),
+ math::Quaterniond(0, 0, IGN_PI/4.0));
+ math::Pose3d B(math::Vector3d(1, 1, 0),
+ math::Quaterniond(0, 0, IGN_PI/2.0));
+ EXPECT_TRUE(math::equal((B - A).Pos().X(), 1.0/sqrt(2)));
+ EXPECT_TRUE(math::equal((B - A).Pos().Y(), 1.0/sqrt(2)));
+ EXPECT_TRUE(math::equal((B - A).Pos().Z(), 0.0));
+ EXPECT_TRUE(math::equal((B - A).Rot().Euler().X(), 0.0));
+ EXPECT_TRUE(math::equal((B - A).Rot().Euler().Y(), 0.0));
+ EXPECT_TRUE(math::equal((B - A).Rot().Euler().Z(), IGN_PI/4.0));
+ }
+ {
+ math::Pose3d pose;
+ EXPECT_TRUE(pose.Pos() == math::Vector3d(0, 0, 0));
+ EXPECT_TRUE(pose.Rot() == math::Quaterniond(0, 0, 0));
+ }
+
+ math::Pose3d pose(math::Vector3d(1, 2, 3), math::Quaterniond(.1, .2, .3));
+ EXPECT_TRUE(pose.Pos() == math::Vector3d(1, 2, 3));
+ EXPECT_TRUE(pose.Rot() == math::Quaterniond(.1, .2, .3));
+
+ math::Pose3d pose1(pose);
+ EXPECT_TRUE(pose1 == pose);
+
+ pose.Set(math::Vector3d(2, 3, 4), math::Quaterniond(.3, .4, .5));
+ EXPECT_TRUE(pose.Pos() == math::Vector3d(2, 3, 4));
+ EXPECT_TRUE(pose.Rot() == math::Quaterniond(.3, .4, .5));
+ EXPECT_TRUE(pose.IsFinite());
+
+ pose1 = pose.Inverse();
+ EXPECT_TRUE(pose1.Pos() == math::Vector3d(-1.38368, -3.05541, -4.21306));
+ EXPECT_TRUE(pose1.Rot() ==
+ math::Quaterniond(0.946281, -0.0933066, -0.226566, -0.210984));
+
+ pose = math::Pose3d(1, 2, 3, .1, .2, .3) + math::Pose3d(4, 5, 6, .4, .5, .6);
+ EXPECT_TRUE(pose ==
+ math::Pose3d(5.74534, 7.01053, 8.62899, 0.675732, 0.535753, 1.01174));
+
+ pose += pose;
+ EXPECT_TRUE(pose ==
+ math::Pose3d(11.314, 16.0487, 15.2559, 1.49463, 0.184295, 2.13932));
+
+ pose -= math::Pose3d(pose);
+ EXPECT_TRUE(pose ==
+ math::Pose3d(0, 0, 0, 0, 0, 0));
+
+ pose.Pos().Set(5, 6, 7);
+ pose.Rot().Euler(math::Vector3d(.4, .6, 0));
+
+ EXPECT_TRUE(pose.CoordPositionAdd(math::Vector3d(1, 2, 3)) ==
+ math::Vector3d(7.82531, 6.67387, 9.35871));
+
+ EXPECT_TRUE(pose.CoordPositionAdd(pose1) ==
+ math::Vector3d(2.58141, 2.4262, 3.8013));
+ EXPECT_TRUE(pose.CoordRotationAdd(math::Quaterniond(0.1, 0, 0.2)) ==
+ math::Quaterniond(0.520975, 0.596586, 0.268194));
+ EXPECT_TRUE(pose.CoordPoseSolve(pose1) ==
+ math::Pose3d(-0.130957, -11.552, -10.2329,
+ -0.462955, -1.15624, -0.00158047));
+
+ EXPECT_TRUE(pose.RotatePositionAboutOrigin(math::Quaterniond(0.1, 0, 0.2)) ==
+ math::Pose3d(6.09235, 5.56147, 6.47714, 0.4, 0.6, 0));
+
+ pose.Reset();
+ EXPECT_TRUE(pose.Pos() == math::Vector3d(0, 0, 0));
+ EXPECT_TRUE(pose.Rot() == math::Quaterniond(0, 0, 0));
+}
+
+/////////////////////////////////////////////////
+TEST(PoseTest, ConstPose)
+{
+ const math::Pose3d pose(0, 1, 2, 0, 0, 0);
+
+ EXPECT_TRUE(pose.Pos() == math::Vector3d(0, 1, 2));
+ EXPECT_TRUE(pose.Rot() == math::Quaterniond(0, 0, 0));
+}
+
+/////////////////////////////////////////////////
+TEST(PoseTest, OperatorStreamOut)
+{
+ math::Pose3d p(0.1, 1.2, 2.3, 0.0, 0.1, 1.0);
+ std::ostringstream stream;
+ stream << p;
+ EXPECT_EQ(stream.str(), "0.1 1.2 2.3 0 0.1 1");
+}
+
diff --git a/src/Quaternion_TEST.cc b/src/Quaternion_TEST.cc
new file mode 100644
index 0000000..59a05d8
--- /dev/null
+++ b/src/Quaternion_TEST.cc
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include <cmath>
+
+#include "ignition/math/Helpers.hh"
+#include "ignition/math/Quaternion.hh"
+#include "ignition/math/Matrix3.hh"
+#include "ignition/math/Matrix4.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(QuaternionTest, Unit)
+{
+ math::Quaterniond q;
+ EXPECT_TRUE(math::equal(q.W(), 1.0));
+ EXPECT_TRUE(math::equal(q.X(), 0.0));
+ EXPECT_TRUE(math::equal(q.Y(), 0.0));
+ EXPECT_TRUE(math::equal(q.Z(), 0.0));
+}
+
+/////////////////////////////////////////////////
+TEST(QuaternionTest, ConstructWithValues)
+{
+ math::Quaterniond q(1, 2, 3, 4);
+ EXPECT_TRUE(math::equal(q.W(), 1.0));
+ EXPECT_TRUE(math::equal(q.X(), 2.0));
+ EXPECT_TRUE(math::equal(q.Y(), 3.0));
+ EXPECT_TRUE(math::equal(q.Z(), 4.0));
+}
+
+/////////////////////////////////////////////////
+TEST(QuaternionTest, ConstructZero)
+{
+ math::Quaterniond q(0, 0, 0, 0);
+ EXPECT_TRUE(math::equal(q.W(), 0.0));
+ EXPECT_TRUE(math::equal(q.X(), 0.0));
+ EXPECT_TRUE(math::equal(q.Y(), 0.0));
+ EXPECT_TRUE(math::equal(q.Z(), 0.0));
+
+ math::Quaterniond qI = q.Inverse();
+ EXPECT_TRUE(math::equal(qI.W(), 1.0));
+ EXPECT_TRUE(math::equal(qI.X(), 0.0));
+ EXPECT_TRUE(math::equal(qI.Y(), 0.0));
+ EXPECT_TRUE(math::equal(qI.Z(), 0.0));
+}
+
+/////////////////////////////////////////////////
+TEST(QuaternionTest, ConstructEuler)
+{
+ math::Quaterniond q(0, 1, 2);
+ EXPECT_TRUE(q == math::Quaterniond(math::Vector3d(0, 1, 2)));
+
+ // Make sure that singularities are being handled properly.
+ // There are an infinite number of equivalent Euler angle
+ // representations when pitch = PI/2, so rather than comparing Euler
+ // angles, we will compare quaternions.
+ for (double pitch : { -IGN_PI_2, IGN_PI_2 })
+ {
+ for (double roll = 0; roll < 2 * IGN_PI + 0.1; roll += IGN_PI_4)
+ {
+ for (double yaw = 0; yaw < 2 * IGN_PI + 0.1; yaw += IGN_PI_4)
+ {
+ math::Quaterniond q_orig(roll, pitch, yaw);
+ math::Quaterniond q_derived(q_orig.Euler());
+ EXPECT_TRUE(q_orig == q_derived || q_orig == -q_derived);
+ }
+ }
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(QuaternionTest, ConstructAxisAngle)
+{
+ math::Quaterniond q1(math::Vector3d(0, 0, 1), IGN_PI);
+ EXPECT_TRUE(math::equal(q1.X(), 0.0));
+ EXPECT_TRUE(math::equal(q1.Y(), 0.0));
+ EXPECT_TRUE(math::equal(q1.Z(), 1.0));
+ EXPECT_TRUE(math::equal(q1.W(), 0.0));
+
+ math::Quaterniond q(q1);
+ EXPECT_TRUE(q == q1);
+}
+
+/////////////////////////////////////////////////
+TEST(QuaternionTest, Identity)
+{
+ math::Quaterniond q = math::Quaterniond::Identity;
+ EXPECT_TRUE(math::equal(q.W(), 1.0));
+ EXPECT_TRUE(math::equal(q.X(), 0.0));
+ EXPECT_TRUE(math::equal(q.Y(), 0.0));
+ EXPECT_TRUE(math::equal(q.Z(), 0.0));
+}
+
+//////////////////////////////////////////////////
+TEST(QuaternionTest, Integrate)
+{
+ // Integrate by zero, expect no change
+ {
+ const math::Quaterniond q(0.5, 0.5, 0.5, 0.5);
+ EXPECT_EQ(q, q.Integrate(math::Vector3d::Zero, 1.0));
+ EXPECT_EQ(q, q.Integrate(math::Vector3d::UnitX, 0.0));
+ EXPECT_EQ(q, q.Integrate(math::Vector3d::UnitY, 0.0));
+ EXPECT_EQ(q, q.Integrate(math::Vector3d::UnitZ, 0.0));
+ }
+
+ // Integrate along single axes,
+ // expect linear change in roll, pitch, yaw
+ {
+ const math::Quaterniond q(1, 0, 0, 0);
+ math::Quaterniond qRoll = q.Integrate(math::Vector3d::UnitX, 1.0);
+ math::Quaterniond qPitch = q.Integrate(math::Vector3d::UnitY, 1.0);
+ math::Quaterniond qYaw = q.Integrate(math::Vector3d::UnitZ, 1.0);
+ EXPECT_EQ(qRoll.Euler(), math::Vector3d::UnitX);
+ EXPECT_EQ(qPitch.Euler(), math::Vector3d::UnitY);
+ EXPECT_EQ(qYaw.Euler(), math::Vector3d::UnitZ);
+ }
+
+ // Integrate sequentially along single axes in order XYZ,
+ // expect rotations to match Euler Angles
+ {
+ const math::Quaterniond q(1, 0, 0, 0);
+ const double angle = 0.5;
+ math::Quaterniond qX = q.Integrate(math::Vector3d::UnitX, angle);
+ math::Quaterniond qXY = qX.Integrate(math::Vector3d::UnitY, angle);
+ EXPECT_EQ(qXY.Euler(), angle*math::Vector3d(1, 1, 0));
+ }
+ {
+ const math::Quaterniond q(1, 0, 0, 0);
+ const double angle = 0.5;
+ math::Quaterniond qX = q.Integrate(math::Vector3d::UnitX, angle);
+ math::Quaterniond qXZ = qX.Integrate(math::Vector3d::UnitZ, angle);
+ EXPECT_EQ(qXZ.Euler(), angle*math::Vector3d(1, 0, 1));
+ }
+ {
+ const math::Quaterniond q(1, 0, 0, 0);
+ const double angle = 0.5;
+ math::Quaterniond qY = q.Integrate(math::Vector3d::UnitY, angle);
+ math::Quaterniond qYZ = qY.Integrate(math::Vector3d::UnitZ, angle);
+ EXPECT_EQ(qYZ.Euler(), angle*math::Vector3d(0, 1, 1));
+ }
+ {
+ const math::Quaterniond q(1, 0, 0, 0);
+ const double angle = 0.5;
+ math::Quaterniond qX = q.Integrate(math::Vector3d::UnitX, angle);
+ math::Quaterniond qXY = qX.Integrate(math::Vector3d::UnitY, angle);
+ math::Quaterniond qXYZ = qXY.Integrate(math::Vector3d::UnitZ, angle);
+ EXPECT_EQ(qXYZ.Euler(), angle*math::Vector3d::One);
+ }
+
+ // Integrate sequentially along single axes in order ZYX,
+ // expect rotations to not match Euler Angles
+ {
+ const math::Quaterniond q(1, 0, 0, 0);
+ const double angle = 0.5;
+ math::Quaterniond qZ = q.Integrate(math::Vector3d::UnitZ, angle);
+ math::Quaterniond qZY = qZ.Integrate(math::Vector3d::UnitY, angle);
+ EXPECT_NE(qZY.Euler(), angle*math::Vector3d(0, 1, 1));
+ }
+ {
+ const math::Quaterniond q(1, 0, 0, 0);
+ const double angle = 0.5;
+ math::Quaterniond qZ = q.Integrate(math::Vector3d::UnitZ, angle);
+ math::Quaterniond qZX = qZ.Integrate(math::Vector3d::UnitX, angle);
+ EXPECT_NE(qZX.Euler(), angle*math::Vector3d(1, 0, 1));
+ }
+ {
+ const math::Quaterniond q(1, 0, 0, 0);
+ const double angle = 0.5;
+ math::Quaterniond qZ = q.Integrate(math::Vector3d::UnitZ, angle);
+ math::Quaterniond qZY = qZ.Integrate(math::Vector3d::UnitY, angle);
+ math::Quaterniond qZYX = qZY.Integrate(math::Vector3d::UnitX, angle);
+ EXPECT_NE(qZYX.Euler(), angle*math::Vector3d(1, 1, 1));
+ }
+ {
+ const math::Quaterniond q(1, 0, 0, 0);
+ const double angle = 0.5;
+ math::Quaterniond qY = q.Integrate(math::Vector3d::UnitY, angle);
+ math::Quaterniond qYX = qY.Integrate(math::Vector3d::UnitX, angle);
+ EXPECT_NE(qYX.Euler(), angle*math::Vector3d(1, 1, 0));
+ }
+
+ // Integrate a full rotation about different axes,
+ // expect no change.
+ {
+ const math::Quaterniond q(0.5, 0.5, 0.5, 0.5);
+ const double fourPi = 4 * IGN_PI;
+ math::Quaterniond qX = q.Integrate(math::Vector3d::UnitX, fourPi);
+ math::Quaterniond qY = q.Integrate(math::Vector3d::UnitY, fourPi);
+ math::Quaterniond qZ = q.Integrate(math::Vector3d::UnitZ, fourPi);
+ EXPECT_EQ(q, qX);
+ EXPECT_EQ(q, qY);
+ EXPECT_EQ(q, qZ);
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(QuaternionTest, Math)
+{
+ math::Quaterniond q(IGN_PI*0.1, IGN_PI*0.5, IGN_PI);
+ EXPECT_TRUE(q == math::Quaterniond(0.110616, -0.698401, 0.110616, 0.698401));
+
+ EXPECT_TRUE(q.Log() ==
+ math::Quaterniond(0, -1.02593, 0.162491, 1.02593));
+
+ EXPECT_TRUE(q.Exp() ==
+ math::Quaterniond(0.545456, -0.588972, 0.093284, 0.588972));
+
+ math::Quaterniond q1 = q;
+ q1.W(2.0);
+ EXPECT_TRUE(q1.Log() ==
+ math::Quaterniond(0, -0.698401, 0.110616, 0.698401));
+
+ q1.X(0.000000001);
+ q1.Y(0.0);
+ q1.Z(0.0);
+ q1.W(0.0);
+ EXPECT_TRUE(q1.Exp() == math::Quaterniond(1, 0, 0, 0));
+
+ q.Invert();
+ EXPECT_TRUE(q == math::Quaterniond(0.110616, 0.698401, -0.110616, -0.698401));
+
+ q.Axis(0, 1, 0, IGN_PI);
+ EXPECT_TRUE(q == math::Quaterniond(6.12303e-17, 0, 1, 0));
+
+ q.Axis(math::Vector3d(1, 0, 0), IGN_PI);
+ EXPECT_TRUE(q == math::Quaterniond(0, 1, 0, 0));
+
+ q.Set(1, 2, 3, 4);
+ EXPECT_TRUE(math::equal(q.W(), 1.0));
+ EXPECT_TRUE(math::equal(q.X(), 2.0));
+ EXPECT_TRUE(math::equal(q.Y(), 3.0));
+ EXPECT_TRUE(math::equal(q.Z(), 4.0));
+
+ q.Normalize();
+ EXPECT_TRUE(q == math::Quaterniond(0.182574, 0.365148, 0.547723, 0.730297));
+
+
+ EXPECT_TRUE(math::equal(q.Roll(), 1.4289, 1e-3));
+ EXPECT_TRUE(math::equal(q.Pitch(), -0.339837, 1e-3));
+ EXPECT_TRUE(math::equal(q.Yaw(), 2.35619, 1e-3));
+
+ math::Vector3d axis;
+ double angle;
+ q.ToAxis(axis, angle);
+ EXPECT_TRUE(axis == math::Vector3d(0.371391, 0.557086, 0.742781));
+ EXPECT_TRUE(math::equal(angle, 2.77438, 1e-3));
+
+ q.Scale(0.1);
+ EXPECT_TRUE(q == math::Quaterniond(0.990394, 0.051354, 0.0770309, 0.102708));
+
+ q = q + math::Quaterniond(0, 1, 2);
+ EXPECT_TRUE(q == math::Quaterniond(1.46455, -0.352069, 0.336066, 0.841168));
+
+ q += q;
+ EXPECT_TRUE(q == math::Quaterniond(2.92911, -0.704137, 0.672131, 1.68234));
+
+ q -= math::Quaterniond(.4, .2, .1);
+ EXPECT_TRUE(q == math::Quaterniond(1.95416, -0.896677, 0.56453, 1.65341));
+
+ q = q - math::Quaterniond(0, 1, 2);
+ EXPECT_TRUE(q == math::Quaterniond(1.48, -0.493254, 0.305496, 0.914947));
+
+ q *= math::Quaterniond(.4, .1, .01);
+ EXPECT_TRUE(q == math::Quaterniond(1.53584, -0.236801, 0.551841, 0.802979));
+
+ q = q * 5.0;
+ EXPECT_TRUE(q == math::Quaterniond(7.67918, -1.184, 2.7592, 4.0149));
+
+ EXPECT_TRUE(q.RotateVectorReverse(math::Vector3d(1, 2, 3)) ==
+ math::Vector3d(-0.104115, 0.4975, 3.70697));
+
+ EXPECT_TRUE(math::equal(q.Dot(math::Quaterniond(.4, .2, .1)), 7.67183, 1e-3));
+
+ EXPECT_TRUE(math::Quaterniond::Squad(1.1, math::Quaterniond(.1, 0, .2),
+ math::Quaterniond(0, .3, .4), math::Quaterniond(.5, .2, 1),
+ math::Quaterniond(0, 0, 2), true) ==
+ math::Quaterniond(0.346807, -0.0511734, -0.0494723, 0.935232));
+
+ EXPECT_TRUE(math::Quaterniond::EulerToQuaternion(
+ math::Vector3d(.1, .2, .3)) ==
+ math::Quaterniond(0.983347, 0.0342708, 0.106021, 0.143572));
+
+ q.Round(2);
+ EXPECT_TRUE(math::equal(-1.18, q.X()));
+ EXPECT_TRUE(math::equal(2.76, q.Y()));
+ EXPECT_TRUE(math::equal(4.01, q.Z()));
+ EXPECT_TRUE(math::equal(7.68, q.W()));
+
+ q.X(0.0);
+ q.Y(0.0);
+ q.Z(0.0);
+ q.W(0.0);
+ q.Normalize();
+ EXPECT_TRUE(q == math::Quaterniond());
+
+ q.Axis(0, 0, 0, 0);
+ EXPECT_TRUE(q == math::Quaterniond());
+
+ EXPECT_TRUE(math::Quaterniond::EulerToQuaternion(0.1, 0.2, 0.3) ==
+ math::Quaterniond(0.983347, 0.0342708, 0.106021, 0.143572));
+
+ q.X(0.0);
+ q.Y(0.0);
+ q.Z(0.0);
+ q.W(0.0);
+ q.ToAxis(axis, angle);
+ EXPECT_TRUE(axis == math::Vector3d(1, 0, 0));
+ EXPECT_TRUE(math::equal(angle, 0.0, 1e-3));
+ {
+ // simple 180 rotation about yaw, should result in x and y flipping signs
+ q = math::Quaterniond(0, 0, IGN_PI);
+ math::Vector3d v = math::Vector3d(1, 2, 3);
+ math::Vector3d r1 = q.RotateVector(v);
+ math::Vector3d r2 = q.RotateVectorReverse(v);
+ std::cout << "[" << q.W() << ", " << q.X() << ", "
+ << q.Y() << ", " << q.Z() << "]\n";
+ std::cout << " forward turns [" << v << "] to [" << r1 << "]\n";
+ std::cout << " reverse turns [" << v << "] to [" << r2 << "]\n";
+ EXPECT_TRUE(r1 == math::Vector3d(-1, -2, 3));
+ EXPECT_TRUE(r2 == math::Vector3d(-1, -2, 3));
+ }
+
+ {
+ // simple 90 rotation about yaw, should map x to y, y to -x
+ // simple -90 rotation about yaw, should map x to -y, y to x
+ q = math::Quaterniond(0, 0, 0.5*IGN_PI);
+ math::Vector3d v = math::Vector3d(1, 2, 3);
+ math::Vector3d r1 = q.RotateVector(v);
+ math::Vector3d r2 = q.RotateVectorReverse(v);
+ std::cout << "[" << q.W() << ", " << q.X() << ", "
+ << q.Y() << ", " << q.Z() << "]\n";
+ std::cout << " forward turns [" << v << "] to [" << r1 << "]\n";
+ std::cout << " reverse turns [" << v << "] to [" << r2 << "]\n";
+ std::cout << " x axis [" << q.XAxis() << "]\n";
+ std::cout << " y axis [" << q.YAxis() << "]\n";
+ std::cout << " z axis [" << q.ZAxis() << "]\n";
+ EXPECT_TRUE(r1 == math::Vector3d(-2, 1, 3));
+ EXPECT_TRUE(r2 == math::Vector3d(2, -1, 3));
+ EXPECT_TRUE(q.Inverse().XAxis() == math::Vector3d(0, -1, 0));
+ EXPECT_TRUE(q.Inverse().YAxis() == math::Vector3d(1, 0, 0));
+ EXPECT_TRUE(q.Inverse().ZAxis() == math::Vector3d(0, 0, 1));
+ }
+
+ // Test RPY fixed-body-frame convention:
+ // Rotate each unit vector in roll and pitch
+ {
+ q = math::Quaterniond(IGN_PI/2.0, IGN_PI/2.0, 0);
+ math::Vector3d v1(1, 0, 0);
+ math::Vector3d r1 = q.RotateVector(v1);
+ // 90 degrees about X does nothing,
+ // 90 degrees about Y sends point down to -Z
+ EXPECT_EQ(r1, math::Vector3d(0, 0, -1));
+
+ math::Vector3d v2(0, 1, 0);
+ math::Vector3d r2 = q.RotateVector(v2);
+ // 90 degrees about X sends point to +Z
+ // 90 degrees about Y sends point to +X
+ EXPECT_EQ(r2, math::Vector3d(1, 0, 0));
+
+ math::Vector3d v3(0, 0, 1);
+ math::Vector3d r3 = q.RotateVector(v3);
+ // 90 degrees about X sends point to -Y
+ // 90 degrees about Y does nothing
+ EXPECT_EQ(r3, math::Vector3d(0, -1, 0));
+ }
+
+ {
+ // now try a harder case (axis[1,2,3], rotation[0.3*pi])
+ // verified with octave
+ q.Axis(math::Vector3d(1, 2, 3), 0.3*IGN_PI);
+ std::cout << "[" << q.W() << ", " << q.X() << ", "
+ << q.Y() << ", " << q.Z() << "]\n";
+ std::cout << " x [" << q.Inverse().XAxis() << "]\n";
+ std::cout << " y [" << q.Inverse().YAxis() << "]\n";
+ std::cout << " z [" << q.Inverse().ZAxis() << "]\n";
+ EXPECT_TRUE(q.Inverse().XAxis() ==
+ math::Vector3d(0.617229, -0.589769, 0.520770));
+ EXPECT_TRUE(q.Inverse().YAxis() ==
+ math::Vector3d(0.707544, 0.705561, -0.039555));
+ EXPECT_TRUE(q.Inverse().ZAxis() ==
+ math::Vector3d(-0.344106, 0.392882, 0.852780));
+
+ // rotate about the axis of rotation should not change axis
+ math::Vector3d v = math::Vector3d(1, 2, 3);
+ math::Vector3d r1 = q.RotateVector(v);
+ math::Vector3d r2 = q.RotateVectorReverse(v);
+ EXPECT_TRUE(r1 == math::Vector3d(1, 2, 3));
+ EXPECT_TRUE(r2 == math::Vector3d(1, 2, 3));
+
+ // rotate unit vectors
+ v = math::Vector3d(0, 0, 1);
+ r1 = q.RotateVector(v);
+ r2 = q.RotateVectorReverse(v);
+ EXPECT_TRUE(r1 == math::Vector3d(0.520770, -0.039555, 0.852780));
+ EXPECT_TRUE(r2 == math::Vector3d(-0.34411, 0.39288, 0.85278));
+ v = math::Vector3d(0, 1, 0);
+ r1 = q.RotateVector(v);
+ r2 = q.RotateVectorReverse(v);
+ EXPECT_TRUE(r1 == math::Vector3d(-0.58977, 0.70556, 0.39288));
+ EXPECT_TRUE(r2 == math::Vector3d(0.707544, 0.705561, -0.039555));
+ v = math::Vector3d(1, 0, 0);
+ r1 = q.RotateVector(v);
+ r2 = q.RotateVectorReverse(v);
+ EXPECT_TRUE(r1 == math::Vector3d(0.61723, 0.70754, -0.34411));
+ EXPECT_TRUE(r2 == math::Vector3d(0.61723, -0.58977, 0.52077));
+
+ EXPECT_TRUE(-q == math::Quaterniond(-0.891007, -0.121334,
+ -0.242668, -0.364002));
+
+ EXPECT_TRUE(math::Matrix3d(q) == math::Matrix3d(
+ 0.617229, -0.589769, 0.52077,
+ 0.707544, 0.705561, -0.0395554,
+ -0.344106, 0.392882, 0.85278));
+
+ math::Matrix3d matFromQ;
+ matFromQ = q;
+ EXPECT_TRUE(matFromQ == math::Matrix3d(
+ 0.617229, -0.589769, 0.52077,
+ 0.707544, 0.705561, -0.0395554,
+ -0.344106, 0.392882, 0.85278));
+
+ EXPECT_TRUE(math::Matrix4d(q) == math::Matrix4d(
+ 0.617229, -0.589769, 0.52077, 0,
+ 0.707544, 0.705561, -0.0395554, 0,
+ -0.344106, 0.392882, 0.85278, 0,
+ 0, 0, 0, 1));
+
+ math::Matrix3d matFromQuat(q);
+
+ math::Quaterniond quatFromMat(matFromQuat);
+ math::Quaterniond quatFromMat2; quatFromMat2.Matrix(matFromQuat);
+
+ EXPECT_TRUE(q == quatFromMat);
+ EXPECT_TRUE(q == quatFromMat2);
+
+ // test the cases where matrix trace is negative (requires special handling)
+ q = math::Quaterniond(0, 0, 0, 1);
+ EXPECT_TRUE(q == math::Quaterniond(math::Matrix3d(
+ -1, 0, 0,
+ 0, -1, 0,
+ 0, 0, 1)));
+
+ q = math::Quaterniond(0, 0, 1, 0);
+ EXPECT_TRUE(q == math::Quaterniond(math::Matrix3d(
+ -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1)));
+
+ q = math::Quaterniond(0, 1, 0, 0);
+ EXPECT_TRUE(q == math::Quaterniond(math::Matrix3d(
+ 1, 0, 0,
+ 0, -1, 0,
+ 0, 0, -1)));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(QuaternionTest, OperatorStreamOut)
+{
+ math::Quaterniond q(0.1, 1.2, 2.3);
+ std::ostringstream stream;
+ stream << q;
+ EXPECT_EQ(stream.str(), "0.1 1.2 2.3");
+}
+
+/////////////////////////////////////////////////
+TEST(QuaternionTest, Slerp)
+{
+ math::Quaterniond q1(0.1, 1.2, 2.3);
+ math::Quaterniond q2(1.2, 2.3, -3.4);
+
+ math::Quaterniond q3 = math::Quaterniond::Slerp(1.0, q1, q2, true);
+ EXPECT_EQ(q3, math::Quaterniond(0.554528, -0.717339, 0.32579, 0.267925));
+}
+
+/////////////////////////////////////////////////
+TEST(QuaterniondTest, From2Axes)
+{
+ math::Vector3d v1(1.0, 0.0, 0.0);
+ math::Vector3d v2(0.0, 1.0, 0.0);
+
+ math::Quaterniond q1;
+ q1.From2Axes(v1, v2);
+
+ math::Quaterniond q2;
+ q2.From2Axes(v2, v1);
+
+ math::Quaterniond q1Correct(sqrt(2)/2, 0, 0, sqrt(2)/2);
+ math::Quaterniond q2Correct(sqrt(2)/2, 0, 0, -sqrt(2)/2);
+
+ EXPECT_NE(q1, q2);
+ EXPECT_EQ(q1Correct, q1);
+ EXPECT_EQ(q2Correct, q2);
+ EXPECT_EQ(math::Quaterniond::Identity, q1 * q2);
+ EXPECT_EQ(v2, q1 * v1);
+ EXPECT_EQ(v1, q2 * v2);
+
+ // still the same rotation, but with non-unit vectors
+ v1.Set(0.5, 0.5, 0);
+ v2.Set(-0.5, 0.5, 0);
+
+ q1.From2Axes(v1, v2);
+ q2.From2Axes(v2, v1);
+
+ EXPECT_NE(q1, q2);
+ EXPECT_EQ(q1Correct, q1);
+ EXPECT_EQ(q2Correct, q2);
+ EXPECT_EQ(math::Quaterniond::Identity, q1 * q2);
+ EXPECT_EQ(v2, q1 * v1);
+ EXPECT_EQ(v1, q2 * v2);
+
+ // Test various settings of opposite vectors (which need special care)
+
+ v1.Set(1, 0, 0);
+ v2.Set(-1, 0, 0);
+ q1.From2Axes(v1, v2);
+ q2 = q1 * q1;
+ EXPECT_TRUE(math::equal(q2.W(), 1.0) || math::equal(q2.W(), -1.0));
+ EXPECT_TRUE(math::equal(q2.X(), 0.0));
+ EXPECT_TRUE(math::equal(q2.Y(), 0.0));
+ EXPECT_TRUE(math::equal(q2.Z(), 0.0));
+
+ v1.Set(0, 1, 0);
+ v2.Set(0, -1, 0);
+ q1.From2Axes(v1, v2);
+ q2 = q1 * q1;
+ EXPECT_TRUE(math::equal(q2.W(), 1.0) || math::equal(q2.W(), -1.0));
+ EXPECT_TRUE(math::equal(q2.X(), 0.0));
+ EXPECT_TRUE(math::equal(q2.Y(), 0.0));
+ EXPECT_TRUE(math::equal(q2.Z(), 0.0));
+
+ v1.Set(0, 0, 1);
+ v2.Set(0, 0, -1);
+ q1.From2Axes(v1, v2);
+ q2 = q1 * q1;
+ EXPECT_TRUE(math::equal(q2.W(), 1.0) || math::equal(q2.W(), -1.0));
+ EXPECT_TRUE(math::equal(q2.X(), 0.0));
+ EXPECT_TRUE(math::equal(q2.Y(), 0.0));
+ EXPECT_TRUE(math::equal(q2.Z(), 0.0));
+
+ v1.Set(0, 1, 1);
+ v2.Set(0, -1, -1);
+ q1.From2Axes(v1, v2);
+ q2 = q1 * q1;
+ EXPECT_TRUE(math::equal(q2.W(), 1.0) || math::equal(q2.W(), -1.0));
+ EXPECT_TRUE(math::equal(q2.X(), 0.0));
+ EXPECT_TRUE(math::equal(q2.Y(), 0.0));
+ EXPECT_TRUE(math::equal(q2.Z(), 0.0));
+}
+
diff --git a/src/Rand.cc b/src/Rand.cc
new file mode 100644
index 0000000..95fcae4
--- /dev/null
+++ b/src/Rand.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <sys/types.h>
+#include <ctime>
+
+#ifdef _WIN32
+ #include <process.h>
+ #define getpid _getpid
+#else
+ #include <unistd.h>
+#endif
+
+#include "ignition/math/Rand.hh"
+
+using namespace ignition;
+using namespace math;
+
+//////////////////////////////////////////////////
+void Rand::Seed(unsigned int _seed)
+{
+ std::seed_seq seq{_seed};
+ SeedMutable() = _seed;
+ RandGenerator().seed(seq);
+}
+
+//////////////////////////////////////////////////
+unsigned int Rand::Seed()
+{
+ return SeedMutable();
+}
+
+//////////////////////////////////////////////////
+double Rand::DblUniform(double _min, double _max)
+{
+ UniformRealDist d(_min, _max);
+ return d(RandGenerator());
+}
+
+//////////////////////////////////////////////////
+double Rand::DblNormal(double _mean, double _sigma)
+{
+ NormalRealDist d(_mean, _sigma);
+ return d(RandGenerator());
+}
+
+//////////////////////////////////////////////////
+int32_t Rand::IntUniform(int _min, int _max)
+{
+ UniformIntDist d(_min, _max);
+
+ return d(RandGenerator());
+}
+
+//////////////////////////////////////////////////
+int32_t Rand::IntNormal(int _mean, int _sigma)
+{
+ NormalRealDist d(_mean, _sigma);
+
+ return static_cast<int32_t>(d(RandGenerator()));
+}
+
+//////////////////////////////////////////////////
+uint32_t &Rand::SeedMutable()
+{
+ // We don't seed with time for the cases when two processes are started the
+ // same time (this mostly happens with launch scripts that start a server
+ // and gui simultaneously).
+ static uint32_t seed = std::random_device {}();
+ return seed;
+}
+
+//////////////////////////////////////////////////
+GeneratorType &Rand::RandGenerator()
+{
+ static GeneratorType randGenerator(Seed());
+ return randGenerator;
+}
diff --git a/src/Rand_TEST.cc b/src/Rand_TEST.cc
new file mode 100644
index 0000000..f422a67
--- /dev/null
+++ b/src/Rand_TEST.cc
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Helpers.hh"
+#include "ignition/math/Rand.hh"
+
+using namespace ignition;
+
+//////////////////////////////////////////////////
+TEST(RandTest, Rand)
+{
+ // TODO: implement a proper random number generator test
+
+ double d = math::Rand::DblUniform(1, 2);
+ EXPECT_GE(d, 1);
+ EXPECT_LE(d, 2);
+
+ int i = math::Rand::IntUniform(1, 2);
+ EXPECT_GE(i, 1);
+ EXPECT_LE(i, 2);
+
+#ifndef _MSC_VER
+ {
+ // Test setting the random number seed
+ math::Rand::Seed(1001);
+
+ d = math::Rand::DblNormal(2, 3);
+ i = math::Rand::IntNormal(10, 5);
+
+ // \todo OSX seems to produce different results. See issue #14.
+#ifdef __APPLE__
+ EXPECT_EQ(i, 8);
+ EXPECT_NEAR(d, 5.01545, 1e-5);
+#else
+ EXPECT_EQ(i, 9);
+ EXPECT_NEAR(d, 3.00618, 1e-5);
+#endif
+ }
+#endif
+}
+
+//////////////////////////////////////////////////
+TEST(RandTest, SetSeed)
+{
+ int N = 10;
+ std::vector<int> first;
+ std::vector<int> second;
+
+ for (int i = 0; i < N; ++i)
+ {
+ math::Rand::Seed(i);
+ first.push_back(math::Rand::IntUniform(-10, 10));
+ second.push_back(math::Rand::IntUniform(-10, 10));
+ }
+
+ for (int i = 0; i < N; ++i)
+ {
+ math::Rand::Seed(i);
+ EXPECT_EQ(math::Rand::Seed(), static_cast<unsigned int>(i));
+ EXPECT_EQ(first[i], math::Rand::IntUniform(-10, 10));
+ EXPECT_EQ(second[i], math::Rand::IntUniform(-10, 10));
+ }
+}
diff --git a/src/RotationSpline.cc b/src/RotationSpline.cc
new file mode 100644
index 0000000..974fbc3
--- /dev/null
+++ b/src/RotationSpline.cc
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 "ignition/math/Quaternion.hh"
+#include "ignition/math/RotationSpline.hh"
+#include "RotationSplinePrivate.hh"
+
+using namespace ignition;
+using namespace math;
+
+/////////////////////////////////////////////////
+RotationSpline::RotationSpline()
+: dataPtr(new RotationSplinePrivate)
+{
+}
+
+/////////////////////////////////////////////////
+RotationSpline::~RotationSpline()
+{
+ delete this->dataPtr;
+ this->dataPtr = NULL;
+}
+
+/////////////////////////////////////////////////
+void RotationSpline::AddPoint(const Quaterniond &_p)
+{
+ this->dataPtr->points.push_back(_p);
+ if (this->dataPtr->autoCalc)
+ this->RecalcTangents();
+}
+
+/////////////////////////////////////////////////
+Quaterniond RotationSpline::Interpolate(double _t,
+ const bool _useShortestPath)
+{
+ // Work out which segment this is in
+ double fSeg = _t * (this->dataPtr->points.size() - 1);
+ unsigned int segIdx = (unsigned int)fSeg;
+
+ // Apportion t
+ _t = fSeg - segIdx;
+
+ return this->Interpolate(segIdx, _t, _useShortestPath);
+}
+
+/////////////////////////////////////////////////
+Quaterniond RotationSpline::Interpolate(const unsigned int _fromIndex,
+ const double _t, const bool _useShortestPath)
+{
+ // Bounds check
+ if (_fromIndex >= this->dataPtr->points.size())
+ return Quaterniond(INF_D, INF_D, INF_D, INF_D);
+
+ if ((_fromIndex + 1) == this->dataPtr->points.size())
+ {
+ // Duff request, cannot blend to nothing
+ // Just return source
+ return this->dataPtr->points[_fromIndex];
+ }
+
+ // Fast special cases
+ if (math::equal(_t, 0.0))
+ return this->dataPtr->points[_fromIndex];
+ else if (math::equal(_t, 1.0))
+ return this->dataPtr->points[_fromIndex + 1];
+
+ // double interpolation
+ // Use squad using tangents we've already set up
+ Quaterniond &p = this->dataPtr->points[_fromIndex];
+ Quaterniond &q = this->dataPtr->points[_fromIndex+1];
+ Quaterniond &a = this->dataPtr->tangents[_fromIndex];
+ Quaterniond &b = this->dataPtr->tangents[_fromIndex+1];
+
+ // NB interpolate to nearest rotation
+ return Quaterniond::Squad(_t, p, a, b, q, _useShortestPath);
+}
+
+/////////////////////////////////////////////////
+void RotationSpline::RecalcTangents()
+{
+ // ShoeMake (1987) approach
+ // Just like Catmull-Rom really, just more gnarly
+ // And no, I don't understand how to derive this!
+ //
+ // let p = point[i], pInv = p.Inverse
+ // tangent[i] = p * exp(-0.25 *
+ // (log(pInv * point[i+1]) + log(pInv * point[i-1])))
+ //
+ // Assume endpoint tangents are parallel with line with neighbour
+
+ unsigned int i;
+ bool isClosed;
+
+ size_t numPoints = this->dataPtr->points.size();
+
+ if (numPoints < 2)
+ {
+ // Can't do anything yet
+ return;
+ }
+
+ this->dataPtr->tangents.resize(numPoints);
+
+ if (this->dataPtr->points[0] == this->dataPtr->points[numPoints-1])
+ isClosed = true;
+ else
+ isClosed = false;
+
+ Quaterniond invp, part1, part2, preExp;
+ for (i = 0; i < numPoints; ++i)
+ {
+ Quaterniond &p = this->dataPtr->points[i];
+ invp = p.Inverse();
+
+ if (i == 0)
+ {
+ // special case start
+ part1 = (invp * this->dataPtr->points[i+1]).Log();
+ if (isClosed)
+ {
+ // Use numPoints-2 since numPoints-1 == end == start == this one
+ part2 = (invp * this->dataPtr->points[numPoints-2]).Log();
+ }
+ else
+ {
+ part2 = (invp * p).Log();
+ }
+ }
+ else if (i == numPoints-1)
+ {
+ // special case end
+ if (isClosed)
+ {
+ // Wrap to [1] (not [0], this is the same as end == this one)
+ part1 = (invp * this->dataPtr->points[1]).Log();
+ }
+ else
+ {
+ part1 = (invp * p).Log();
+ }
+ part2 = (invp * this->dataPtr->points[i-1]).Log();
+ }
+ else
+ {
+ part1 = (invp * this->dataPtr->points[i+1]).Log();
+ part2 = (invp * this->dataPtr->points[i-1]).Log();
+ }
+
+ preExp = (part1 + part2) * -0.25;
+ this->dataPtr->tangents[i] = p * preExp.Exp();
+ }
+}
+
+/////////////////////////////////////////////////
+const Quaterniond &RotationSpline::Point(const unsigned int _index) const
+{
+ static Quaterniond inf(INF_D, INF_D, INF_D, INF_D);
+
+ if (this->dataPtr->points.empty())
+ return inf;
+
+ return this->dataPtr->points[
+ clamp(_index, 0u, static_cast<unsigned int>(
+ this->dataPtr->points.size()-1))];
+}
+
+/////////////////////////////////////////////////
+unsigned int RotationSpline::PointCount() const
+{
+ return static_cast<unsigned int>(this->dataPtr->points.size());
+}
+
+/////////////////////////////////////////////////
+void RotationSpline::Clear()
+{
+ this->dataPtr->points.clear();
+ this->dataPtr->tangents.clear();
+}
+
+/////////////////////////////////////////////////
+bool RotationSpline::UpdatePoint(const unsigned int _index,
+ const Quaterniond &_value)
+{
+ if (_index >= this->dataPtr->points.size())
+ return false;
+
+ this->dataPtr->points[_index] = _value;
+ if (this->dataPtr->autoCalc)
+ this->RecalcTangents();
+
+ return true;
+}
+
+/////////////////////////////////////////////////
+void RotationSpline::AutoCalculate(bool _autoCalc)
+{
+ this->dataPtr->autoCalc = _autoCalc;
+}
diff --git a/src/RotationSplinePrivate.cc b/src/RotationSplinePrivate.cc
new file mode 100644
index 0000000..1396e82
--- /dev/null
+++ b/src/RotationSplinePrivate.cc
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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 "RotationSplinePrivate.hh"
+
+using namespace ignition;
+using namespace math;
+
+/////////////////////////////////////////////////
+RotationSplinePrivate::RotationSplinePrivate()
+: autoCalc(true)
+{
+}
diff --git a/src/RotationSplinePrivate.hh b/src/RotationSplinePrivate.hh
new file mode 100644
index 0000000..c668034
--- /dev/null
+++ b/src/RotationSplinePrivate.hh
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_ROTATIONSPLINEPRIVATE_HH_
+#define IGNITION_MATH_ROTATIONSPLINEPRIVATE_HH_
+
+#include <vector>
+#include "ignition/math/Quaternion.hh"
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \internal
+ /// \brief Private data for RotationSpline
+ class RotationSplinePrivate
+ {
+ /// \brief Constructor
+ public: RotationSplinePrivate();
+
+ /// \brief Automatic recalculation of tangents when control points are
+ /// updated
+ public: bool autoCalc;
+
+ /// \brief the control points
+ public: std::vector<Quaterniond> points;
+
+ /// \brief the tangents
+ public: std::vector<Quaterniond> tangents;
+ };
+ }
+}
+#endif
diff --git a/src/RotationSpline_TEST.cc b/src/RotationSpline_TEST.cc
new file mode 100644
index 0000000..57a1e6b
--- /dev/null
+++ b/src/RotationSpline_TEST.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Helpers.hh"
+#include "ignition/math/Vector3.hh"
+#include "ignition/math/Quaternion.hh"
+#include "ignition/math/RotationSpline.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(RotationSplineTest, RotationSpline)
+{
+ math::RotationSpline s;
+
+ s.AddPoint(math::Quaterniond(0, 0, 0));
+ EXPECT_EQ(static_cast<unsigned int>(1), s.PointCount());
+
+ s.Clear();
+ EXPECT_EQ(static_cast<unsigned int>(0), s.PointCount());
+
+ s.AddPoint(math::Quaterniond(0, 0, 0));
+ EXPECT_TRUE(s.Point(0) == math::Quaterniond(0, 0, 0));
+ s.AddPoint(math::Quaterniond(.1, .1, .1));
+ EXPECT_TRUE(s.Point(1) == math::Quaterniond(.1, .1, .1));
+
+ // ::UpdatePoint
+ EXPECT_NO_THROW(s.UpdatePoint(2, math::Quaterniond(.2, .2, .2)));
+ EXPECT_FALSE(s.UpdatePoint(2, math::Quaterniond(.2, .2, .2)));
+
+ EXPECT_TRUE(s.UpdatePoint(1, math::Quaterniond(.2, .2, .2)));
+ s.AutoCalculate(false);
+ EXPECT_TRUE(s.UpdatePoint(0, math::Quaterniond(
+ math::Vector3d(-.1, -.1, -.1))));
+ s.AutoCalculate(true);
+
+ // ::Interpolate
+ EXPECT_TRUE(s.Interpolate(0.5) ==
+ math::Quaterniond(0.998089, 0.0315333, 0.0427683, 0.0315333));
+
+ // ::Interpolate
+ s.AddPoint(math::Quaterniond(.4, .4, .4));
+ EXPECT_NO_THROW(s.Interpolate(4, 0.2));
+ EXPECT_FALSE(s.Interpolate(4, 0.2).IsFinite());
+
+ EXPECT_EQ(s.Interpolate(s.PointCount()-1, 0.2),
+ s.Point(s.PointCount()-1));
+ EXPECT_TRUE(s.Interpolate(1, 0.2) ==
+ math::Quaterniond(0.978787, 0.107618, 0.137159, 0.107618));
+ EXPECT_EQ(s.Interpolate(1, 0.0), s.Point(1));
+ EXPECT_EQ(s.Interpolate(1, 1.0), s.Point(2));
+}
+
+/////////////////////////////////////////////////
+TEST(RotationSplineTest, GetPoint)
+{
+ math::RotationSpline s;
+ EXPECT_NO_THROW(s.Point(0));
+ EXPECT_FALSE(s.Point(0).IsFinite());
+ EXPECT_NO_THROW(s.Point(1));
+ EXPECT_FALSE(s.Point(1).IsFinite());
+
+ s.AddPoint(math::Quaterniond(0, 0, 0));
+ EXPECT_NO_THROW(s.Point(0));
+ EXPECT_NO_THROW(s.Point(1));
+ EXPECT_TRUE(s.Point(1).IsFinite());
+}
+
+/////////////////////////////////////////////////
+TEST(RotationSplineTest, RecalcTangents)
+{
+ math::RotationSpline s;
+ s.AddPoint(math::Quaterniond(0, 0, 0));
+ s.AddPoint(math::Quaterniond(.4, .4, .4));
+ s.AddPoint(math::Quaterniond(0, 0, 0));
+
+ s.RecalcTangents();
+ EXPECT_EQ(s.Interpolate(0, 0.5),
+ math::Quaterniond(0.987225, 0.077057, 0.11624, 0.077057));
+
+ math::Quaterniond q = s.Interpolate(1, 0.5);
+ EXPECT_EQ(s.Interpolate(1, 0.5),
+ math::Quaterniond(0.987225, 0.077057, 0.11624, 0.077057));
+}
diff --git a/src/SemanticVersion.cc b/src/SemanticVersion.cc
new file mode 100644
index 0000000..2f1efba
--- /dev/null
+++ b/src/SemanticVersion.cc
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2016 Open Source Robotics Foundation
+ *
+ * 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 <sstream>
+
+#include "ignition/math/SemanticVersion.hh"
+
+using namespace ignition;
+using namespace math;
+
+namespace ignition
+{
+ namespace math
+ {
+ class SemanticVersionPrivate
+ {
+ /// \brief Major revision (incompatible api changes)
+ public: unsigned int maj = 0;
+
+ /// \brief Minor revision (backwards compatible new functionality)
+ public: unsigned int min = 0;
+
+ /// \brief Patch (bug fixes)
+ public: unsigned int patch = 0;
+
+ /// \brief Optional pre-release info. A prerelease string may be
+ /// denoted by appending a hyphen and a series of dot separated
+ /// identifiers immediately following the patch version
+ public: std::string prerelease = "";
+
+ /// \brief Optional build meta-data. Build metadata may be denoted by
+ /// appending a plus sign and a series of dot separated identifiers
+ /// immediately following the patch or pre-release version
+ public: std::string build = "";
+ };
+ }
+}
+
+/////////////////////////////////////////////////
+SemanticVersion::SemanticVersion()
+: dataPtr(new SemanticVersionPrivate())
+{
+}
+
+/////////////////////////////////////////////////
+SemanticVersion::SemanticVersion(const std::string &_versionStr)
+: dataPtr(new SemanticVersionPrivate())
+{
+ this->Parse(_versionStr);
+}
+
+/////////////////////////////////////////////////
+SemanticVersion::SemanticVersion(const unsigned int _major,
+ const unsigned int _minor,
+ const unsigned int _patch,
+ const std::string &_prerelease,
+ const std::string &_build)
+: dataPtr(new SemanticVersionPrivate())
+{
+ this->dataPtr->maj = _major;
+ this->dataPtr->min = _minor;
+ this->dataPtr->patch = _patch;
+ this->dataPtr->prerelease = _prerelease;
+ this->dataPtr->build = _build;
+}
+
+/////////////////////////////////////////////////
+SemanticVersion::SemanticVersion(const SemanticVersion &_copy)
+: dataPtr(new SemanticVersionPrivate())
+{
+ *this->dataPtr = *_copy.dataPtr;
+}
+
+/////////////////////////////////////////////////
+SemanticVersion& SemanticVersion::operator=(const SemanticVersion &_other)
+{
+ *this->dataPtr = *_other.dataPtr;
+ return *this;
+}
+
+/////////////////////////////////////////////////
+SemanticVersion::~SemanticVersion()
+{
+}
+
+/////////////////////////////////////////////////
+std::string SemanticVersion::Version() const
+{
+ std::string result = std::to_string(this->dataPtr->maj) + "." +
+ std::to_string(this->dataPtr->min) + "." +
+ std::to_string(this->dataPtr->patch);
+
+ if (!this->dataPtr->prerelease.empty())
+ result += "-" + this->dataPtr->prerelease;
+
+ if (!this->dataPtr->build.empty())
+ result += "+" + this->dataPtr->build;
+
+ return result;
+}
+
+/////////////////////////////////////////////////
+bool SemanticVersion::operator<(const SemanticVersion &_other) const
+{
+ if (this == &_other)
+ return false;
+
+ if (this->dataPtr->maj < _other.dataPtr->maj)
+ {
+ return true;
+ }
+ if (this->dataPtr->maj > _other.dataPtr->maj)
+ {
+ return false;
+ }
+ if (this->dataPtr->min < _other.dataPtr->min)
+ {
+ return true;
+ }
+ if (this->dataPtr->min > _other.dataPtr->min)
+ {
+ return false;
+ }
+ if (this->dataPtr->patch < _other.dataPtr->patch)
+ {
+ return true;
+ }
+ if (this->dataPtr->patch > _other.dataPtr->patch)
+ {
+ return false;
+ }
+
+ // If this version has prelrease and the _other doesn't, then this
+ // version is lower. We don't compare the prerelease strings because
+ // they can contain any alphanumeric values.
+ if (!this->dataPtr->prerelease.empty() && _other.dataPtr->prerelease.empty())
+ return true;
+
+ // _other is equal
+ return false;
+}
+
+/////////////////////////////////////////////////
+bool SemanticVersion::operator<=(const SemanticVersion &_other) const
+{
+ return (_other > *this) || (_other == *this);
+}
+
+/////////////////////////////////////////////////
+bool SemanticVersion::operator>(const SemanticVersion &_other) const
+{
+ return (_other < *this);
+}
+
+/////////////////////////////////////////////////
+bool SemanticVersion::operator>=(const SemanticVersion &_other) const
+{
+ return (_other < *this) || (_other == *this);
+}
+
+/////////////////////////////////////////////////
+bool SemanticVersion::operator==(const SemanticVersion &_other) const
+{
+ if (this == &_other)
+ return true;
+
+ return (_other.dataPtr->maj == this->dataPtr->maj)
+ && (_other.dataPtr->min == this->dataPtr->min)
+ && (_other.dataPtr->patch == this->dataPtr->patch);
+}
+
+/////////////////////////////////////////////////
+bool SemanticVersion::operator!=(const SemanticVersion &_other) const
+{
+ return !(*this == _other);
+}
+
+/////////////////////////////////////////////////
+unsigned int SemanticVersion::Major() const
+{
+ return this->dataPtr->maj;
+}
+
+/////////////////////////////////////////////////
+unsigned int SemanticVersion::Minor() const
+{
+ return this->dataPtr->min;
+}
+
+/////////////////////////////////////////////////
+unsigned int SemanticVersion::Patch() const
+{
+ return this->dataPtr->patch;
+}
+
+/////////////////////////////////////////////////
+std::string SemanticVersion::Prerelease() const
+{
+ return this->dataPtr->prerelease;
+}
+
+/////////////////////////////////////////////////
+std::string SemanticVersion::Build() const
+{
+ return this->dataPtr->build;
+}
+
+/////////////////////////////////////////////////
+bool SemanticVersion::Parse(const std::string &_versionStr)
+{
+ if (_versionStr.empty())
+ return false;
+
+ size_t numericEnd = _versionStr.size();
+ size_t prereleaseStart = _versionStr.find("-");
+ size_t buildStart = _versionStr.find("+");
+
+ // Build meta data, if present, must be after prerelease string, if
+ // present
+ if (buildStart != std::string::npos &&
+ prereleaseStart != std::string::npos &&
+ buildStart < prereleaseStart)
+ {
+ return false;
+ }
+
+ // Check if a prerelease version is present
+ if (prereleaseStart != std::string::npos)
+ {
+ // Set the end of the numeric (major.minor.patch) portion to the
+ // start of the prerelease
+ numericEnd = prereleaseStart;
+
+ // Check if build metadata is present
+ if (buildStart != std::string::npos)
+ {
+ // Set prerelease to the portion between "-" and "+"
+ this->dataPtr->prerelease = _versionStr.substr(numericEnd + 1,
+ buildStart - numericEnd - 1);
+
+ // Get the build metadata.
+ this->dataPtr->build = _versionStr.substr(buildStart + 1);
+ }
+ else
+ {
+ // Set prerelease to the portion between "-" and the string's end
+ this->dataPtr->prerelease = _versionStr.substr(numericEnd + 1);
+ }
+ }
+ // Check if build metadata is present
+ else if (buildStart != std::string::npos)
+ {
+ // Set the end of the numeric (major.minor.patch) portion to the
+ // start of the build metadata
+ numericEnd = buildStart;
+ // Pre-release info can't follow the build metadata.
+ this->dataPtr->build = _versionStr.substr(numericEnd + 1);
+ }
+
+ std::string numeric = _versionStr.substr(0, numericEnd);
+ std::istringstream is(numeric);
+ std::string token;
+ for (int i = 0; std::getline(is, token, '.'); ++i)
+ {
+ switch (i)
+ {
+ default:
+ case 0:
+ this->dataPtr->maj = std::stoi(token);
+ break;
+ case 1:
+ this->dataPtr->min = std::stoi(token);
+ break;
+ case 2:
+ this->dataPtr->patch = std::stoi(token);
+ break;
+ };
+ }
+ return true;
+}
diff --git a/src/SemanticVersion_TEST.cc b/src/SemanticVersion_TEST.cc
new file mode 100644
index 0000000..66f60d2
--- /dev/null
+++ b/src/SemanticVersion_TEST.cc
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2016 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/SemanticVersion.hh"
+
+using namespace ignition;
+using namespace math;
+
+/////////////////////////////////////////////////
+TEST(SemVerTest, Prerelease)
+{
+ SemanticVersion a("0.1.0");
+ SemanticVersion b("0.1.0-pr2");
+
+ EXPECT_TRUE(b < a);
+ EXPECT_TRUE(a.Prerelease().empty());
+ EXPECT_EQ(b.Prerelease(), "pr2");
+
+ EXPECT_EQ(b.Major(), a.Major());
+ EXPECT_EQ(b.Minor(), a.Minor());
+ EXPECT_EQ(b.Patch(), a.Patch());
+ EXPECT_EQ(b.Build(), a.Build());
+
+ EXPECT_EQ(a.Version(), "0.1.0");
+ EXPECT_EQ(b.Version(), "0.1.0-pr2");
+}
+
+/////////////////////////////////////////////////
+TEST(SemVerTest, Build)
+{
+ SemanticVersion a("0.1.0");
+ SemanticVersion b("0.1.0+012345");
+
+ EXPECT_TRUE(b == a);
+ EXPECT_TRUE(a.Build().empty());
+ EXPECT_EQ(b.Build(), "012345");
+
+ EXPECT_EQ(b.Major(), a.Major());
+ EXPECT_EQ(b.Minor(), a.Minor());
+ EXPECT_EQ(b.Patch(), a.Patch());
+ EXPECT_EQ(b.Prerelease(), a.Prerelease());
+
+ EXPECT_EQ(a.Version(), "0.1.0");
+ EXPECT_EQ(b.Version(), "0.1.0+012345");
+}
+
+/////////////////////////////////////////////////
+TEST(SemVerTest, PrereleaseBuild)
+{
+ SemanticVersion a("0.1.0");
+ SemanticVersion b("0.1.0-pr2");
+ SemanticVersion c("0.1.0+012345");
+ SemanticVersion d("0.1.0-pr2+012345");
+
+ EXPECT_EQ(a.Version(), "0.1.0");
+ EXPECT_EQ(b.Version(), "0.1.0-pr2");
+ EXPECT_EQ(c.Version(), "0.1.0+012345");
+ EXPECT_EQ(d.Version(), "0.1.0-pr2+012345");
+
+ EXPECT_TRUE(b < a);
+ EXPECT_TRUE(b < c);
+ EXPECT_TRUE(b == d);
+ EXPECT_TRUE(a == c);
+
+ EXPECT_EQ(a.Major(), b.Major());
+ EXPECT_EQ(a.Minor(), b.Minor());
+ EXPECT_EQ(a.Patch(), b.Patch());
+ EXPECT_TRUE(a.Prerelease().empty());
+ EXPECT_TRUE(a.Build().empty());
+
+ EXPECT_EQ(b.Major(), c.Major());
+ EXPECT_EQ(b.Minor(), c.Minor());
+ EXPECT_EQ(b.Patch(), c.Patch());
+ EXPECT_EQ(b.Prerelease(), d.Prerelease());
+
+ EXPECT_EQ(c.Major(), d.Major());
+ EXPECT_EQ(c.Minor(), d.Minor());
+ EXPECT_EQ(c.Patch(), d.Patch());
+ EXPECT_EQ(c.Build(), d.Build());
+
+ EXPECT_EQ(d.Build(), "012345");
+ EXPECT_EQ(d.Prerelease(), "pr2");
+}
+
+/////////////////////////////////////////////////
+TEST(SemVerTest, OperatorStreamOut)
+{
+ SemanticVersion a("0.1.0");
+ SemanticVersion b("0.1.0-pr2");
+ SemanticVersion c("0.1.0+012345");
+ SemanticVersion d("0.1.0-pr2+012345");
+
+ {
+ std::ostringstream stream;
+ stream << a;
+ EXPECT_EQ(stream.str(), "0.1.0");
+ }
+
+ {
+ std::ostringstream stream;
+ stream << b;
+ EXPECT_EQ(stream.str(), "0.1.0-pr2");
+ }
+
+ {
+ std::ostringstream stream;
+ stream << c;
+ EXPECT_EQ(stream.str(), "0.1.0+012345");
+ }
+
+ {
+ std::ostringstream stream;
+ stream << d;
+ EXPECT_EQ(stream.str(), "0.1.0-pr2+012345");
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(SemVerTest, Operators)
+{
+ SemanticVersion a("0.1.0");
+ SemanticVersion b("1.0.0");
+ SemanticVersion c("1.0.0");
+ SemanticVersion c2("1.0.2");
+ SemanticVersion d("0.2.0");
+
+ // check that the short form is the same as the long one
+ SemanticVersion aa("0.1");
+ EXPECT_EQ(aa.Version(), "0.1.0");
+
+ // check second constructor
+ SemanticVersion c2b(1, 0, 2);
+ EXPECT_TRUE(c2 == c2b);
+
+ EXPECT_FALSE(a < a);
+ EXPECT_FALSE(b < a);
+ EXPECT_TRUE(a < d);
+ EXPECT_FALSE(d < a);
+
+ EXPECT_TRUE(a < b);
+ EXPECT_TRUE(a <= b);
+
+ // equality
+ EXPECT_TRUE(a != b);
+ EXPECT_TRUE(b == c);
+ EXPECT_FALSE(a == b);
+ EXPECT_FALSE(a != a);
+
+ // other operators
+ EXPECT_TRUE(b <= c);
+ EXPECT_TRUE(b >= c);
+ EXPECT_TRUE(c2 > c);
+ EXPECT_FALSE(c2 < c);
+ EXPECT_TRUE(b == b);
+}
+
+/////////////////////////////////////////////////
+TEST(SemVerTest, AssignCopy)
+{
+ SemanticVersion a("0.1+pr2");
+ SemanticVersion b("0.2");
+
+ SemanticVersion aa(a);
+ EXPECT_TRUE(a == aa);
+ SemanticVersion aaa = aa;
+ EXPECT_TRUE(a == aaa);
+ // change a
+ a = b;
+ // aaa unchanged
+ EXPECT_TRUE(aa == aaa);
+ // a and aaa now different
+ EXPECT_TRUE(a != aaa);
+}
+
+/////////////////////////////////////////////////
+TEST(SemVerTest, Parse)
+{
+ SemanticVersion a;
+ EXPECT_FALSE(a.Parse(""));
+ EXPECT_FALSE(a.Parse("0.1.2+1-1"));
+}
+
+/////////////////////////////////////////////////
+TEST(SemVerTest, Constructor)
+{
+ SemanticVersion a;
+
+ EXPECT_EQ(a.Major(), 0u);
+ EXPECT_EQ(a.Minor(), 0u);
+ EXPECT_EQ(a.Patch(), 0u);
+ EXPECT_TRUE(a.Prerelease().empty());
+ EXPECT_TRUE(a.Build().empty());
+}
+
+/////////////////////////////////////////////////
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
diff --git a/src/SignalStats.cc b/src/SignalStats.cc
new file mode 100644
index 0000000..4523bef
--- /dev/null
+++ b/src/SignalStats.cc
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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 <cmath>
+#include <iostream>
+#include <ignition/math/SignalStats.hh>
+#include "SignalStatsPrivate.hh"
+
+using namespace ignition;
+using namespace math;
+
+//////////////////////////////////////////////////
+SignalStatistic::SignalStatistic()
+ : dataPtr(new SignalStatisticPrivate)
+{
+ this->dataPtr->data = 0.0;
+ this->dataPtr->extraData = 0.0;
+ this->dataPtr->count = 0;
+}
+
+//////////////////////////////////////////////////
+SignalStatistic::~SignalStatistic()
+{
+}
+
+//////////////////////////////////////////////////
+SignalStatistic::SignalStatistic(const SignalStatistic &_ss)
+ : dataPtr(_ss.dataPtr->Clone())
+{
+}
+
+//////////////////////////////////////////////////
+size_t SignalStatistic::Count() const
+{
+ return this->dataPtr->count;
+}
+
+//////////////////////////////////////////////////
+void SignalStatistic::Reset()
+{
+ this->dataPtr->data = 0;
+ this->dataPtr->count = 0;
+}
+
+//////////////////////////////////////////////////
+double SignalMaximum::Value() const
+{
+ return this->dataPtr->data;
+}
+
+//////////////////////////////////////////////////
+std::string SignalMaximum::ShortName() const
+{
+ return "max";
+}
+
+//////////////////////////////////////////////////
+void SignalMaximum::InsertData(const double _data)
+{
+ if (this->dataPtr->count == 0 || _data > this->dataPtr->data)
+ {
+ this->dataPtr->data = _data;
+ }
+ this->dataPtr->count++;
+}
+
+//////////////////////////////////////////////////
+double SignalMean::Value() const
+{
+ if (this->dataPtr->count == 0)
+ {
+ return 0;
+ }
+ return this->dataPtr->data / this->dataPtr->count;
+}
+
+//////////////////////////////////////////////////
+std::string SignalMean::ShortName() const
+{
+ return "mean";
+}
+
+//////////////////////////////////////////////////
+void SignalMean::InsertData(const double _data)
+{
+ this->dataPtr->data += _data;
+ this->dataPtr->count++;
+}
+
+//////////////////////////////////////////////////
+double SignalMinimum::Value() const
+{
+ return this->dataPtr->data;
+}
+
+//////////////////////////////////////////////////
+std::string SignalMinimum::ShortName() const
+{
+ return "min";
+}
+
+//////////////////////////////////////////////////
+void SignalMinimum::InsertData(const double _data)
+{
+ if (this->dataPtr->count == 0 || _data < this->dataPtr->data)
+ {
+ this->dataPtr->data = _data;
+ }
+ this->dataPtr->count++;
+}
+
+//////////////////////////////////////////////////
+double SignalRootMeanSquare::Value() const
+{
+ if (this->dataPtr->count == 0)
+ {
+ return 0;
+ }
+ return sqrt(this->dataPtr->data / this->dataPtr->count);
+}
+
+//////////////////////////////////////////////////
+std::string SignalRootMeanSquare::ShortName() const
+{
+ return "rms";
+}
+
+//////////////////////////////////////////////////
+void SignalRootMeanSquare::InsertData(const double _data)
+{
+ this->dataPtr->data += _data * _data;
+ this->dataPtr->count++;
+}
+
+//////////////////////////////////////////////////
+double SignalMaxAbsoluteValue::Value() const
+{
+ return this->dataPtr->data;
+}
+
+//////////////////////////////////////////////////
+std::string SignalMaxAbsoluteValue::ShortName() const
+{
+ return "maxAbs";
+}
+
+//////////////////////////////////////////////////
+void SignalMaxAbsoluteValue::InsertData(const double _data)
+{
+ double absData = std::abs(_data);
+ if (absData > this->dataPtr->data)
+ {
+ this->dataPtr->data = absData;
+ }
+ this->dataPtr->count++;
+}
+
+//////////////////////////////////////////////////
+// wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm
+// based on Knuth's algorithm
+double SignalVariance::Value() const
+{
+ if (this->dataPtr->count < 2)
+ return 0.0;
+
+ // variance = M2 / (n - 1)
+ return this->dataPtr->data / (this->dataPtr->count - 1);
+}
+
+//////////////////////////////////////////////////
+std::string SignalVariance::ShortName() const
+{
+ return "var";
+}
+
+//////////////////////////////////////////////////
+// wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm
+// based on Knuth's algorithm
+void SignalVariance::InsertData(const double _data)
+{
+ // n++
+ this->dataPtr->count++;
+
+ // delta = x - mean
+ double delta = _data - this->dataPtr->extraData;
+
+ // mean += delta / n
+ this->dataPtr->extraData += delta / this->dataPtr->count;
+
+ // M2 += delta*(x - mean)
+ this->dataPtr->data += delta * (_data - this->dataPtr->extraData);
+}
+
+//////////////////////////////////////////////////
+SignalStats::SignalStats()
+ : dataPtr(new SignalStatsPrivate)
+{
+}
+
+//////////////////////////////////////////////////
+SignalStats::~SignalStats()
+{
+}
+
+//////////////////////////////////////////////////
+SignalStats::SignalStats(const SignalStats &_ss)
+ : dataPtr(_ss.dataPtr->Clone())
+{
+}
+
+//////////////////////////////////////////////////
+size_t SignalStats::Count() const
+{
+ if (this->dataPtr->stats.empty())
+ return 0;
+
+ return this->dataPtr->stats.front()->Count();
+}
+
+//////////////////////////////////////////////////
+std::map<std::string, double> SignalStats::Map() const
+{
+ std::map<std::string, double> map;
+ for (auto const &statistic : this->dataPtr->stats)
+ {
+ map[statistic->ShortName()] = statistic->Value();
+ }
+ return map;
+}
+
+//////////////////////////////////////////////////
+void SignalStats::InsertData(const double _data)
+{
+ for (auto &statistic : this->dataPtr->stats)
+ {
+ statistic->InsertData(_data);
+ }
+}
+
+//////////////////////////////////////////////////
+bool SignalStats::InsertStatistic(const std::string &_name)
+{
+ // Check if the statistic is already inserted
+ {
+ auto map = this->Map();
+ if (map.find(_name) != map.end())
+ {
+ std::cerr << "Unable to InsertStatistic ["
+ << _name
+ << "] since it has already been inserted."
+ << std::endl;
+ return false;
+ }
+ }
+
+ SignalStatisticPtr stat;
+ if (_name == "max")
+ {
+ stat.reset(new SignalMaximum());
+ }
+ else if (_name == "maxAbs")
+ {
+ stat.reset(new SignalMaxAbsoluteValue());
+ }
+ else if (_name == "mean")
+ {
+ stat.reset(new SignalMean());
+ }
+ else if (_name == "min")
+ {
+ stat.reset(new SignalMinimum());
+ }
+ else if (_name == "rms")
+ {
+ stat.reset(new SignalRootMeanSquare());
+ }
+ else if (_name == "var")
+ {
+ stat.reset(new SignalVariance());
+ }
+ else
+ {
+ // Unrecognized name string
+ std::cerr << "Unable to InsertStatistic ["
+ << _name
+ << "] since it is an unrecognized name."
+ << std::endl;
+ return false;
+ }
+ this->dataPtr->stats.push_back(stat);
+ return true;
+}
+
+//////////////////////////////////////////////////
+bool SignalStats::InsertStatistics(const std::string &_names)
+{
+ if (_names.empty())
+ {
+ std::cerr << "Unable to InsertStatistics "
+ << "since no names were supplied."
+ << std::endl;
+ return false;
+ }
+
+ bool result = true;
+ std::vector<std::string> names;
+ // Replace the following with std::string functions
+ // boost::split(names, _names, boost::is_any_of(","));
+ // std::regex_token_iterator may be considered when it
+ // is supported by gcc
+ {
+ std::string::size_type start = 0;
+ std::string::size_type end = _names.find(',', start);
+ while (end != std::string::npos)
+ {
+ names.push_back(_names.substr(start, end-start));
+ start = end + 1;
+ end = _names.find(',', start);
+ }
+ if (start < _names.length())
+ {
+ names.push_back(_names.substr(start));
+ }
+ }
+ for (auto const &name : names)
+ {
+ result = result && this->InsertStatistic(name);
+ }
+ return result;
+}
+
+//////////////////////////////////////////////////
+void SignalStats::Reset()
+{
+ for (auto &statistic : this->dataPtr->stats)
+ {
+ statistic->Reset();
+ }
+}
+
+//////////////////////////////////////////////////
+SignalStats &SignalStats::operator=(const SignalStats &_s)
+{
+ this->dataPtr = _s.dataPtr->Clone();
+ return *this;
+}
diff --git a/src/SignalStatsPrivate.hh b/src/SignalStatsPrivate.hh
new file mode 100644
index 0000000..6c7240a
--- /dev/null
+++ b/src/SignalStatsPrivate.hh
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_SIGNALSTATSPRIVATE_HH_
+#define IGNITION_MATH_SIGNALSTATSPRIVATE_HH_
+
+#include <memory>
+#include <vector>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \brief Private data class for the SignalStatistic class.
+ class SignalStatisticPrivate
+ {
+ /// \brief Scalar representation of signal data.
+ public: double data;
+
+ /// \brief Scalar representation of extra signal data.
+ /// For example, the standard deviation statistic needs an extra variable.
+ public: double extraData;
+
+ /// \brief Count of data values in mean.
+ public: unsigned int count;
+
+ /// \brief Clone the SignalStatisticPrivate object. Used for implementing
+ /// copy semantics.
+ public: std::unique_ptr<SignalStatisticPrivate> Clone() const
+ {
+ std::unique_ptr<SignalStatisticPrivate> dataPtr(
+ new SignalStatisticPrivate(*this));
+ return dataPtr;
+ }
+ };
+
+ class SignalStatistic;
+
+ /// \def SignalStatisticPtr
+ /// \brief Shared pointer to SignalStatistic object
+ typedef std::shared_ptr<SignalStatistic> SignalStatisticPtr;
+
+ /// \def SignalStatistic_V
+ /// \brief Vector of SignalStatisticPtr
+ typedef std::vector<SignalStatisticPtr> SignalStatistic_V;
+
+ /// \brief Private data class for the SignalStats class.
+ class SignalStatsPrivate
+ {
+ /// \brief Vector of `SignalStatistic`s.
+ public: SignalStatistic_V stats;
+
+ /// \brief Clone the SignalStatsPrivate object. Used for implementing
+ /// copy semantics.
+ public: std::unique_ptr<SignalStatsPrivate> Clone() const
+ {
+ std::unique_ptr<SignalStatsPrivate> dataPtr(
+ new SignalStatsPrivate(*this));
+ return dataPtr;
+ }
+ };
+ }
+}
+#endif
diff --git a/src/SignalStats_TEST.cc b/src/SignalStats_TEST.cc
new file mode 100644
index 0000000..8024fb9
--- /dev/null
+++ b/src/SignalStats_TEST.cc
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include <ignition/math/Rand.hh>
+#include <ignition/math/SignalStats.hh>
+
+using namespace ignition;
+
+//////////////////////////////////////////////////
+TEST(SignalStatsTest, SignalMaximumConstructor)
+{
+ // Constructor
+ math::SignalMaximum max;
+ EXPECT_DOUBLE_EQ(max.Value(), 0.0);
+ EXPECT_EQ(max.Count(), 0u);
+ EXPECT_EQ(max.ShortName(), std::string("max"));
+
+ math::SignalMaximum maxCopy(max);
+
+ EXPECT_DOUBLE_EQ(max.Value(), maxCopy.Value());
+ EXPECT_EQ(max.Count(), maxCopy.Count());
+ EXPECT_EQ(max.ShortName(), maxCopy.ShortName());
+
+ // Reset
+ max.Reset();
+ EXPECT_DOUBLE_EQ(max.Value(), 0.0);
+ EXPECT_EQ(max.Count(), 0u);
+}
+
+//////////////////////////////////////////////////
+TEST(SignalStatsTest, SignalMaximumConstantValues)
+{
+ // Constant values, max should match
+ math::SignalMaximum max;
+ EXPECT_DOUBLE_EQ(max.Value(), 0.0);
+ EXPECT_EQ(max.Count(), 0u);
+
+ const double value = 3.14159;
+
+ // Loop two times to verify Reset
+ for (int j = 0; j < 2; ++j)
+ {
+ for (unsigned int i = 1; i <= 10; ++i)
+ {
+ max.InsertData(value);
+ EXPECT_DOUBLE_EQ(max.Value(), value);
+ EXPECT_EQ(max.Count(), i);
+ }
+
+ // Reset
+ max.Reset();
+ EXPECT_DOUBLE_EQ(max.Value(), 0.0);
+ EXPECT_EQ(max.Count(), 0u);
+ }
+}
+
+//////////////////////////////////////////////////
+TEST(SignalStatsTest, SignalMaximumAlternatingValues)
+{
+ // Values with alternating sign, increasing magnitude
+ // Should always match positive value
+ math::SignalMaximum max;
+ EXPECT_DOUBLE_EQ(max.Value(), 0.0);
+ EXPECT_EQ(max.Count(), 0u);
+
+ const double value = 3.14159;
+
+ // Loop two times to verify Reset
+ for (int j = 0; j < 2; ++j)
+ {
+ for (unsigned int i = 1; i <= 10; ++i)
+ {
+ max.InsertData(value * i);
+ EXPECT_DOUBLE_EQ(max.Value(), value * i);
+ max.InsertData(-value * i);
+ EXPECT_DOUBLE_EQ(max.Value(), value * i);
+ EXPECT_EQ(max.Count(), i*2);
+ }
+
+ // Reset
+ max.Reset();
+ EXPECT_DOUBLE_EQ(max.Value(), 0.0);
+ EXPECT_EQ(max.Count(), 0u);
+ }
+}
+
+//////////////////////////////////////////////////
+TEST(SignalStatsTest, SignalMean)
+{
+ {
+ // Constructor
+ math::SignalMean mean;
+ EXPECT_DOUBLE_EQ(mean.Value(), 0.0);
+ EXPECT_EQ(mean.Count(), 0u);
+ EXPECT_EQ(mean.ShortName(), std::string("mean"));
+
+ // Reset
+ mean.Reset();
+ EXPECT_DOUBLE_EQ(mean.Value(), 0.0);
+ EXPECT_EQ(mean.Count(), 0u);
+ }
+
+ {
+ // Constant values, mean should match
+ math::SignalMean mean;
+ EXPECT_DOUBLE_EQ(mean.Value(), 0.0);
+ EXPECT_EQ(mean.Count(), 0u);
+
+ const double value = 3.14159;
+
+ // Loop two times to verify Reset
+ for (int j = 0; j < 2; ++j)
+ {
+ for (unsigned int i = 1; i <= 10; ++i)
+ {
+ mean.InsertData(value);
+ EXPECT_DOUBLE_EQ(mean.Value(), value);
+ EXPECT_EQ(mean.Count(), i);
+ }
+
+ // Reset
+ mean.Reset();
+ EXPECT_DOUBLE_EQ(mean.Value(), 0.0);
+ EXPECT_EQ(mean.Count(), 0u);
+ }
+ }
+
+ {
+ // Values with alternating sign, increasing magnitude
+ // Should be zero every other time
+ math::SignalMean mean;
+ EXPECT_DOUBLE_EQ(mean.Value(), 0.0);
+ EXPECT_EQ(mean.Count(), 0u);
+
+ const double value = 3.14159;
+
+ // Loop two times to verify Reset
+ for (int j = 0; j < 2; ++j)
+ {
+ for (unsigned int i = 1; i <= 10; ++i)
+ {
+ mean.InsertData(value * i);
+ mean.InsertData(-value * i);
+ EXPECT_DOUBLE_EQ(mean.Value(), 0.0);
+ EXPECT_EQ(mean.Count(), i*2);
+ }
+
+ // Reset
+ mean.Reset();
+ EXPECT_DOUBLE_EQ(mean.Value(), 0.0);
+ EXPECT_EQ(mean.Count(), 0u);
+ }
+ }
+}
+
+//////////////////////////////////////////////////
+TEST(SignalStatsTest, SignalMinimumConstructor)
+{
+ // Constructor
+ math::SignalMinimum min;
+ EXPECT_DOUBLE_EQ(min.Value(), 0.0);
+ EXPECT_EQ(min.Count(), 0u);
+ EXPECT_EQ(min.ShortName(), std::string("min"));
+
+ // Reset
+ min.Reset();
+ EXPECT_DOUBLE_EQ(min.Value(), 0.0);
+ EXPECT_EQ(min.Count(), 0u);
+}
+
+//////////////////////////////////////////////////
+TEST(SignalStatsTest, SignalMinimumConstantValues)
+{
+ // Constant values, min should match
+ math::SignalMinimum min;
+ EXPECT_DOUBLE_EQ(min.Value(), 0.0);
+ EXPECT_EQ(min.Count(), 0u);
+
+ const double value = 3.14159;
+
+ // Loop two times to verify Reset
+ for (int j = 0; j < 2; ++j)
+ {
+ for (unsigned int i = 1; i <= 10; ++i)
+ {
+ min.InsertData(value);
+ EXPECT_DOUBLE_EQ(min.Value(), value);
+ EXPECT_EQ(min.Count(), i);
+ }
+
+ // Reset
+ min.Reset();
+ EXPECT_DOUBLE_EQ(min.Value(), 0.0);
+ EXPECT_EQ(min.Count(), 0u);
+ }
+}
+
+//////////////////////////////////////////////////
+TEST(SignalStatsTest, SignalMinimumAlternatingValues)
+{
+ // Values with alternating sign, increasing magnitude
+ // Should always match negative value
+ math::SignalMinimum min;
+ EXPECT_DOUBLE_EQ(min.Value(), 0.0);
+ EXPECT_EQ(min.Count(), 0u);
+
+ const double value = 3.14159;
+
+ // Loop two times to verify Reset
+ for (int j = 0; j < 2; ++j)
+ {
+ for (unsigned int i = 1; i <= 10; ++i)
+ {
+ min.InsertData(value * i);
+ min.InsertData(-value * i);
+ EXPECT_DOUBLE_EQ(min.Value(), -value * i);
+ EXPECT_EQ(min.Count(), i*2);
+ }
+
+ // Reset
+ min.Reset();
+ EXPECT_DOUBLE_EQ(min.Value(), 0.0);
+ EXPECT_EQ(min.Count(), 0u);
+ }
+}
+
+//////////////////////////////////////////////////
+TEST(SignalStatsTest, SignalRootMeanSquare)
+{
+ {
+ // Constructor
+ math::SignalRootMeanSquare rms;
+ EXPECT_DOUBLE_EQ(rms.Value(), 0.0);
+ EXPECT_EQ(rms.Count(), 0u);
+ EXPECT_EQ(rms.ShortName(), std::string("rms"));
+
+ // Reset
+ rms.Reset();
+ EXPECT_DOUBLE_EQ(rms.Value(), 0.0);
+ EXPECT_EQ(rms.Count(), 0u);
+ }
+
+ {
+ // Constant values, rms should match
+ math::SignalRootMeanSquare rms;
+ EXPECT_DOUBLE_EQ(rms.Value(), 0.0);
+ EXPECT_EQ(rms.Count(), 0u);
+
+ const double value = 3.14159;
+
+ // Loop two times to verify Reset
+ for (int j = 0; j < 2; ++j)
+ {
+ for (unsigned int i = 1; i <= 10; ++i)
+ {
+ rms.InsertData(value);
+ EXPECT_DOUBLE_EQ(rms.Value(), value);
+ EXPECT_EQ(rms.Count(), i);
+ }
+
+ // Reset
+ rms.Reset();
+ EXPECT_DOUBLE_EQ(rms.Value(), 0.0);
+ EXPECT_EQ(rms.Count(), 0u);
+ }
+ }
+
+ {
+ // Values with alternating sign, same magnitude
+ // rms should match absolute value every time
+ math::SignalRootMeanSquare rms;
+ EXPECT_DOUBLE_EQ(rms.Value(), 0.0);
+ EXPECT_EQ(rms.Count(), 0u);
+
+ const double value = 3.14159;
+
+ // Loop two times to verify Reset
+ for (int j = 0; j < 2; ++j)
+ {
+ for (unsigned int i = 1; i <= 10; ++i)
+ {
+ rms.InsertData(value);
+ EXPECT_DOUBLE_EQ(rms.Value(), value);
+ EXPECT_EQ(rms.Count(), i*2-1);
+
+ rms.InsertData(-value);
+ EXPECT_DOUBLE_EQ(rms.Value(), value);
+ EXPECT_EQ(rms.Count(), i*2);
+ }
+
+ // Reset
+ rms.Reset();
+ EXPECT_DOUBLE_EQ(rms.Value(), 0.0);
+ EXPECT_EQ(rms.Count(), 0u);
+ }
+ }
+}
+
+//////////////////////////////////////////////////
+TEST(SignalStatsTest, SignalMaxAbsoluteValue)
+{
+ {
+ // Constructor
+ math::SignalMaxAbsoluteValue max;
+ EXPECT_DOUBLE_EQ(max.Value(), 0.0);
+ EXPECT_EQ(max.Count(), 0u);
+ EXPECT_EQ(max.ShortName(), std::string("maxAbs"));
+
+ // Reset
+ max.Reset();
+ EXPECT_DOUBLE_EQ(max.Value(), 0.0);
+ EXPECT_EQ(max.Count(), 0u);
+ }
+
+ {
+ // Constant values, max should match
+ math::SignalMaxAbsoluteValue max;
+ EXPECT_DOUBLE_EQ(max.Value(), 0.0);
+ EXPECT_EQ(max.Count(), 0u);
+
+ const double value = 3.14159;
+
+ // Loop two times to verify Reset
+ for (int j = 0; j < 2; ++j)
+ {
+ for (unsigned int i = 1; i <= 10; ++i)
+ {
+ max.InsertData(value);
+ EXPECT_DOUBLE_EQ(max.Value(), value);
+ EXPECT_EQ(max.Count(), i);
+ }
+
+ // Reset
+ max.Reset();
+ EXPECT_DOUBLE_EQ(max.Value(), 0.0);
+ EXPECT_EQ(max.Count(), 0u);
+ }
+ }
+
+ {
+ // Values with alternating sign, increasing magnitude
+ // max should match absolute value every time
+ math::SignalMaxAbsoluteValue max;
+ EXPECT_DOUBLE_EQ(max.Value(), 0.0);
+ EXPECT_EQ(max.Count(), 0u);
+
+ const double value = 3.14159;
+
+ // Loop two times to verify Reset
+ for (int j = 0; j < 2; ++j)
+ {
+ for (unsigned int i = 1; i <= 10; ++i)
+ {
+ max.InsertData(value * i);
+ EXPECT_DOUBLE_EQ(max.Value(), value * i);
+ EXPECT_EQ(max.Count(), i*2-1);
+
+ max.InsertData(-value * i);
+ EXPECT_DOUBLE_EQ(max.Value(), value * i);
+ EXPECT_EQ(max.Count(), i*2);
+ }
+
+ // Reset
+ max.Reset();
+ EXPECT_DOUBLE_EQ(max.Value(), 0.0);
+ EXPECT_EQ(max.Count(), 0u);
+ }
+ }
+}
+
+//////////////////////////////////////////////////
+TEST(SignalStatsTest, SignalVarianceConstructor)
+{
+ // Constructor
+ math::SignalVariance var;
+ EXPECT_DOUBLE_EQ(var.Value(), 0.0);
+ EXPECT_EQ(var.Count(), 0u);
+ EXPECT_EQ(var.ShortName(), std::string("var"));
+
+ // Reset
+ var.Reset();
+ EXPECT_DOUBLE_EQ(var.Value(), 0.0);
+ EXPECT_EQ(var.Count(), 0u);
+}
+
+//////////////////////////////////////////////////
+TEST(SignalStatsTest, SignalVarianceOneValue)
+{
+ // Add one value, expect 0.0 variance
+ std::vector<double> values = {0, 1.0, 10.0, -100.0};
+ for (auto value : values)
+ {
+ math::SignalVariance var;
+ var.InsertData(value);
+ EXPECT_EQ(var.Count(), 1u);
+ EXPECT_DOUBLE_EQ(0.0, var.Value());
+
+ // Reset
+ var.Reset();
+ EXPECT_DOUBLE_EQ(0.0, var.Value());
+ EXPECT_EQ(var.Count(), 0u);
+ }
+}
+
+//////////////////////////////////////////////////
+TEST(SignalStatsTest, SignalVarianceConstantValues)
+{
+ // Constant values, expect 0.0 variance
+ math::SignalVariance var;
+ const double value = 3.14159;
+
+ // Loop two times to verify Reset
+ for (int j = 0; j < 2; ++j)
+ {
+ for (unsigned int i = 1; i <= 10; ++i)
+ {
+ var.InsertData(value);
+ EXPECT_DOUBLE_EQ(0.0, var.Value());
+ EXPECT_EQ(var.Count(), i);
+ }
+
+ // Reset
+ var.Reset();
+ EXPECT_DOUBLE_EQ(var.Value(), 0.0);
+ EXPECT_EQ(var.Count(), 0u);
+ }
+}
+
+//////////////////////////////////////////////////
+TEST(SignalStatsTest, SignalVarianceRandomValues)
+{
+ // Random normally distributed values
+ // The sample variance has the following variance:
+ // 2 variance^2 / (count - 1)
+ // en.wikipedia.org/wiki/Variance#Distribution_of_the_sample_variance
+ // We will use 5 sigma (4e-5 chance of failure)
+ math::SignalVariance var;
+ const double stdDev = 3.14159;
+ const int count = 10000;
+ const double sigma = 5.0;
+ for (int i = 0; i < count; ++i)
+ {
+ var.InsertData(math::Rand::DblNormal(0.0, stdDev));
+ }
+ const double variance = stdDev*stdDev;
+ double sampleVariance2 = 2 * variance*variance / (count - 1);
+ EXPECT_NEAR(var.Value(), variance, sigma*sqrt(sampleVariance2));
+ std::cout << "True variance " << variance
+ << ", measured variance " << var.Value()
+ << ", sigma " << sqrt(sampleVariance2)
+ << std::endl;
+
+ // Reset
+ var.Reset();
+ EXPECT_DOUBLE_EQ(var.Value(), 0.0);
+ EXPECT_EQ(var.Count(), 0u);
+}
+
+//////////////////////////////////////////////////
+TEST(SignalStatsTest, SignalStats)
+{
+ {
+ // Constructor
+ math::SignalStats stats;
+ EXPECT_TRUE(stats.Map().empty());
+ EXPECT_EQ(stats.Count(), 0u);
+
+ const math::SignalStats &stats2 = stats;
+ EXPECT_EQ(stats.Count(), stats2.Count());
+
+ // Reset
+ stats.Reset();
+ EXPECT_TRUE(stats.Map().empty());
+ EXPECT_EQ(stats.Count(), 0u);
+ }
+
+ {
+ // InsertStatistic
+ math::SignalStats stats;
+ EXPECT_TRUE(stats.Map().empty());
+
+ EXPECT_TRUE(stats.InsertStatistic("max"));
+ EXPECT_FALSE(stats.InsertStatistic("max"));
+ EXPECT_FALSE(stats.Map().empty());
+
+ EXPECT_TRUE(stats.InsertStatistic("maxAbs"));
+ EXPECT_FALSE(stats.InsertStatistic("maxAbs"));
+ EXPECT_FALSE(stats.Map().empty());
+
+ EXPECT_TRUE(stats.InsertStatistic("mean"));
+ EXPECT_FALSE(stats.InsertStatistic("mean"));
+ EXPECT_FALSE(stats.Map().empty());
+
+ EXPECT_TRUE(stats.InsertStatistic("min"));
+ EXPECT_FALSE(stats.InsertStatistic("min"));
+ EXPECT_FALSE(stats.Map().empty());
+
+ EXPECT_TRUE(stats.InsertStatistic("rms"));
+ EXPECT_FALSE(stats.InsertStatistic("rms"));
+ EXPECT_FALSE(stats.Map().empty());
+
+ EXPECT_TRUE(stats.InsertStatistic("var"));
+ EXPECT_FALSE(stats.InsertStatistic("var"));
+ EXPECT_FALSE(stats.Map().empty());
+
+ EXPECT_FALSE(stats.InsertStatistic("FakeStatistic"));
+
+ // Map with no data
+ std::map<std::string, double> map = stats.Map();
+ EXPECT_FALSE(map.empty());
+ EXPECT_EQ(map.size(), 6u);
+ EXPECT_EQ(map.count("max"), 1u);
+ EXPECT_EQ(map.count("maxAbs"), 1u);
+ EXPECT_EQ(map.count("mean"), 1u);
+ EXPECT_EQ(map.count("min"), 1u);
+ EXPECT_EQ(map.count("rms"), 1u);
+ EXPECT_EQ(map.count("var"), 1u);
+ EXPECT_EQ(map.count("FakeStatistic"), 0u);
+
+ math::SignalStats stats2(stats);
+ std::map<std::string, double> map2 = stats2.Map();
+ EXPECT_FALSE(map2.empty());
+ EXPECT_EQ(map.size(), map2.size());
+ EXPECT_EQ(map.count("max"), map2.count("max"));
+ EXPECT_EQ(map.count("maxAbs"), map2.count("maxAbs"));
+ EXPECT_EQ(map.count("mean"), map2.count("mean"));
+ EXPECT_EQ(map.count("min"), map2.count("min"));
+ EXPECT_EQ(map.count("rms"), map2.count("rms"));
+ EXPECT_EQ(map.count("var"), map2.count("var"));
+ EXPECT_EQ(map.count("FakeStatistic"), map2.count("FakeStatistic"));
+ }
+
+ {
+ // InsertStatistics
+ math::SignalStats stats;
+ EXPECT_FALSE(stats.InsertStatistics(""));
+ EXPECT_TRUE(stats.Map().empty());
+
+ EXPECT_TRUE(stats.InsertStatistics("maxAbs,rms"));
+ EXPECT_EQ(stats.Map().size(), 2u);
+ EXPECT_FALSE(stats.InsertStatistics("maxAbs,rms"));
+ EXPECT_FALSE(stats.InsertStatistics("maxAbs"));
+ EXPECT_FALSE(stats.InsertStatistics("rms"));
+ EXPECT_EQ(stats.Map().size(), 2u);
+
+ EXPECT_FALSE(stats.InsertStatistics("mean,FakeStatistic"));
+ EXPECT_EQ(stats.Map().size(), 3u);
+
+ EXPECT_FALSE(stats.InsertStatistics("var,FakeStatistic"));
+ EXPECT_EQ(stats.Map().size(), 4u);
+
+ EXPECT_FALSE(stats.InsertStatistics("max,FakeStatistic"));
+ EXPECT_EQ(stats.Map().size(), 5u);
+
+ EXPECT_FALSE(stats.InsertStatistics("min,FakeStatistic"));
+ EXPECT_EQ(stats.Map().size(), 6u);
+
+ EXPECT_FALSE(stats.InsertStatistics("FakeStatistic"));
+ EXPECT_EQ(stats.Map().size(), 6u);
+
+ // Map with no data
+ std::map<std::string, double> map = stats.Map();
+ EXPECT_FALSE(map.empty());
+ EXPECT_EQ(map.size(), 6u);
+ EXPECT_EQ(map.count("max"), 1u);
+ EXPECT_EQ(map.count("maxAbs"), 1u);
+ EXPECT_EQ(map.count("mean"), 1u);
+ EXPECT_EQ(map.count("min"), 1u);
+ EXPECT_EQ(map.count("rms"), 1u);
+ EXPECT_EQ(map.count("var"), 1u);
+ EXPECT_EQ(map.count("FakeStatistic"), 0u);
+ }
+
+ {
+ // Add some statistics
+ math::SignalStats stats;
+ EXPECT_TRUE(stats.InsertStatistics("max,maxAbs,mean,min,rms"));
+ EXPECT_EQ(stats.Map().size(), 5u);
+
+ // No data yet
+ EXPECT_EQ(stats.Count(), 0u);
+
+ // Insert data with alternating signs
+ const double value = 3.14159;
+ stats.InsertData(value);
+ stats.InsertData(-value);
+ EXPECT_EQ(stats.Count(), 2u);
+
+ {
+ std::map<std::string, double> map = stats.Map();
+ EXPECT_DOUBLE_EQ(map["max"], value);
+ EXPECT_DOUBLE_EQ(map["maxAbs"], value);
+ EXPECT_DOUBLE_EQ(map["min"], -value);
+ EXPECT_DOUBLE_EQ(map["rms"], value);
+ EXPECT_DOUBLE_EQ(map["mean"], 0.0);
+ }
+
+ // test operator=
+ {
+ math::SignalStats copy;
+ copy = stats;
+ EXPECT_EQ(copy.Count(), 2u);
+ auto map = stats.Map();
+ EXPECT_EQ(map.size(), 5u);
+ EXPECT_DOUBLE_EQ(map["max"], value);
+ EXPECT_DOUBLE_EQ(map["maxAbs"], value);
+ EXPECT_DOUBLE_EQ(map["min"], -value);
+ EXPECT_DOUBLE_EQ(map["rms"], value);
+ EXPECT_DOUBLE_EQ(map["mean"], 0.0);
+ }
+
+ stats.Reset();
+ EXPECT_EQ(stats.Map().size(), 5u);
+ EXPECT_EQ(stats.Count(), 0u);
+ {
+ std::map<std::string, double> map = stats.Map();
+ EXPECT_DOUBLE_EQ(map["max"], 0.0);
+ EXPECT_DOUBLE_EQ(map["maxAbs"], 0.0);
+ EXPECT_DOUBLE_EQ(map["min"], 0.0);
+ EXPECT_DOUBLE_EQ(map["rms"], 0.0);
+ EXPECT_DOUBLE_EQ(map["mean"], 0.0);
+ }
+ }
+}
+
diff --git a/src/SphericalCoordinates.cc b/src/SphericalCoordinates.cc
new file mode 100644
index 0000000..9beb891
--- /dev/null
+++ b/src/SphericalCoordinates.cc
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <string>
+
+#include "ignition/math/Matrix3.hh"
+#include "ignition/math/SphericalCoordinates.hh"
+
+using namespace ignition;
+using namespace math;
+
+// Parameters for EARTH_WGS84 model
+// wikipedia: World_Geodetic_System#A_new_World_Geodetic_System:_WGS_84
+
+// a: Equatorial radius. Semi-major axis of the WGS84 spheroid (meters).
+const double g_EarthWGS84AxisEquatorial = 6378137.0;
+
+// b: Polar radius. Semi-minor axis of the wgs84 spheroid (meters).
+const double g_EarthWGS84AxisPolar = 6356752.314245;
+
+// if: WGS84 inverse flattening parameter (no units)
+const double g_EarthWGS84Flattening = 1.0/298.257223563;
+
+// Radius of the Earth (meters).
+const double g_EarthRadius = 6371000.0;
+
+// Private data for the SphericalCoordinates class.
+class ignition::math::SphericalCoordinatesPrivate
+{
+ /// \brief Type of surface being used.
+ public: SphericalCoordinates::SurfaceType surfaceType;
+
+ /// \brief Latitude of reference point.
+ public: ignition::math::Angle latitudeReference;
+
+ /// \brief Longitude of reference point.
+ public: ignition::math::Angle longitudeReference;
+
+ /// \brief Elevation of reference point relative to sea level in meters.
+ public: double elevationReference;
+
+ /// \brief Heading offset, expressed as angle from East to
+ /// gazebo x-axis, or equivalently from North to gazebo y-axis.
+ public: ignition::math::Angle headingOffset;
+
+ /// \brief Semi-major axis ellipse parameter
+ public: double ellA;
+
+ /// \brief Semi-minor axis ellipse parameter
+ public: double ellB;
+
+ /// \brief Flattening ellipse parameter
+ public: double ellF;
+
+ /// \brief First eccentricity ellipse parameter
+ public: double ellE;
+
+ /// \brief Second eccentricity ellipse parameter
+ public: double ellP;
+
+ /// \brief Rotation matrix that moves ECEF to GLOBAL
+ public: ignition::math::Matrix3d rotECEFToGlobal;
+
+ /// \brief Rotation matrix that moves GLOBAL to ECEF
+ public: ignition::math::Matrix3d rotGlobalToECEF;
+
+ /// \brief Cache the ECEF position of the the origin
+ public: ignition::math::Vector3d origin;
+
+ /// \brief Cache cosine head transform
+ public: double cosHea;
+
+ /// \brief Cache sine head transform
+ public: double sinHea;
+};
+
+//////////////////////////////////////////////////
+SphericalCoordinates::SurfaceType SphericalCoordinates::Convert(
+ const std::string &_str)
+{
+ if ("EARTH_WGS84" == _str)
+ return EARTH_WGS84;
+
+ std::cerr << "SurfaceType string not recognized, "
+ << "EARTH_WGS84 returned by default" << std::endl;
+ return EARTH_WGS84;
+}
+
+//////////////////////////////////////////////////
+SphericalCoordinates::SphericalCoordinates()
+ : dataPtr(new SphericalCoordinatesPrivate)
+{
+ this->SetSurface(EARTH_WGS84);
+ this->SetElevationReference(0.0);
+}
+
+//////////////////////////////////////////////////
+SphericalCoordinates::SphericalCoordinates(const SurfaceType _type)
+ : dataPtr(new SphericalCoordinatesPrivate)
+{
+ this->SetSurface(_type);
+ this->SetElevationReference(0.0);
+}
+
+//////////////////////////////////////////////////
+SphericalCoordinates::SphericalCoordinates(const SurfaceType _type,
+ const ignition::math::Angle &_latitude,
+ const ignition::math::Angle &_longitude,
+ const double _elevation,
+ const ignition::math::Angle &_heading)
+: dataPtr(new SphericalCoordinatesPrivate)
+{
+ // Set the reference and calculate ellipse parameters
+ this->SetSurface(_type);
+
+ // Set the coordinate transform parameters
+ this->dataPtr->latitudeReference = _latitude;
+ this->dataPtr->longitudeReference = _longitude;
+ this->dataPtr->elevationReference = _elevation;
+ this->dataPtr->headingOffset = _heading;
+
+ // Generate transformation matrix
+ this->UpdateTransformationMatrix();
+}
+
+//////////////////////////////////////////////////
+SphericalCoordinates::SphericalCoordinates(const SphericalCoordinates &_sc)
+ : SphericalCoordinates()
+{
+ (*this) = _sc;
+}
+
+//////////////////////////////////////////////////
+SphericalCoordinates::~SphericalCoordinates()
+{
+}
+
+//////////////////////////////////////////////////
+SphericalCoordinates::SurfaceType SphericalCoordinates::Surface() const
+{
+ return this->dataPtr->surfaceType;
+}
+
+//////////////////////////////////////////////////
+ignition::math::Angle SphericalCoordinates::LatitudeReference() const
+{
+ return this->dataPtr->latitudeReference;
+}
+
+//////////////////////////////////////////////////
+ignition::math::Angle SphericalCoordinates::LongitudeReference() const
+{
+ return this->dataPtr->longitudeReference;
+}
+
+//////////////////////////////////////////////////
+double SphericalCoordinates::ElevationReference() const
+{
+ return this->dataPtr->elevationReference;
+}
+
+//////////////////////////////////////////////////
+ignition::math::Angle SphericalCoordinates::HeadingOffset() const
+{
+ return this->dataPtr->headingOffset;
+}
+
+//////////////////////////////////////////////////
+void SphericalCoordinates::SetSurface(const SurfaceType &_type)
+{
+ this->dataPtr->surfaceType = _type;
+
+ switch (this->dataPtr->surfaceType)
+ {
+ case EARTH_WGS84:
+ {
+ // Set the semi-major axis
+ this->dataPtr->ellA = g_EarthWGS84AxisEquatorial;
+
+ // Set the semi-minor axis
+ this->dataPtr->ellB = g_EarthWGS84AxisPolar;
+
+ // Set the flattening parameter
+ this->dataPtr->ellF = g_EarthWGS84Flattening;
+
+ // Set the first eccentricity ellipse parameter
+ // https://en.wikipedia.org/wiki/Eccentricity_(mathematics)#Ellipses
+ this->dataPtr->ellE = sqrt(1.0 -
+ std::pow(this->dataPtr->ellB, 2) / std::pow(this->dataPtr->ellA, 2));
+
+ // Set the second eccentricity ellipse parameter
+ // https://en.wikipedia.org/wiki/Eccentricity_(mathematics)#Ellipses
+ this->dataPtr->ellP = sqrt(
+ std::pow(this->dataPtr->ellA, 2) / std::pow(this->dataPtr->ellB, 2) -
+ 1.0);
+
+ break;
+ }
+ default:
+ {
+ std::cerr << "Unknown surface type["
+ << this->dataPtr->surfaceType << "]\n";
+ break;
+ }
+ }
+}
+
+//////////////////////////////////////////////////
+void SphericalCoordinates::SetLatitudeReference(
+ const ignition::math::Angle &_angle)
+{
+ this->dataPtr->latitudeReference = _angle;
+ this->UpdateTransformationMatrix();
+}
+
+//////////////////////////////////////////////////
+void SphericalCoordinates::SetLongitudeReference(
+ const ignition::math::Angle &_angle)
+{
+ this->dataPtr->longitudeReference = _angle;
+ this->UpdateTransformationMatrix();
+}
+
+//////////////////////////////////////////////////
+void SphericalCoordinates::SetElevationReference(const double _elevation)
+{
+ this->dataPtr->elevationReference = _elevation;
+ this->UpdateTransformationMatrix();
+}
+
+//////////////////////////////////////////////////
+void SphericalCoordinates::SetHeadingOffset(const ignition::math::Angle &_angle)
+{
+ this->dataPtr->headingOffset.Radian(_angle.Radian());
+ this->UpdateTransformationMatrix();
+}
+
+//////////////////////////////////////////////////
+ignition::math::Vector3d SphericalCoordinates::SphericalFromLocalPosition(
+ const ignition::math::Vector3d &_xyz) const
+{
+ ignition::math::Vector3d result =
+ this->PositionTransform(_xyz, LOCAL, SPHERICAL);
+ result.X(IGN_RTOD(result.X()));
+ result.Y(IGN_RTOD(result.Y()));
+ return result;
+}
+
+//////////////////////////////////////////////////
+ignition::math::Vector3d SphericalCoordinates::LocalFromSphericalPosition(
+ const ignition::math::Vector3d &_xyz) const
+{
+ ignition::math::Vector3d result = _xyz;
+ result.X(IGN_DTOR(result.X()));
+ result.Y(IGN_DTOR(result.Y()));
+ return this->PositionTransform(result, SPHERICAL, LOCAL);
+}
+
+//////////////////////////////////////////////////
+ignition::math::Vector3d SphericalCoordinates::GlobalFromLocalVelocity(
+ const ignition::math::Vector3d &_xyz) const
+{
+ return this->VelocityTransform(_xyz, LOCAL, GLOBAL);
+}
+
+//////////////////////////////////////////////////
+ignition::math::Vector3d SphericalCoordinates::LocalFromGlobalVelocity(
+ const ignition::math::Vector3d &_xyz) const
+{
+ return this->VelocityTransform(_xyz, GLOBAL, LOCAL);
+}
+
+//////////////////////////////////////////////////
+/// Based on Haversine formula (http://en.wikipedia.org/wiki/Haversine_formula).
+double SphericalCoordinates::Distance(const ignition::math::Angle &_latA,
+ const ignition::math::Angle &_lonA,
+ const ignition::math::Angle &_latB,
+ const ignition::math::Angle &_lonB)
+{
+ ignition::math::Angle dLat = _latB - _latA;
+ ignition::math::Angle dLon = _lonB - _lonA;
+
+ double a = sin(dLat.Radian() / 2) * sin(dLat.Radian() / 2) +
+ sin(dLon.Radian() / 2) * sin(dLon.Radian() / 2) *
+ cos(_latA.Radian()) * cos(_latB.Radian());
+
+ double c = 2 * atan2(sqrt(a), sqrt(1 - a));
+ double d = g_EarthRadius * c;
+ return d;
+}
+
+//////////////////////////////////////////////////
+void SphericalCoordinates::UpdateTransformationMatrix()
+{
+ // Cache trig results
+ double cosLat = cos(this->dataPtr->latitudeReference.Radian());
+ double sinLat = sin(this->dataPtr->latitudeReference.Radian());
+ double cosLon = cos(this->dataPtr->longitudeReference.Radian());
+ double sinLon = sin(this->dataPtr->longitudeReference.Radian());
+
+ // Create a rotation matrix that moves ECEF to GLOBAL
+ // http://www.navipedia.net/index.php/
+ // Transformations_between_ECEF_and_ENU_coordinates
+ this->dataPtr->rotECEFToGlobal = ignition::math::Matrix3d(
+ -sinLon, cosLon, 0.0,
+ -cosLon * sinLat, -sinLon * sinLat, cosLat,
+ cosLon * cosLat, sinLon * cosLat, sinLat);
+
+ // Create a rotation matrix that moves GLOBAL to ECEF
+ // http://www.navipedia.net/index.php/
+ // Transformations_between_ECEF_and_ENU_coordinates
+ this->dataPtr->rotGlobalToECEF = ignition::math::Matrix3d(
+ -sinLon, -cosLon * sinLat, cosLon * cosLat,
+ cosLon, -sinLon * sinLat, sinLon * cosLat,
+ 0, cosLat, sinLat);
+
+ // Cache heading transforms -- note that we have to negate the heading in
+ // order to preserve backward compatibility. ie. Gazebo has traditionally
+ // expressed positive angle as a CLOCKWISE rotation that takes the GLOBAL
+ // frame to the LOCAL frame. However, right hand coordinate systems require
+ // this to be expressed as an ANTI-CLOCKWISE rotation. So, we negate it.
+ this->dataPtr->cosHea = cos(-this->dataPtr->headingOffset.Radian());
+ this->dataPtr->sinHea = sin(-this->dataPtr->headingOffset.Radian());
+
+ // Cache the ECEF coordinate of the origin
+ this->dataPtr->origin = ignition::math::Vector3d(
+ this->dataPtr->latitudeReference.Radian(),
+ this->dataPtr->longitudeReference.Radian(),
+ this->dataPtr->elevationReference);
+ this->dataPtr->origin =
+ this->PositionTransform(this->dataPtr->origin, SPHERICAL, ECEF);
+}
+
+/////////////////////////////////////////////////
+ignition::math::Vector3d SphericalCoordinates::PositionTransform(
+ const ignition::math::Vector3d &_pos,
+ const CoordinateType &_in, const CoordinateType &_out) const
+{
+ ignition::math::Vector3d tmp = _pos;
+
+ // Cache trig results
+ double cosLat = cos(_pos.X());
+ double sinLat = sin(_pos.X());
+ double cosLon = cos(_pos.Y());
+ double sinLon = sin(_pos.Y());
+
+ // Radius of planet curvature (meters)
+ double curvature = 1.0 -
+ this->dataPtr->ellE * this->dataPtr->ellE * sinLat * sinLat;
+ curvature = this->dataPtr->ellA / sqrt(curvature);
+
+ // Convert whatever arrives to a more flexible ECEF coordinate
+ switch (_in)
+ {
+ // East, North, Up (ENU), note no break at end of case
+ case LOCAL:
+ {
+ tmp.X(-_pos.X() * this->dataPtr->cosHea + _pos.Y() *
+ this->dataPtr->sinHea);
+ tmp.Y(-_pos.X() * this->dataPtr->sinHea - _pos.Y() *
+ this->dataPtr->cosHea);
+ }
+
+ case GLOBAL:
+ {
+ tmp = this->dataPtr->origin + this->dataPtr->rotGlobalToECEF * tmp;
+ break;
+ }
+
+ case SPHERICAL:
+ {
+ tmp.X((_pos.Z() + curvature) * cosLat * cosLon);
+ tmp.Y((_pos.Z() + curvature) * cosLat * sinLon);
+ tmp.Z(((this->dataPtr->ellB * this->dataPtr->ellB)/
+ (this->dataPtr->ellA * this->dataPtr->ellA) *
+ curvature + _pos.Z()) * sinLat);
+ break;
+ }
+
+ // Do nothing
+ case ECEF:
+ break;
+ default:
+ {
+ std::cerr << "Invalid coordinate type[" << _in << "]\n";
+ return _pos;
+ }
+ }
+
+ // Convert ECEF to the requested output coordinate system
+ switch (_out)
+ {
+ case SPHERICAL:
+ {
+ // Convert from ECEF to SPHERICAL
+ double p = sqrt(tmp.X() * tmp.X() + tmp.Y() * tmp.Y());
+ double theta = atan((tmp.Z() * this->dataPtr->ellA) /
+ (p * this->dataPtr->ellB));
+
+ // Calculate latitude and longitude
+ double lat = atan(
+ (tmp.Z() + std::pow(this->dataPtr->ellP, 2) * this->dataPtr->ellB *
+ std::pow(sin(theta), 3)) /
+ (p - std::pow(this->dataPtr->ellE, 2) *
+ this->dataPtr->ellA * std::pow(cos(theta), 3)));
+
+ double lon = atan2(tmp.Y(), tmp.X());
+
+ // Recalculate radius of planet curvature at the current latitude.
+ double nCurvature = 1.0 - std::pow(this->dataPtr->ellE, 2) *
+ std::pow(sin(lat), 2);
+ nCurvature = this->dataPtr->ellA / sqrt(nCurvature);
+
+ tmp.X(lat);
+ tmp.Y(lon);
+
+ // Now calculate Z
+ tmp.Z(p/cos(lat) - nCurvature);
+ break;
+ }
+
+ // Convert from ECEF TO GLOBAL
+ case GLOBAL:
+ tmp = this->dataPtr->rotECEFToGlobal * (tmp - this->dataPtr->origin);
+ break;
+
+ // Convert from ECEF TO LOCAL
+ case LOCAL:
+ tmp = this->dataPtr->rotECEFToGlobal * (tmp - this->dataPtr->origin);
+
+ tmp = ignition::math::Vector3d(
+ tmp.X() * this->dataPtr->cosHea - tmp.Y() * this->dataPtr->sinHea,
+ tmp.X() * this->dataPtr->sinHea + tmp.Y() * this->dataPtr->cosHea,
+ tmp.Z());
+ break;
+
+ // Return ECEF (do nothing)
+ case ECEF:
+ break;
+
+ default:
+ std::cerr << "Unknown coordinate type[" << _out << "]\n";
+ return _pos;
+ }
+
+ return tmp;
+}
+
+//////////////////////////////////////////////////
+ignition::math::Vector3d SphericalCoordinates::VelocityTransform(
+ const ignition::math::Vector3d &_vel,
+ const CoordinateType &_in, const CoordinateType &_out) const
+{
+ // Sanity check -- velocity should not be expressed in spherical coordinates
+ if (_in == SPHERICAL || _out == SPHERICAL)
+ {
+ return _vel;
+ }
+
+ // Intermediate data type
+ ignition::math::Vector3d tmp = _vel;
+
+ // First, convert to an ECEF vector
+ switch (_in)
+ {
+ // ENU (note no break at end of case)
+ case LOCAL:
+ tmp.X(-_vel.X() * this->dataPtr->cosHea + _vel.Y() *
+ this->dataPtr->sinHea);
+ tmp.Y(-_vel.X() * this->dataPtr->sinHea - _vel.Y() *
+ this->dataPtr->cosHea);
+ // spherical
+ case GLOBAL:
+ tmp = this->dataPtr->rotGlobalToECEF * tmp;
+ break;
+ // Do nothing
+ case ECEF:
+ tmp = _vel;
+ break;
+ default:
+ std::cerr << "Unknown coordinate type[" << _in << "]\n";
+ return _vel;
+ }
+
+ // Then, convert to the request coordinate type
+ switch (_out)
+ {
+ // ECEF, do nothing
+ case ECEF:
+ break;
+
+ // Convert from ECEF to global
+ case GLOBAL:
+ tmp = this->dataPtr->rotECEFToGlobal * tmp;
+ break;
+
+ // Convert from ECEF to local
+ case LOCAL:
+ tmp = this->dataPtr->rotECEFToGlobal * tmp;
+ tmp = ignition::math::Vector3d(
+ tmp.X() * this->dataPtr->cosHea - tmp.Y() * this->dataPtr->sinHea,
+ tmp.X() * this->dataPtr->sinHea + tmp.Y() * this->dataPtr->cosHea,
+ tmp.Z());
+ break;
+
+ default:
+ std::cerr << "Unknown coordinate type[" << _out << "]\n";
+ return _vel;
+ }
+
+ return tmp;
+}
+
+//////////////////////////////////////////////////
+bool SphericalCoordinates::operator==(const SphericalCoordinates &_sc) const
+{
+ return this->Surface() == _sc.Surface() &&
+ this->LatitudeReference() == _sc.LatitudeReference() &&
+ this->LongitudeReference() == _sc.LongitudeReference() &&
+ math::equal(this->ElevationReference(), _sc.ElevationReference()) &&
+ this->HeadingOffset() == _sc.HeadingOffset();
+}
+
+//////////////////////////////////////////////////
+bool SphericalCoordinates::operator!=(const SphericalCoordinates &_sc) const
+{
+ return !(*this == _sc);
+}
+
+//////////////////////////////////////////////////
+SphericalCoordinates &SphericalCoordinates::operator=(
+ const SphericalCoordinates &_sc)
+{
+ this->SetSurface(_sc.Surface());
+ this->SetLatitudeReference(_sc.LatitudeReference());
+ this->SetLongitudeReference(_sc.LongitudeReference());
+ this->SetElevationReference(_sc.ElevationReference());
+ this->SetHeadingOffset(_sc.HeadingOffset());
+
+ // Generate transformation matrix
+ this->UpdateTransformationMatrix();
+
+ return *this;
+}
diff --git a/src/SphericalCoordinates_TEST.cc b/src/SphericalCoordinates_TEST.cc
new file mode 100644
index 0000000..b7b1159
--- /dev/null
+++ b/src/SphericalCoordinates_TEST.cc
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/SphericalCoordinates.hh"
+
+using namespace ignition;
+
+//////////////////////////////////////////////////
+// Test different constructors, default parameters
+TEST(SphericalCoordinatesTest, Constructor)
+{
+ // Default surface type
+ math::SphericalCoordinates::SurfaceType st =
+ math::SphericalCoordinates::EARTH_WGS84;
+
+ // No arguments, default parameters
+ {
+ math::SphericalCoordinates sc;
+ EXPECT_EQ(sc.Surface(), st);
+ EXPECT_EQ(sc.LatitudeReference(), ignition::math::Angle());
+ EXPECT_EQ(sc.LongitudeReference(), ignition::math::Angle());
+ EXPECT_EQ(sc.HeadingOffset(), ignition::math::Angle());
+ EXPECT_NEAR(sc.ElevationReference(), 0.0, 1e-6);
+ }
+
+ // SurfaceType argument, default parameters
+ {
+ math::SphericalCoordinates sc(st);
+ EXPECT_EQ(sc.Surface(), st);
+ EXPECT_EQ(sc.LatitudeReference(), ignition::math::Angle());
+ EXPECT_EQ(sc.LongitudeReference(), ignition::math::Angle());
+ EXPECT_EQ(sc.HeadingOffset(), ignition::math::Angle());
+ EXPECT_NEAR(sc.ElevationReference(), 0.0, 1e-6);
+ }
+
+ // All arguments
+ {
+ ignition::math::Angle lat(0.3), lon(-1.2), heading(0.5);
+ double elev = 354.1;
+ math::SphericalCoordinates sc(st, lat, lon, elev, heading);
+ EXPECT_EQ(sc.Surface(), st);
+ EXPECT_EQ(sc.LatitudeReference(), lat);
+ EXPECT_EQ(sc.LongitudeReference(), lon);
+ EXPECT_EQ(sc.HeadingOffset(), heading);
+ EXPECT_NEAR(sc.ElevationReference(), elev, 1e-6);
+
+ // Copy constructor
+ math::SphericalCoordinates sc2(sc);
+ EXPECT_EQ(sc, sc2);
+ }
+}
+
+//////////////////////////////////////////////////
+// SurfaceType Convert function
+TEST(SphericalCoordinatesTest, Convert)
+{
+ // Default surface type
+ math::SphericalCoordinates::SurfaceType st =
+ math::SphericalCoordinates::EARTH_WGS84;
+
+ EXPECT_EQ(math::SphericalCoordinates::Convert("EARTH_WGS84"), st);
+
+ EXPECT_EQ(math::SphericalCoordinates::EARTH_WGS84,
+ math::SphericalCoordinates::Convert("OTHER-COORD"));
+}
+
+//////////////////////////////////////////////////
+// Test Set functions
+TEST(SphericalCoordinatesTest, SetFunctions)
+{
+ // Default surface type
+ math::SphericalCoordinates::SurfaceType st =
+ math::SphericalCoordinates::EARTH_WGS84;
+
+ // Default parameters
+ math::SphericalCoordinates sc;
+ EXPECT_EQ(sc.Surface(), st);
+ EXPECT_EQ(sc.LatitudeReference(), ignition::math::Angle());
+ EXPECT_EQ(sc.LongitudeReference(), ignition::math::Angle());
+ EXPECT_EQ(sc.HeadingOffset(), ignition::math::Angle());
+ EXPECT_NEAR(sc.ElevationReference(), 0.0, 1e-6);
+
+ {
+ ignition::math::Angle lat(0.3), lon(-1.2), heading(0.5);
+ double elev = 354.1;
+ sc.SetSurface(st);
+ sc.SetLatitudeReference(lat);
+ sc.SetLongitudeReference(lon);
+ sc.SetHeadingOffset(heading);
+ sc.SetElevationReference(elev);
+
+ EXPECT_EQ(sc.Surface(), st);
+ EXPECT_EQ(sc.LatitudeReference(), lat);
+ EXPECT_EQ(sc.LongitudeReference(), lon);
+ EXPECT_EQ(sc.HeadingOffset(), heading);
+ EXPECT_NEAR(sc.ElevationReference(), elev, 1e-6);
+ }
+}
+
+//////////////////////////////////////////////////
+// Test coordinate transformations
+TEST(SphericalCoordinatesTest, CoordinateTransforms)
+{
+ // Default surface type
+ math::SphericalCoordinates::SurfaceType st =
+ math::SphericalCoordinates::EARTH_WGS84;
+
+ {
+ // Parameters
+ ignition::math::Angle lat(0.3), lon(-1.2),
+ heading(ignition::math::Angle::HalfPi);
+ double elev = 354.1;
+ math::SphericalCoordinates sc(st, lat, lon, elev, heading);
+
+ // Check GlobalFromLocal with heading offset of 90 degrees
+ {
+ // local frame
+ ignition::math::Vector3d xyz;
+ // east, north, up
+ ignition::math::Vector3d enu;
+
+ xyz.Set(1, 0, 0);
+ enu = sc.GlobalFromLocalVelocity(xyz);
+ EXPECT_NEAR(enu.Y(), xyz.X(), 1e-6);
+ EXPECT_NEAR(enu.X(), -xyz.Y(), 1e-6);
+ EXPECT_EQ(xyz, sc.LocalFromGlobalVelocity(enu));
+
+ xyz.Set(0, 1, 0);
+ enu = sc.GlobalFromLocalVelocity(xyz);
+ EXPECT_NEAR(enu.Y(), xyz.X(), 1e-6);
+ EXPECT_NEAR(enu.X(), -xyz.Y(), 1e-6);
+ EXPECT_EQ(xyz, sc.LocalFromGlobalVelocity(enu));
+
+ xyz.Set(1, -1, 0);
+ enu = sc.GlobalFromLocalVelocity(xyz);
+ EXPECT_NEAR(enu.Y(), xyz.X(), 1e-6);
+ EXPECT_NEAR(enu.X(), -xyz.Y(), 1e-6);
+ EXPECT_EQ(xyz, sc.LocalFromGlobalVelocity(enu));
+
+ xyz.Set(2243.52334, 556.35, 435.6553);
+ enu = sc.GlobalFromLocalVelocity(xyz);
+ EXPECT_NEAR(enu.Y(), xyz.X(), 1e-6);
+ EXPECT_NEAR(enu.X(), -xyz.Y(), 1e-6);
+ EXPECT_EQ(xyz, sc.LocalFromGlobalVelocity(enu));
+ }
+
+ // Check SphericalFromLocal
+ {
+ // local frame
+ ignition::math::Vector3d xyz;
+ // spherical coordinates
+ ignition::math::Vector3d sph;
+
+ // No offset
+ xyz.Set(0, 0, 0);
+ sph = sc.SphericalFromLocalPosition(xyz);
+ // latitude
+ EXPECT_NEAR(sph.X(), lat.Degree(), 1e-6);
+ // longitude
+ EXPECT_NEAR(sph.Y(), lon.Degree(), 1e-6);
+ // elevation
+ EXPECT_NEAR(sph.Z(), elev, 1e-6);
+
+ // 200 km offset in x (pi/2 heading offset means North). We use
+ // SphericalFromLocal, which means that xyz is a linear movement on
+ // a plane (not along the curvature of Earth). This will result in
+ // a large height offset.
+ xyz.Set(2e5, 0, 0);
+ sph = sc.SphericalFromLocalPosition(xyz);
+ // increase in latitude about 1.8 degrees
+ EXPECT_NEAR(sph.X(), lat.Degree() + 1.8, 0.008);
+ // no change in longitude
+ EXPECT_NEAR(sph.Z(), 3507.024791, 1e-6);
+
+ ignition::math::Vector3d xyz2 = sc.LocalFromSphericalPosition(sph);
+ EXPECT_EQ(xyz, xyz2);
+ }
+
+ // Check position projection
+ {
+ // WGS84 coordinate obtained from online mapping software
+ // > gdaltransform -s_srs WGS84 -t_srs EPSG:4978
+ // > latitude longitude altitude
+ // > X Y Z
+ ignition::math::Vector3d tmp;
+ ignition::math::Vector3d osrf_s(37.3877349, -122.0651166, 32.0);
+ ignition::math::Vector3d osrf_e(
+ -2693701.91434394, -4299942.14687992, 3851691.0393571);
+ ignition::math::Vector3d goog_s(37.4216719, -122.0821853, 30.0);
+ ignition::math::Vector3d goog_e(
+ -2693766.71906146, -4297199.59926038, 3854681.81878812);
+
+ // Local tangent plane coordinates (ENU = GLOBAL) coordinates of
+ // Google when OSRF is taken as the origin:
+ // > proj +ellps=WGS84 +proj=tmerc
+ // +lat_0=37.3877349 +lon_0=-122.0651166 +k=1 +x_0=0 +y_0=0
+ // > -122.0821853 37.4216719 (LON,LAT)
+ // > -1510.88 3766.64 (EAST,NORTH)
+ ignition::math::Vector3d vec(-1510.88, 3766.64, -3.29);
+
+ // Convert degrees to radians
+ osrf_s.X() *= 0.0174532925;
+ osrf_s.Y() *= 0.0174532925;
+
+ // Set the ORIGIN to be the Open Source Robotics Foundation
+ math::SphericalCoordinates sc2(st, ignition::math::Angle(osrf_s.X()),
+ ignition::math::Angle(osrf_s.Y()), osrf_s.Z(),
+ ignition::math::Angle::Zero);
+
+ // Check that SPHERICAL -> ECEF works
+ tmp = sc2.PositionTransform(osrf_s,
+ math::SphericalCoordinates::SPHERICAL,
+ math::SphericalCoordinates::ECEF);
+
+ EXPECT_NEAR(tmp.X(), osrf_e.X(), 8e-2);
+ EXPECT_NEAR(tmp.Y(), osrf_e.Y(), 8e-2);
+ EXPECT_NEAR(tmp.Z(), osrf_e.Z(), 1e-2);
+
+ // Check that ECEF -> SPHERICAL works
+ tmp = sc2.PositionTransform(tmp,
+ math::SphericalCoordinates::ECEF,
+ math::SphericalCoordinates::SPHERICAL);
+
+ EXPECT_NEAR(tmp.X(), osrf_s.X(), 1e-2);
+ EXPECT_NEAR(tmp.Y(), osrf_s.Y(), 1e-2);
+ EXPECT_NEAR(tmp.Z(), osrf_s.Z(), 1e-2);
+
+ // Check that SPHERICAL -> LOCAL works
+ tmp = sc2.LocalFromSphericalPosition(goog_s);
+ EXPECT_NEAR(tmp.X(), vec.X(), 8e-2);
+ EXPECT_NEAR(tmp.Y(), vec.Y(), 8e-2);
+ EXPECT_NEAR(tmp.Z(), vec.Z(), 1e-2);
+
+ // Check that SPHERICAL -> LOCAL -> SPHERICAL works
+ tmp = sc2.SphericalFromLocalPosition(tmp);
+ EXPECT_NEAR(tmp.X(), goog_s.X(), 8e-2);
+ EXPECT_NEAR(tmp.Y(), goog_s.Y(), 8e-2);
+ EXPECT_NEAR(tmp.Z(), goog_s.Z(), 1e-2);
+ }
+ }
+}
+
+//////////////////////////////////////////////////
+// Test distance
+TEST(SphericalCoordinatesTest, Distance)
+{
+ ignition::math::Angle latA, longA, latB, longB;
+ latA.Degree(46.250944);
+ longA.Degree(-122.249972);
+ latB.Degree(46.124953);
+ longB.Degree(-122.251683);
+ double d = math::SphericalCoordinates::Distance(latA, longA, latB, longB);
+
+ EXPECT_NEAR(14002, d, 20);
+}
+
+//////////////////////////////////////////////////
+TEST(SphericalCoordinatesTest, BadSetSurface)
+{
+ math::SphericalCoordinates sc;
+ sc.SetSurface(static_cast<math::SphericalCoordinates::SurfaceType>(2));
+ EXPECT_EQ(sc.Surface(), 2);
+}
+
+//////////////////////////////////////////////////
+TEST(SphericalCoordinatesTest, Transform)
+{
+ math::SphericalCoordinates sc;
+ math::Vector3d vel(1, 2, -4);
+ math::Vector3d result = sc.VelocityTransform(vel,
+ math::SphericalCoordinates::ECEF,
+ math::SphericalCoordinates::ECEF);
+
+ EXPECT_EQ(result, vel);
+
+ math::Vector3d pos(-1510.88, 2, -4);
+ result = sc.PositionTransform(pos,
+ math::SphericalCoordinates::ECEF,
+ math::SphericalCoordinates::GLOBAL);
+
+ EXPECT_NEAR(result.X(), 2, 1e-6);
+ EXPECT_NEAR(result.Y(), -4, 1e-6);
+ EXPECT_NEAR(result.Z(), -6379647.8799999999, 1e-6);
+
+ std::cout << "NEW POS[" << result << "]\n";
+}
+
+//////////////////////////////////////////////////
+TEST(SphericalCoordinatesTest, BadCoordinateType)
+{
+ math::SphericalCoordinates sc;
+ math::Vector3d pos(1, 2, -4);
+ math::Vector3d result = sc.PositionTransform(pos,
+ static_cast<math::SphericalCoordinates::CoordinateType>(5),
+ static_cast<math::SphericalCoordinates::CoordinateType>(6));
+
+ EXPECT_EQ(result, pos);
+
+ result = sc.PositionTransform(pos,
+ static_cast<math::SphericalCoordinates::CoordinateType>(4),
+ static_cast<math::SphericalCoordinates::CoordinateType>(6));
+
+ EXPECT_EQ(result, pos);
+
+ result = sc.VelocityTransform(pos,
+ math::SphericalCoordinates::SPHERICAL,
+ math::SphericalCoordinates::ECEF);
+ EXPECT_EQ(result, pos);
+
+ result = sc.VelocityTransform(pos,
+ math::SphericalCoordinates::ECEF,
+ math::SphericalCoordinates::SPHERICAL);
+ EXPECT_EQ(result, pos);
+
+ result = sc.VelocityTransform(pos,
+ static_cast<math::SphericalCoordinates::CoordinateType>(5),
+ math::SphericalCoordinates::ECEF);
+ EXPECT_EQ(result, pos);
+
+ result = sc.VelocityTransform(pos,
+ math::SphericalCoordinates::ECEF,
+ static_cast<math::SphericalCoordinates::CoordinateType>(5));
+ EXPECT_EQ(result, pos);
+}
+
+//////////////////////////////////////////////////
+// Test [in]equality operators.
+TEST(SphericalCoordinatesTest, EqualityOps)
+{
+ // Default surface type
+ math::SphericalCoordinates::SurfaceType st =
+ math::SphericalCoordinates::EARTH_WGS84;
+ ignition::math::Angle lat(0.3), lon(-1.2), heading(0.5);
+ double elev = 354.1;
+ math::SphericalCoordinates sc1(st, lat, lon, elev, heading);
+
+ math::SphericalCoordinates sc2(st, lat, lon, elev, heading);
+ EXPECT_TRUE(sc1 == sc2);
+ EXPECT_FALSE(sc1 != sc2);
+ math::SphericalCoordinates sc3(st, ignition::math::Angle::Zero, lon, elev,
+ heading);
+ EXPECT_FALSE(sc1 == sc3);
+ EXPECT_TRUE(sc1 != sc3);
+ math::SphericalCoordinates sc4(st, lat, ignition::math::Angle::Zero, elev,
+ heading);
+ EXPECT_FALSE(sc1 == sc4);
+ EXPECT_TRUE(sc1 != sc4);
+ math::SphericalCoordinates sc5(st, lat, lon, elev + 1, heading);
+ EXPECT_FALSE(sc1 == sc5);
+ EXPECT_TRUE(sc1 != sc5);
+ math::SphericalCoordinates sc6(st, lat, lon, elev,
+ ignition::math::Angle::Zero);
+ EXPECT_FALSE(sc1 == sc6);
+ EXPECT_TRUE(sc1 != sc6);
+}
+
+//////////////////////////////////////////////////
+// Test assignment operator.
+TEST(SphericalCoordinatesTest, AssignmentOp)
+{
+ // Default surface type
+ math::SphericalCoordinates::SurfaceType st =
+ math::SphericalCoordinates::EARTH_WGS84;
+ ignition::math::Angle lat(0.3), lon(-1.2), heading(0.5);
+ double elev = 354.1;
+ math::SphericalCoordinates sc1(st, lat, lon, elev, heading);
+
+ math::SphericalCoordinates sc2 = sc1;
+ EXPECT_EQ(sc1, sc2);
+}
diff --git a/src/Spline.cc b/src/Spline.cc
new file mode 100644
index 0000000..c54b3e3
--- /dev/null
+++ b/src/Spline.cc
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+// Note: Originally cribbed from Ogre3d. Modified to implement Cardinal
+// spline and catmull-rom spline
+
+#include "SplinePrivate.hh"
+#include "ignition/math/Helpers.hh"
+#include "ignition/math/Vector4.hh"
+#include "ignition/math/Spline.hh"
+
+using namespace ignition;
+using namespace math;
+
+///////////////////////////////////////////////////////////
+Spline::Spline()
+ : dataPtr(new SplinePrivate())
+{
+ // Set up matrix
+ this->dataPtr->autoCalc = true;
+ this->dataPtr->tension = 0.0;
+ this->dataPtr->arcLength = INF_D;
+}
+
+///////////////////////////////////////////////////////////
+Spline::~Spline()
+{
+ delete this->dataPtr;
+ this->dataPtr = NULL;
+}
+
+///////////////////////////////////////////////////////////
+void Spline::Tension(double _t)
+{
+ this->dataPtr->tension = _t;
+ if (this->dataPtr->autoCalc)
+ this->RecalcTangents();
+}
+
+///////////////////////////////////////////////////////////
+double Spline::Tension() const
+{
+ return this->dataPtr->tension;
+}
+
+///////////////////////////////////////////////////////////
+double Spline::ArcLength() const
+{
+ return this->dataPtr->arcLength;
+}
+
+///////////////////////////////////////////////////////////
+double Spline::ArcLength(const double _t) const
+{
+ unsigned int fromIndex; double tFraction;
+ if (!this->MapToSegment(_t, fromIndex, tFraction))
+ return INF_D;
+ return (this->dataPtr->cumulativeArcLengths[fromIndex] +
+ this->ArcLength(fromIndex, tFraction));
+}
+
+///////////////////////////////////////////////////////////
+double Spline::ArcLength(const unsigned int _index,
+ const double _t) const
+{
+ if (_index >= this->dataPtr->segments.size())
+ return INF_D;
+ return this->dataPtr->segments[_index].ArcLength(_t);
+}
+
+///////////////////////////////////////////////////////////
+void Spline::AddPoint(const Vector3d &_p)
+{
+ this->AddPoint(
+ ControlPoint({_p, Vector3d(INF_D, INF_D, INF_D)}), false);
+}
+
+///////////////////////////////////////////////////////////
+void Spline::AddPoint(const Vector3d &_p, const Vector3d &_t)
+{
+ this->AddPoint(
+ ControlPoint({_p, _t}), true);
+}
+
+///////////////////////////////////////////////////////////
+void Spline::AddPoint(const ControlPoint &_cp, const bool _fixed)
+{
+ this->dataPtr->points.push_back(_cp);
+ this->dataPtr->fixings.push_back(_fixed);
+ if (this->dataPtr->autoCalc)
+ this->RecalcTangents();
+ else
+ this->Rebuild();
+}
+
+///////////////////////////////////////////////////////////
+Vector3d Spline::InterpolateMthDerivative(const unsigned int _mth,
+ const double _t) const
+{
+ unsigned int fromIndex; double tFraction;
+ this->MapToSegment(_t, fromIndex, tFraction);
+ return this->InterpolateMthDerivative(fromIndex, _mth, tFraction);
+}
+
+///////////////////////////////////////////////////////////
+Vector3d Spline::InterpolateMthDerivative(const unsigned int _fromIndex,
+ const unsigned int _mth,
+ const double _t) const
+{
+ // Bounds check
+ if (_fromIndex >= this->dataPtr->points.size())
+ return Vector3d(INF_D, INF_D, INF_D);
+
+ if (_fromIndex == this->dataPtr->segments.size())
+ {
+ // Duff request, cannot blend to nothing
+ // Just return source
+ return this->dataPtr->points[_fromIndex].MthDerivative(_mth);
+ }
+
+ // Interpolate derivative
+ return this->dataPtr->segments[_fromIndex].InterpolateMthDerivative(_mth, _t);
+}
+
+///////////////////////////////////////////////////////////
+Vector3d Spline::Interpolate(const double _t) const
+{
+ return this->InterpolateMthDerivative(0, _t);
+}
+
+///////////////////////////////////////////////////////////
+Vector3d Spline::Interpolate(const unsigned int _fromIndex,
+ const double _t) const
+{
+ return this->InterpolateMthDerivative(_fromIndex, 0, _t);
+}
+
+///////////////////////////////////////////////////////////
+Vector3d Spline::InterpolateTangent(const double _t) const
+{
+ return this->InterpolateMthDerivative(1, _t);
+}
+
+///////////////////////////////////////////////////////////
+Vector3d Spline::InterpolateTangent(const unsigned int _fromIndex,
+ const double _t) const
+{
+ return this->InterpolateMthDerivative(_fromIndex, 1, _t);
+}
+
+///////////////////////////////////////////////////////////
+void Spline::RecalcTangents()
+{
+ // Catmull-Rom approach
+ //
+ // tangent[i] = 0.5 * (point[i+1] - point[i-1])
+ //
+ // Assume endpoint tangents are parallel with line with neighbour
+
+ size_t i, numPoints;
+ bool isClosed;
+
+ numPoints = this->dataPtr->points.size();
+ if (numPoints < 2)
+ {
+ // Can't do anything yet
+ return;
+ }
+
+ // Closed or open?
+ if (this->dataPtr->points[0].MthDerivative(0) ==
+ this->dataPtr->points[numPoints-1].MthDerivative(0))
+ isClosed = true;
+ else
+ isClosed = false;
+
+ double t = 1.0 - this->dataPtr->tension;
+
+ for (i = 0; i < numPoints; ++i)
+ {
+ if (!this->dataPtr->fixings[i])
+ {
+ if (i == 0)
+ {
+ // Special case start
+ if (isClosed)
+ {
+ // Use this->dataPtr->points-2 since this->dataPtr->points-1
+ // is the last point and == [0]
+ this->dataPtr->points[i].MthDerivative(1) =
+ ((this->dataPtr->points[1].MthDerivative(0) -
+ this->dataPtr->points[numPoints-2].MthDerivative(0)) * 0.5) * t;
+ }
+ else
+ {
+ this->dataPtr->points[i].MthDerivative(1) =
+ ((this->dataPtr->points[1].MthDerivative(0) -
+ this->dataPtr->points[0].MthDerivative(0)) * 0.5) * t;
+ }
+ }
+ else if (i == numPoints-1)
+ {
+ // Special case end
+ if (isClosed)
+ {
+ // Use same tangent as already calculated for [0]
+ this->dataPtr->points[i].MthDerivative(1) =
+ this->dataPtr->points[0].MthDerivative(1);
+ }
+ else
+ {
+ this->dataPtr->points[i].MthDerivative(1) =
+ ((this->dataPtr->points[i].MthDerivative(0) -
+ this->dataPtr->points[i-1].MthDerivative(0)) * 0.5) * t;
+ }
+ }
+ else
+ {
+ this->dataPtr->points[i].MthDerivative(1) =
+ ((this->dataPtr->points[i+1].MthDerivative(0) -
+ this->dataPtr->points[i-1].MthDerivative(0)) * 0.5) * t;
+ }
+ }
+ }
+ this->Rebuild();
+}
+
+///////////////////////////////////////////////////////////
+bool Spline::MapToSegment(const double _t,
+ unsigned int &_index,
+ double &_fraction) const
+{
+ _index = 0;
+ _fraction = 0.0;
+
+ // Check corner cases
+ if (this->dataPtr->segments.empty())
+ return false;
+
+ if (equal(_t, 0.0))
+ return true;
+
+ if (equal(_t, 1.0))
+ {
+ _index = static_cast<unsigned int>(this->dataPtr->segments.size()-1);
+ _fraction = 1.0;
+ return true;
+ }
+
+ // Assume linear relationship between t and arclength
+ double tArc = _t * this->dataPtr->arcLength;
+
+ // Get segment index where t would lie
+ auto it = std::lower_bound(this->dataPtr->cumulativeArcLengths.begin(),
+ this->dataPtr->cumulativeArcLengths.end(),
+ tArc);
+
+ if (it != this->dataPtr->cumulativeArcLengths.begin())
+ _index = static_cast<unsigned int>(
+ (it - this->dataPtr->cumulativeArcLengths.begin() - 1));
+
+ // Get fraction of t, but renormalized to the segment
+ _fraction = (tArc - this->dataPtr->cumulativeArcLengths[_index])
+ / this->dataPtr->segments[_index].ArcLength();
+ return true;
+}
+
+///////////////////////////////////////////////////////////
+void Spline::Rebuild()
+{
+ size_t numPoints = this->dataPtr->points.size();
+
+ if (numPoints < 2) {
+ // Can't do anything yet
+ return;
+ }
+
+ size_t numSegments = numPoints - 1;
+ this->dataPtr->segments.resize(numSegments);
+ this->dataPtr->cumulativeArcLengths.resize(numSegments);
+ for (size_t i = 0 ; i < numSegments ; ++i)
+ {
+ this->dataPtr->segments[i].SetPoints(this->dataPtr->points[i],
+ this->dataPtr->points[i+1]);
+
+ if (i > 0) {
+ this->dataPtr->cumulativeArcLengths[i] =
+ (this->dataPtr->segments[i-1].ArcLength()
+ + this->dataPtr->cumulativeArcLengths[i-1]);
+ }
+ else
+ {
+ this->dataPtr->cumulativeArcLengths[i] = 0.0;
+ }
+ }
+ this->dataPtr->arcLength = (this->dataPtr->cumulativeArcLengths.back()
+ + this->dataPtr->segments.back().ArcLength());
+}
+
+///////////////////////////////////////////////////////////
+Vector3d Spline::Point(const unsigned int _index) const
+{
+ return this->MthDerivative(_index, 0);
+}
+
+///////////////////////////////////////////////////////////
+Vector3d Spline::Tangent(const unsigned int _index) const
+{
+ return this->MthDerivative(_index, 1);
+}
+
+///////////////////////////////////////////////////////////
+Vector3d Spline::MthDerivative(const unsigned int _index,
+ const unsigned int _mth) const
+{
+ if (_index >= this->dataPtr->points.size())
+ return Vector3d(INF_D, INF_D, INF_D);
+ return this->dataPtr->points[_index].MthDerivative(_mth);
+}
+
+///////////////////////////////////////////////////////////
+size_t Spline::PointCount() const
+{
+ return this->dataPtr->points.size();
+}
+
+///////////////////////////////////////////////////////////
+void Spline::Clear()
+{
+ this->dataPtr->points.clear();
+ this->dataPtr->segments.clear();
+ this->dataPtr->fixings.clear();
+}
+
+///////////////////////////////////////////////////////////
+bool Spline::UpdatePoint(const unsigned int _index,
+ const Vector3d &_point)
+{
+ return this->UpdatePoint(_index, ControlPoint({_point}), false);
+}
+
+///////////////////////////////////////////////////////////
+bool Spline::UpdatePoint(const unsigned int _index,
+ const Vector3d &_point,
+ const Vector3d &_tangent)
+{
+ return this->UpdatePoint(_index, ControlPoint({_point, _tangent}), true);
+}
+
+///////////////////////////////////////////////////////////
+bool Spline::UpdatePoint(const unsigned int _index,
+ const ControlPoint &_point,
+ const bool _fixed)
+{
+ if (_index >= this->dataPtr->points.size())
+ return false;
+
+ this->dataPtr->points[_index].Match(_point);
+ this->dataPtr->fixings[_index] = _fixed;
+
+ if (this->dataPtr->autoCalc)
+ this->RecalcTangents();
+ else
+ this->Rebuild();
+ return true;
+}
+
+///////////////////////////////////////////////////////////
+void Spline::AutoCalculate(bool _autoCalc)
+{
+ this->dataPtr->autoCalc = _autoCalc;
+}
diff --git a/src/SplinePrivate.cc b/src/SplinePrivate.cc
new file mode 100644
index 0000000..7248fe8
--- /dev/null
+++ b/src/SplinePrivate.cc
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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 "ignition/math/Matrix4.hh"
+
+#include "SplinePrivate.hh"
+
+namespace ignition
+{
+namespace math
+{
+
+///////////////////////////////////////////////////////////
+Vector4d PolynomialPowers(const unsigned int _order,
+ const double _t)
+{
+ // It is much faster to go over this table than
+ // delving into factorials and power computations.
+ double t2 = _t * _t;
+ double t3 = t2 * _t;
+ switch (_order) {
+ case 0:
+ return Vector4d(t3, t2, _t, 1.0);
+ case 1:
+ return Vector4d(3*t2, 2*_t, 1.0, 0.0);
+ case 2:
+ return Vector4d(6*_t, 2.0, 0.0, 0.0);
+ case 3:
+ return Vector4d(6.0, 0.0, 0.0, 0.0);
+ default:
+ return Vector4d(0.0, 0.0, 0.0, 0.0);
+ }
+}
+
+///////////////////////////////////////////////////////////
+void ComputeCubicBernsteinHermiteCoeff(const ControlPoint &_startPoint,
+ const ControlPoint &_endPoint,
+ Matrix4d &_coeffs)
+{
+ // Get values and tangents
+ const Vector3d &point0 = _startPoint.MthDerivative(0);
+ const Vector3d &point1 = _endPoint.MthDerivative(0);
+ const Vector3d &tan0 = _startPoint.MthDerivative(1);
+ const Vector3d &tan1 = _endPoint.MthDerivative(1);
+
+ // Hermite basis matrix
+ const Matrix4d bmatrix(2.0, -2.0, 1.0, 1.0,
+ -3.0, 3.0, -2.0, -1.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 1.0, 0.0, 0.0, 0.0);
+
+ // Control vectors matrix
+ Matrix4d cmatrix(point0.X(), point0.Y(), point0.Z(), 1.0,
+ point1.X(), point1.Y(), point1.Z(), 1.0,
+ tan0.X(), tan0.Y(), tan0.Z(), 1.0,
+ tan1.X(), tan1.Y(), tan1.Z(), 1.0);
+
+ // Compute coefficients
+ _coeffs = bmatrix * cmatrix;
+}
+
+///////////////////////////////////////////////////////////
+IntervalCubicSpline::IntervalCubicSpline()
+ : startPoint({Vector3d::Zero, Vector3d::Zero}),
+ endPoint({Vector3d::Zero, Vector3d::Zero}),
+ coeffs(Matrix4d::Zero),
+ arcLength(0.0)
+{
+}
+
+///////////////////////////////////////////////////////////
+void IntervalCubicSpline::SetPoints(const ControlPoint &_startPoint,
+ const ControlPoint &_endPoint)
+{
+ this->startPoint = _startPoint;
+ this->endPoint = _endPoint;
+
+ ComputeCubicBernsteinHermiteCoeff(
+ this->startPoint, this->endPoint, this->coeffs);
+
+ this->startPoint.MthDerivative(2) = this->DoInterpolateMthDerivative(2, 0.0);
+ this->startPoint.MthDerivative(3) = this->DoInterpolateMthDerivative(3, 0.0);
+ this->endPoint.MthDerivative(2) = this->DoInterpolateMthDerivative(2, 1.0);
+ this->endPoint.MthDerivative(3) = this->DoInterpolateMthDerivative(3, 1.0);
+ this->arcLength = this->ArcLength(1.0);
+}
+
+///////////////////////////////////////////////////////////
+double IntervalCubicSpline::ArcLength(const double _t) const
+{
+ // Bound check
+ if (_t < 0.0 || _t > 1.0)
+ return INF_D;
+
+ // 5 Point Gauss-Legendre quadrature rule for numerical path integration
+ // TODO: generalize into a numerical integration toolkit ?
+ double w1 = 0.28444444444444444 * _t;
+ double w23 = 0.23931433524968326 * _t;
+ double w45 = 0.11846344252809456 * _t;
+ double x1 = 0.5 * _t;
+ double x2 = 0.23076534494715845 * _t;
+ double x3 = 0.7692346550528415 * _t;
+ double x4 = 0.0469100770306680 * _t;
+ double x5 = 0.9530899229693319 * _t;
+
+ double arc_length = w1 * this->InterpolateMthDerivative(1, x1).Length();
+ arc_length += w23 * this->InterpolateMthDerivative(1, x2).Length();
+ arc_length += w23 * this->InterpolateMthDerivative(1, x3).Length();
+ arc_length += w45 * this->InterpolateMthDerivative(1, x4).Length();
+ arc_length += w45 * this->InterpolateMthDerivative(1, x5).Length();
+ return arc_length;
+}
+
+///////////////////////////////////////////////////////////
+Vector3d IntervalCubicSpline::DoInterpolateMthDerivative(
+ const unsigned int _mth, const double _t) const
+{
+ Vector4d powers = PolynomialPowers(_mth, _t);
+ Vector4d interpolation = powers * this->coeffs;
+ return Vector3d(interpolation.X(), interpolation.Y(), interpolation.Z());
+}
+
+///////////////////////////////////////////////////////////
+Vector3d IntervalCubicSpline::InterpolateMthDerivative(
+ const unsigned int _mth, const double _t) const
+{
+ // Bound check
+ if (_t < 0.0 || _t > 1.0)
+ return Vector3d(INF_D, INF_D, INF_D);
+
+ if (equal(_t, 0.0))
+ return this->startPoint.MthDerivative(_mth);
+ else if (equal(_t, 1.0))
+ return this->endPoint.MthDerivative(_mth);
+
+ return this->DoInterpolateMthDerivative(_mth, _t);
+}
+
+
+
+}
+}
diff --git a/src/SplinePrivate.hh b/src/SplinePrivate.hh
new file mode 100644
index 0000000..9106f07
--- /dev/null
+++ b/src/SplinePrivate.hh
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_SPLINEPRIVATE_HH_
+#define IGNITION_MATH_SPLINEPRIVATE_HH_
+
+#include <algorithm>
+#include <vector>
+#include <ignition/math/Vector3.hh>
+#include <ignition/math/Vector4.hh>
+#include <ignition/math/Matrix4.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \brief Control point representation for
+ /// polynomial interpolation, defined in terms
+ /// of arbitrary m derivatives at such point.
+ class ControlPoint
+ {
+ /// \brief Default constructor.
+ public: ControlPoint()
+ {
+ }
+
+ /// \brief Constructor that takes the M derivatives that
+ /// define the control point.
+ /// \param[in] _initList with the M derivatives.
+ public: explicit ControlPoint(const std::vector<Vector3d> &_initList)
+ : derivatives(_initList.begin(), _initList.end())
+ {
+ }
+
+ /// \brief Matches all mth derivatives defined in \p _other
+ /// to this.
+ /// \remarks Higher order derivatives in this and not defined
+ /// in \p _other are kept.
+ /// \param[in] _other control point to be matches.
+ public: inline void Match(const ControlPoint &_other)
+ {
+ std::copy(_other.derivatives.begin(),
+ _other.derivatives.end(),
+ this->derivatives.begin());
+ }
+
+ /// \brief Checks for control point equality.
+ /// \param[in] _other control point to compare against.
+ /// \return whether this and \p _other can be seen as equal.
+ public: inline bool operator==(const ControlPoint &_other) const
+ {
+ // Workaround to compare the two vector of vectors in MSVC 2013
+ // and MSVC 2015. See
+ // https://bitbucket.org/ignitionrobotics/ign-math/issues/70
+ if (this->derivatives.size() != _other.derivatives.size())
+ return false;
+
+ for (size_t i = 0; i < this->derivatives.size(); ++i)
+ if (this->derivatives[i] != _other.derivatives[i])
+ return false;
+
+ return true;
+ }
+
+ /// \brief Gets the mth derivative of this control point.
+ /// \remarks Higher derivatives than those defined
+ /// default to [0.0, 0.0, 0.0].
+ /// \param[in] _mth derivative order.
+ /// \return The mth derivative value.
+ public: inline Vector3d MthDerivative(const unsigned int _mth) const
+ {
+ if (_mth >= this->derivatives.size())
+ return Vector3d(0.0, 0.0, 0.0);
+ return this->derivatives[_mth];
+ }
+
+ /// \brief Returns a mutable reference to the mth derivative of
+ /// this control point.
+ /// \remarks Higher derivatives than those defined
+ /// default to [0.0, 0.0, 0.0].
+ /// \param[in] _mth derivative order.
+ /// \return The mth derivative value.
+ public: inline Vector3d& MthDerivative(const unsigned int _mth)
+ {
+ if (_mth >= this->derivatives.size())
+ {
+ this->derivatives.insert(this->derivatives.end(),
+ _mth - this->derivatives.size() + 1,
+ Vector3d(0.0, 0.0, 0.0));
+ }
+ return this->derivatives[_mth];
+ }
+
+ /// \brief control point M derivatives (0 to M-1).
+ private: std::vector<Vector3d> derivatives;
+ };
+
+ /// \brief Cubic interpolator for splines defined
+ /// between each pair of control points.
+ class IntervalCubicSpline
+ {
+ /// \brief Dummy constructor.
+ public: IntervalCubicSpline();
+
+ /// \brief Sets both control points.
+ /// \param[in] _startPoint start control point.
+ /// \param[in] _endPoint end control point.
+ public: void SetPoints(const ControlPoint &_startPoint,
+ const ControlPoint &_endPoint);
+
+ /// \brief Gets the start control point.
+ /// \return the start control point.
+ public: inline const ControlPoint &StartPoint() const
+ {
+ return this->startPoint;
+ };
+
+ /// \brief Gets the end control point.
+ /// \return the end control point.
+ public: inline const ControlPoint &EndPoint() const
+ {
+ return this->endPoint;
+ };
+
+ /// \brief Interpolates the curve mth derivative at
+ /// parameter value \p _t.
+ /// \param[in] _mth order of curve derivative to interpolate.
+ /// \param[in] _t parameter value (range 0 to 1).
+ /// \return the interpolated mth derivative, or [INF, INF, INF]
+ /// on error. Use Vector3d::IsFinite() to check for an error.
+ public: Vector3d InterpolateMthDerivative(
+ const unsigned int _mth, const double _t) const;
+
+ /// \brief Gets curve arc length
+ /// \return the arc length
+ public: inline double ArcLength() const { return this->arcLength; }
+
+ /// \brief Gets curve arc length up to a given point \p _t.
+ /// \param[in] _t parameter value (range 0 to 1).
+ /// \return the arc length up to \p _t or INF on error.
+ public: double ArcLength(const double _t) const;
+
+ /// \internal
+ /// \brief Interpolates the curve mth derivative at parameter
+ /// value \p _t.
+ /// \param[in] _mth order of curve derivative to interpolate.
+ /// \param[in] _t parameter value (range 0 to 1).
+ /// \return the interpolated mth derivative of the curve.
+ private: Vector3d DoInterpolateMthDerivative(
+ const unsigned int _mth, const double _t) const;
+
+ /// \brief start control point for the curve.
+ private: ControlPoint startPoint;
+
+ /// \brief end control point for the curve.
+ private: ControlPoint endPoint;
+
+ /// \brief Bernstein-Hermite polynomial coefficients
+ /// for interpolation.
+ private: Matrix4d coeffs;
+
+ /// \brief curve arc length.
+ private: double arcLength;
+ };
+
+ /// \brief Private data for Spline class.
+ class SplinePrivate
+ {
+ /// \brief when true, the tangents are recalculated when the control
+ /// point change.
+ public: bool autoCalc;
+
+ /// \brief tension of 0 = Catmull-Rom spline, otherwise a Cardinal spline.
+ public: double tension;
+
+ /// \brief fixings for control points.
+ public: std::vector<bool> fixings;
+
+ /// \brief control points.
+ public: std::vector<ControlPoint> points;
+
+ // \brief interpolated segments.
+ public: std::vector<IntervalCubicSpline> segments;
+
+ // \brief segments arc length cumulative distribution.
+ public: std::vector<double> cumulativeArcLengths;
+
+ // \brief spline arc length.
+ public: double arcLength;
+ };
+ }
+}
+
+#endif
diff --git a/src/Spline_TEST.cc b/src/Spline_TEST.cc
new file mode 100644
index 0000000..d1d0133
--- /dev/null
+++ b/src/Spline_TEST.cc
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Vector3.hh"
+#include "ignition/math/Spline.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(SplineTest, Spline)
+{
+ math::Spline s;
+
+ s.AddPoint(math::Vector3d(0, 0, 0));
+ EXPECT_EQ(static_cast<unsigned int>(1), s.PointCount());
+
+ s.Clear();
+ EXPECT_EQ(static_cast<unsigned int>(0), s.PointCount());
+
+ s.AddPoint(math::Vector3d(0, 0, 0));
+ EXPECT_TRUE(s.Point(0) == math::Vector3d(0, 0, 0));
+ s.AddPoint(math::Vector3d(1, 1, 1));
+ EXPECT_TRUE(s.Point(1) == math::Vector3d(1, 1, 1));
+
+ // ::UpdatePoint
+ EXPECT_FALSE(s.UpdatePoint(2, math::Vector3d(2, 2, 2)));
+
+ EXPECT_TRUE(s.UpdatePoint(1, math::Vector3d(2, 2, 2)));
+ s.AutoCalculate(false);
+ EXPECT_TRUE(s.UpdatePoint(0, math::Vector3d(-1, -1, -1)));
+ s.AutoCalculate(true);
+
+ // ::Interpolate
+ EXPECT_EQ(s.Interpolate(0.0), math::Vector3d(-1, -1, -1));
+ EXPECT_EQ(s.Interpolate(0.5), math::Vector3d(0.5, 0.5, 0.5));
+ EXPECT_EQ(s.Interpolate(1.0), math::Vector3d(2, 2, 2));
+
+ // ::Interpolate
+ s.AddPoint(math::Vector3d(4, 4, 4));
+ EXPECT_EQ(s.Interpolate(1, 0.2), math::Vector3d(2.496, 2.496, 2.496));
+}
+
+/////////////////////////////////////////////////
+TEST(SplineTest, FixedTangentSpline)
+{
+ math::Spline s;
+
+ // ::AddPoint
+ s.AutoCalculate(false);
+ s.AddPoint(math::Vector3d(0, 0, 0));
+ s.AddPoint(math::Vector3d(0, 0.5, 0), math::Vector3d(0, 1, 0));
+ s.AddPoint(math::Vector3d(0.5, 1, 0), math::Vector3d(1, 0, 0));
+ s.AddPoint(math::Vector3d(1, 1, 0), math::Vector3d(1, 0, 0));
+
+ // ::UpdatePoint
+ s.UpdatePoint(0, math::Vector3d(0, 0, 0), math::Vector3d(0, 1, 0));
+
+ s.AutoCalculate(true);
+
+ s.RecalcTangents();
+
+ // ::Interpolate
+ EXPECT_EQ(s.Interpolate(0, 0.5), math::Vector3d(0, 0.25, 0));
+ EXPECT_EQ(s.InterpolateTangent(0, 0.5), math::Vector3d(0, 0.25, 0));
+ EXPECT_EQ(s.Interpolate(1, 0.5), math::Vector3d(0.125, 0.875, 0));
+ EXPECT_EQ(s.Interpolate(2, 0.5), math::Vector3d(0.75, 1, 0));
+ EXPECT_EQ(s.InterpolateTangent(2, 0.5), math::Vector3d(0.25, 0, 0));
+}
+
+/////////////////////////////////////////////////
+TEST(SplineTest, ArcLength)
+{
+ math::Spline s;
+ EXPECT_FALSE(std::isfinite(s.ArcLength()));
+ s.AddPoint(math::Vector3d(1, 1, 1), math::Vector3d(1, 1, 1));
+ EXPECT_FALSE(std::isfinite(s.ArcLength(0)));
+ s.AddPoint(math::Vector3d(3, 3, 3), math::Vector3d(1, 1, 1));
+ s.AddPoint(math::Vector3d(4, 4, 4), math::Vector3d(1, 1, 1));
+ EXPECT_NEAR(s.ArcLength(0, 1.0), 3.46410161513775, 1e-14);
+ EXPECT_NEAR(s.ArcLength(), 5.19615242270663, 1e-14);
+ EXPECT_DOUBLE_EQ(s.ArcLength(), s.ArcLength(1.0));
+ EXPECT_FALSE(std::isfinite(s.ArcLength(-1.0)));
+ EXPECT_FALSE(std::isfinite(s.ArcLength(4, 0.0)));
+}
+
+/////////////////////////////////////////////////
+TEST(SplineTest, Tension)
+{
+ math::Spline s;
+ s.Tension(0.1);
+
+ EXPECT_DOUBLE_EQ(s.Tension(), 0.1);
+}
+
+/////////////////////////////////////////////////
+TEST(SplineTest, Interpolate)
+{
+ math::Spline s;
+ EXPECT_NO_THROW(s.Interpolate(0, 0.1));
+ EXPECT_FALSE(s.Interpolate(0, 0.1).IsFinite());
+
+ s.AddPoint(math::Vector3d(0, 0, 0));
+ EXPECT_EQ(s.Interpolate(0, 0.1), math::Vector3d(0, 0, 0));
+ EXPECT_FALSE(s.InterpolateTangent(0.1).IsFinite());
+
+ s.AddPoint(math::Vector3d(1, 2, 3));
+ EXPECT_EQ(s.Interpolate(0, 0.0), s.Point(0));
+ EXPECT_FALSE(s.Interpolate(0, -0.1).IsFinite());
+ EXPECT_EQ(s.InterpolateTangent(0, 0.0), s.Tangent(0));
+
+ // Fast and slow call variations
+ EXPECT_EQ(s.Interpolate(0, 0.5), math::Vector3d(0.5, 1.0, 1.5));
+ EXPECT_EQ(s.Interpolate(0, 1.0), s.Point(1));
+ EXPECT_EQ(s.InterpolateTangent(0, 0.5), math::Vector3d(1.25, 2.5, 3.75));
+ EXPECT_EQ(s.InterpolateTangent(0, 1.0), s.Tangent(1));
+ EXPECT_EQ(s.InterpolateMthDerivative(2, 0.5), math::Vector3d(0, 0, 0));
+ EXPECT_EQ(s.InterpolateMthDerivative(2, 1.0), math::Vector3d(-3, -6, -9));
+ EXPECT_EQ(s.InterpolateMthDerivative(3, 0.5), math::Vector3d(-6, -12, -18));
+ EXPECT_EQ(s.InterpolateMthDerivative(3, 1.0), math::Vector3d(-6, -12, -18));
+ EXPECT_EQ(s.InterpolateMthDerivative(4, 0.5), math::Vector3d(0, 0, 0));
+ EXPECT_EQ(s.InterpolateMthDerivative(4, 1.0), math::Vector3d(0, 0, 0));
+}
+
+/////////////////////////////////////////////////
+TEST(SplineTest, Point)
+{
+ math::Spline s;
+ EXPECT_NO_THROW(s.Point(0));
+ EXPECT_FALSE(s.Point(0).IsFinite());
+}
+
+/////////////////////////////////////////////////
+TEST(SplineTest, Tangent)
+{
+ math::Spline s;
+ EXPECT_NO_THROW(s.Tangent(0));
+ EXPECT_FALSE(s.Tangent(0).IsFinite());
+
+ s.AddPoint(math::Vector3d(0, 0, 0));
+ EXPECT_NO_THROW(s.Tangent(0));
+ EXPECT_FALSE(s.Tangent(0).IsFinite());
+
+ s.AddPoint(math::Vector3d(1, 0, 0));
+ EXPECT_EQ(s.Tangent(0), math::Vector3d(0.5, 0, 0));
+
+ s.AddPoint(math::Vector3d(1, 1, 0), math::Vector3d(-1, 1, 0));
+ EXPECT_EQ(s.Tangent(1), math::Vector3d(0.5, 0.5, 0));
+ EXPECT_EQ(s.Tangent(2), math::Vector3d(-1, 1, 0));
+}
+
+/////////////////////////////////////////////////
+TEST(SplineTest, RecalcTangents)
+{
+ math::Spline s;
+ s.AddPoint(math::Vector3d(0, 0, 0));
+ s.AddPoint(math::Vector3d(.4, .4, .4));
+ s.AddPoint(math::Vector3d(0, 0, 0));
+
+ s.RecalcTangents();
+
+ EXPECT_EQ(s.Interpolate(0, 0.5), math::Vector3d(0.2, 0.2, 0.2));
+ EXPECT_EQ(s.Interpolate(1, 0.5), math::Vector3d(0.2, 0.2, 0.2));
+}
diff --git a/src/Temperature.cc b/src/Temperature.cc
new file mode 100644
index 0000000..711d316
--- /dev/null
+++ b/src/Temperature.cc
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2016 Open Source Robotics Foundation
+ *
+ * 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 "ignition/math/Temperature.hh"
+
+/// \brief Private data for the Temperature class.
+class ignition::math::TemperaturePrivate
+{
+ /// \brief Default constructor
+ public: TemperaturePrivate() : kelvin(0.0) {}
+
+ /// \brief Constructor
+ /// \param[in] _temp Temperature in Kelvin
+ public: explicit TemperaturePrivate(const double _temp) : kelvin(_temp) {}
+
+ /// \brief Temperature value in Kelvin.
+ public: double kelvin;
+};
+
+using namespace ignition;
+using namespace math;
+
+/////////////////////////////////////////////////
+Temperature::Temperature()
+: dataPtr(new TemperaturePrivate)
+{
+}
+
+/////////////////////////////////////////////////
+Temperature::Temperature(const double _temp)
+: dataPtr(new TemperaturePrivate(_temp))
+{
+}
+
+/////////////////////////////////////////////////
+Temperature::Temperature(const Temperature &_temp)
+: dataPtr(new TemperaturePrivate(_temp.Kelvin()))
+{
+}
+
+/////////////////////////////////////////////////
+Temperature::~Temperature()
+{
+}
+
+/////////////////////////////////////////////////
+double Temperature::KelvinToCelsius(const double _temp)
+{
+ return _temp - 273.15;
+}
+
+/////////////////////////////////////////////////
+double Temperature::KelvinToFahrenheit(const double _temp)
+{
+ return _temp * 1.8 - 459.67;
+}
+
+/////////////////////////////////////////////////
+double Temperature::CelsiusToFahrenheit(const double _temp)
+{
+ return _temp * 1.8 + 32.0;
+}
+
+/////////////////////////////////////////////////
+double Temperature::CelsiusToKelvin(const double _temp)
+{
+ return _temp + 273.15;
+}
+
+/////////////////////////////////////////////////
+double Temperature::FahrenheitToCelsius(const double _temp)
+{
+ return (_temp - 32.0) / 1.8;
+}
+
+/////////////////////////////////////////////////
+double Temperature::FahrenheitToKelvin(const double _temp)
+{
+ return (_temp + 459.67) / 1.8;
+}
+
+/////////////////////////////////////////////////
+void Temperature::SetKelvin(const double _temp)
+{
+ this->dataPtr->kelvin = _temp;
+}
+
+/////////////////////////////////////////////////
+void Temperature::SetCelsius(const double _temp)
+{
+ this->SetKelvin(CelsiusToKelvin(_temp));
+}
+
+/////////////////////////////////////////////////
+void Temperature::SetFahrenheit(const double _temp)
+{
+ this->SetKelvin(FahrenheitToKelvin(_temp));
+}
+
+/////////////////////////////////////////////////
+double Temperature::Kelvin() const
+{
+ return this->dataPtr->kelvin;
+}
+
+/////////////////////////////////////////////////
+double Temperature::Celsius() const
+{
+ return KelvinToCelsius(this->dataPtr->kelvin);
+}
+
+/////////////////////////////////////////////////
+double Temperature::Fahrenheit() const
+{
+ return KelvinToFahrenheit(this->dataPtr->kelvin);
+}
+
+/////////////////////////////////////////////////
+double Temperature::operator()() const
+{
+ return this->dataPtr->kelvin;
+}
+
+/////////////////////////////////////////////////
+Temperature &Temperature::operator=(const double _temp)
+{
+ this->SetKelvin(_temp);
+ return *this;
+}
+
+/////////////////////////////////////////////////
+Temperature &Temperature::operator=(const Temperature &_temp)
+{
+ this->SetKelvin(_temp.Kelvin());
+ return *this;
+}
+
+/////////////////////////////////////////////////
+Temperature Temperature::operator+(const double _temp)
+{
+ return this->dataPtr->kelvin + _temp;
+}
+
+/////////////////////////////////////////////////
+Temperature Temperature::operator+(const Temperature &_temp)
+{
+ return this->dataPtr->kelvin + _temp;
+}
+
+/////////////////////////////////////////////////
+const Temperature &Temperature::operator+=(const double _temp)
+{
+ this->dataPtr->kelvin += _temp;
+ return *this;
+}
+
+/////////////////////////////////////////////////
+const Temperature &Temperature::operator+=(const Temperature &_temp)
+{
+ this->dataPtr->kelvin += _temp.Kelvin();
+ return *this;
+}
+
+/////////////////////////////////////////////////
+Temperature Temperature::operator-(const double _temp)
+{
+ return this->dataPtr->kelvin - _temp;
+}
+
+/////////////////////////////////////////////////
+Temperature Temperature::operator-(const Temperature &_temp)
+{
+ return this->dataPtr->kelvin - _temp.Kelvin();
+}
+
+/////////////////////////////////////////////////
+const Temperature &Temperature::operator-=(const double _temp)
+{
+ this->dataPtr->kelvin -= _temp;
+ return *this;
+}
+
+/////////////////////////////////////////////////
+const Temperature &Temperature::operator-=(const Temperature &_temp)
+{
+ this->dataPtr->kelvin -= _temp.Kelvin();
+ return *this;
+}
+
+/////////////////////////////////////////////////
+Temperature Temperature::operator*(const double _temp)
+{
+ return Temperature(this->dataPtr->kelvin * _temp);
+}
+
+/////////////////////////////////////////////////
+Temperature Temperature::operator*(const Temperature &_temp)
+{
+ return Temperature(this->dataPtr->kelvin * _temp.Kelvin());
+}
+
+/////////////////////////////////////////////////
+const Temperature &Temperature::operator*=(const double _temp)
+{
+ this->dataPtr->kelvin *= _temp;
+ return *this;
+}
+
+/////////////////////////////////////////////////
+const Temperature &Temperature::operator*=(const Temperature &_temp)
+{
+ this->dataPtr->kelvin *= _temp.Kelvin();
+ return *this;
+}
+
+/////////////////////////////////////////////////
+Temperature Temperature::operator/(const double _temp)
+{
+ return Temperature(this->dataPtr->kelvin / _temp);
+}
+
+/////////////////////////////////////////////////
+Temperature Temperature::operator/(const Temperature &_temp)
+{
+ return Temperature(this->dataPtr->kelvin / _temp.Kelvin());
+}
+
+/////////////////////////////////////////////////
+const Temperature &Temperature::operator/=(const double _temp)
+{
+ this->dataPtr->kelvin /= _temp;
+ return *this;
+}
+
+/////////////////////////////////////////////////
+const Temperature &Temperature::operator/=(const Temperature &_temp)
+{
+ this->dataPtr->kelvin /= _temp.Kelvin();
+ return *this;
+}
+
+/////////////////////////////////////////////////
+bool Temperature::operator==(const Temperature &_temp) const
+{
+ return ignition::math::equal(this->dataPtr->kelvin, _temp.Kelvin());
+}
+
+/////////////////////////////////////////////////
+bool Temperature::operator==(const double _temp) const
+{
+ return ignition::math::equal(this->dataPtr->kelvin, _temp);
+}
+
+/////////////////////////////////////////////////
+bool Temperature::operator!=(const Temperature &_temp) const
+{
+ return !(*this == _temp);
+}
+
+/////////////////////////////////////////////////
+bool Temperature::operator!=(const double _temp) const
+{
+ return !(*this == _temp);
+}
+
+/////////////////////////////////////////////////
+bool Temperature::operator<(const Temperature &_temp) const
+{
+ return this->dataPtr->kelvin < _temp.Kelvin();
+}
+
+/////////////////////////////////////////////////
+bool Temperature::operator<(const double _temp) const
+{
+ return this->dataPtr->kelvin < _temp;
+}
+
+/////////////////////////////////////////////////
+bool Temperature::operator<=(const Temperature &_temp) const
+{
+ return this->dataPtr->kelvin <= _temp.Kelvin();
+}
+
+/////////////////////////////////////////////////
+bool Temperature::operator<=(const double _temp) const
+{
+ return this->dataPtr->kelvin <= _temp;
+}
+
+/////////////////////////////////////////////////
+bool Temperature::operator>(const Temperature &_temp) const
+{
+ return this->dataPtr->kelvin > _temp.Kelvin();
+}
+
+/////////////////////////////////////////////////
+bool Temperature::operator>(const double _temp) const
+{
+ return this->dataPtr->kelvin > _temp;
+}
+
+/////////////////////////////////////////////////
+bool Temperature::operator>=(const Temperature &_temp) const
+{
+ return this->dataPtr->kelvin >= _temp.Kelvin();
+}
+
+/////////////////////////////////////////////////
+bool Temperature::operator>=(const double _temp) const
+{
+ return this->dataPtr->kelvin >= _temp;
+}
diff --git a/src/Temperature_TEST.cc b/src/Temperature_TEST.cc
new file mode 100644
index 0000000..0cb39cc
--- /dev/null
+++ b/src/Temperature_TEST.cc
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2016 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Temperature.hh"
+
+using namespace ignition;
+using namespace math;
+
+/////////////////////////////////////////////////
+TEST(TemperatureTest, Constructor)
+{
+ Temperature temp;
+ EXPECT_NEAR(temp.Kelvin(), 0.0, 1e-6);
+
+ Temperature temp2(1.1);
+ EXPECT_NEAR(temp2.Kelvin(), 1.1, 1e-6);
+
+ Temperature temp3(temp2);
+ EXPECT_NEAR(temp3.Kelvin(), 1.1, 1e-6);
+ EXPECT_NEAR(temp3.Celsius(), -272.05, 1e-6);
+
+ EXPECT_TRUE(temp2 == temp3);
+ EXPECT_TRUE(temp2 == 1.1);
+ EXPECT_TRUE(temp2 != temp);
+ EXPECT_TRUE(temp2 != 1.2);
+
+ EXPECT_TRUE(temp < temp2);
+ EXPECT_TRUE(temp < 10.0);
+ EXPECT_TRUE(temp <= temp2);
+ EXPECT_TRUE(temp <= 0.0);
+ EXPECT_TRUE(temp <= 0.1);
+
+ EXPECT_FALSE(temp > temp2);
+ EXPECT_FALSE(temp > 80.0);
+ EXPECT_FALSE(temp >= temp2);
+ EXPECT_FALSE(temp >= 0.1);
+ EXPECT_TRUE(temp >= 0.0);
+}
+
+/////////////////////////////////////////////////
+TEST(TemperatureTest, Conversions)
+{
+ EXPECT_NEAR(Temperature::KelvinToCelsius(0), -273.15, 1e-6);
+ EXPECT_NEAR(Temperature::KelvinToFahrenheit(300), 80.33, 1e-6);
+
+ EXPECT_NEAR(Temperature::CelsiusToFahrenheit(20.0), 68.0, 1e-6);
+ EXPECT_NEAR(Temperature::CelsiusToKelvin(10.0), 283.15, 1e-6);
+
+ EXPECT_NEAR(Temperature::FahrenheitToCelsius(-40.0),
+ Temperature::CelsiusToFahrenheit(-40.0), 1e-6);
+ EXPECT_NEAR(Temperature::FahrenheitToKelvin(60.0), 288.7055, 1e-3);
+}
+
+/////////////////////////////////////////////////
+TEST(TemperatureTest, MutatorsAccessors)
+{
+ Temperature temp;
+ EXPECT_NEAR(temp.Kelvin(), 0.0, 1e-6);
+
+ temp.SetKelvin(10);
+ EXPECT_NEAR(temp.Kelvin(), 10.0, 1e-6);
+
+ temp.SetCelsius(20);
+ EXPECT_NEAR(temp(), 293.15, 1e-6);
+
+ temp.SetFahrenheit(30);
+ EXPECT_NEAR(temp.Fahrenheit(), 30.0, 1e-6);
+ EXPECT_NEAR(temp(), 272.0388889, 1e-6);
+}
+
+/////////////////////////////////////////////////
+TEST(TemperatureTest, Operators)
+{
+ Temperature temp(20);
+ EXPECT_NEAR(temp(), 20, 1e-6);
+
+ temp = 30;
+ EXPECT_NEAR(temp(), 30, 1e-6);
+
+ Temperature temp2 = temp;
+ // cppcheck-suppress knownConditionTrueFalse
+ EXPECT_TRUE(temp == temp2);
+
+ EXPECT_NEAR((temp + temp2).Kelvin(), 60, 1e-6);
+ EXPECT_NEAR((temp + 40).Kelvin(), 70, 1e-6);
+
+ EXPECT_NEAR((temp - temp2).Kelvin(), 0, 1e-6);
+ EXPECT_NEAR((temp - 20).Kelvin(), 10.0, 1e-6);
+
+ EXPECT_NEAR((temp * temp2).Kelvin(), 900, 1e-6);
+ EXPECT_NEAR((temp * 2).Kelvin(), 60.0, 1e-6);
+
+ EXPECT_NEAR((temp / temp2).Kelvin(), 1.0, 1e-6);
+ EXPECT_NEAR((temp / 2).Kelvin(), 15.0, 1e-6);
+
+ temp += temp2;
+ EXPECT_NEAR(temp.Kelvin(), 60.0, 1e-6);
+ temp -= temp2;
+ EXPECT_NEAR(temp.Kelvin(), 30.0, 1e-6);
+
+ temp += 5.0;
+ EXPECT_NEAR(temp.Kelvin(), 35.0, 1e-6);
+ temp -= 5.0;
+ EXPECT_NEAR(temp.Kelvin(), 30.0, 1e-6);
+
+ temp *= temp2;
+ EXPECT_NEAR(temp.Kelvin(), 900, 1e-6);
+ temp /= temp2;
+ EXPECT_NEAR(temp.Kelvin(), 30, 1e-6);
+
+ temp *= 4.0;
+ EXPECT_NEAR(temp.Kelvin(), 120, 1e-6);
+ temp /= 4.0;
+ EXPECT_NEAR(temp.Kelvin(), 30, 1e-6);
+
+ Temperature temp3;
+ temp3 = temp;
+ EXPECT_TRUE(temp3 == temp);
+ EXPECT_TRUE(temp3 == temp2);
+}
+
+/////////////////////////////////////////////////
+TEST(TemperatureTest, OperatorStreamOut)
+{
+ Temperature temp(55.45);
+ std::ostringstream stream;
+ stream << temp;
+ EXPECT_EQ(stream.str(), "55.45");
+}
+
+/////////////////////////////////////////////////
+TEST(TemperatureTest, OperatorStreamIn)
+{
+ Temperature temp;
+ std::istringstream stream("23.4");
+ stream >> temp;
+ EXPECT_NEAR(temp.Kelvin(), 23.4, 1e-6);
+}
+
+/////////////////////////////////////////////////
+TEST(TemperatureTest, Negative)
+{
+ Temperature temp(235.0);
+
+ Temperature temp2 = 103.0 - temp;
+ EXPECT_NEAR(temp2.Kelvin(), -132.0, 1e-6);
+
+ Temperature temp3 = 133.0 + temp2;
+ EXPECT_NEAR(temp3.Kelvin(), 1.0, 1e-6);
+
+ Temperature temp4 = 4.0 * temp3;
+ EXPECT_NEAR(temp4.Kelvin(), 4.0, 1e-6);
+
+ Temperature temp5 = 2.0 / temp3;
+ EXPECT_NEAR(temp5.Kelvin(), 2.0, 1e-6);
+}
+
+/////////////////////////////////////////////////
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/Triangle3_TEST.cc b/src/Triangle3_TEST.cc
new file mode 100644
index 0000000..bcc798e
--- /dev/null
+++ b/src/Triangle3_TEST.cc
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Triangle3.hh"
+#include "ignition/math/Helpers.hh"
+
+using namespace ignition;
+using namespace math;
+
+/////////////////////////////////////////////////
+TEST(Triangle3Test, Constructor)
+{
+ {
+ // Default constructor
+ Triangle3d tri;
+ EXPECT_EQ(tri[0], Vector3d(0, 0, 0));
+ EXPECT_EQ(tri[1], Vector3d(0, 0, 0));
+ EXPECT_EQ(tri[2], Vector3d(0, 0, 0));
+ EXPECT_FALSE(tri.Valid());
+ }
+
+ {
+ // Construct from three points
+ Triangle3d tri(Vector3d(0, 0, 0), Vector3d(0, 1, 0), Vector3d(1, 0, 0));
+
+ EXPECT_TRUE(tri.Valid());
+
+ EXPECT_EQ(tri[0], Vector3d(0, 0, 0));
+ EXPECT_EQ(tri[1], Vector3d(0, 1, 0));
+ EXPECT_EQ(tri[2], Vector3d(1, 0, 0));
+ EXPECT_NO_THROW(tri[3]);
+ EXPECT_EQ(tri[3], tri[2]);
+ }
+
+ {
+ // Construct degenerate from 3 collinear points
+ Triangle3d tri(Vector3d(0, 0, 0), Vector3d(0, 1, 0), Vector3d(0, 2, 0));
+
+ // Expect not Valid
+ EXPECT_FALSE(tri.Valid());
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(Triangle3Test, Set)
+{
+ Triangle3d tri;
+
+ tri.Set(0, Vector3d(3, 4, 1));
+ tri.Set(1, Vector3d(5, -6, 2));
+ tri.Set(2, Vector3d(7, 8, -3));
+ EXPECT_EQ(tri[0], Vector3d(3, 4, 1));
+ EXPECT_EQ(tri[1], Vector3d(5, -6, 2));
+ EXPECT_EQ(tri[2], Vector3d(7, 8, -3));
+
+ tri.Set(Vector3d(0.1, 0.2, -0.3),
+ Vector3d(0.3, -0.4, 0.5),
+ Vector3d(1.5, -2.6, 3.7));
+ EXPECT_EQ(tri[0], Vector3d(0.1, 0.2, -0.3));
+ EXPECT_EQ(tri[1], Vector3d(0.3, -0.4, 0.5));
+ EXPECT_EQ(tri[2], Vector3d(1.5, -2.6, 3.7));
+
+ EXPECT_NO_THROW(tri.Set(3, Vector3d(1.5, 2.6, 3.8)));
+ EXPECT_EQ(tri[3], Vector3d(1.5, 2.6, 3.8));
+}
+
+/////////////////////////////////////////////////
+TEST(Triangle3Test, Side)
+{
+ Triangle3d tri(Vector3d(0, 0, 0), Vector3d(0, 1, 1), Vector3d(1, 0, 2));
+
+ EXPECT_TRUE(tri.Side(0) == Line3d(0, 0, 0, 0, 1, 1));
+ EXPECT_TRUE(tri.Side(1) == Line3d(0, 1, 1, 1, 0, 2));
+ EXPECT_TRUE(tri.Side(2) == Line3d(1, 0, 2, 0, 0, 0));
+
+ EXPECT_NO_THROW(tri.Side(3));
+ EXPECT_TRUE(tri.Side(3) == tri.Side(2));
+}
+
+/////////////////////////////////////////////////
+TEST(Triangle3Test, ContainsLine)
+{
+ Triangle3d tri(Vector3d(0, 0, 0), Vector3d(0, 1, 0), Vector3d(1, 0, 0));
+
+ EXPECT_TRUE(tri.Contains(tri.Side(0)));
+ EXPECT_TRUE(tri.Contains(tri.Side(1)));
+ EXPECT_TRUE(tri.Contains(tri.Side(2)));
+
+ EXPECT_TRUE(tri.Contains(Line3d(0.1, 0.1, 0, 0.5, 0.5, 0)));
+
+ EXPECT_FALSE(tri.Contains(Line3d(0.1, 0.1, 0, 0.6, 0.6, 0)));
+ EXPECT_FALSE(tri.Contains(Line3d(-0.1, -0.1, 0, 0.5, 0.5, 0)));
+ EXPECT_FALSE(tri.Contains(Line3d(0.1, 0.1, 0.1, 0.5, 0.5, 0.1)));
+ EXPECT_FALSE(tri.Contains(Line3d(0.1, 0.1, -0.1, 0.1, 0.1, 0.1)));
+}
+
+/////////////////////////////////////////////////
+TEST(Triangle3Test, Intersects)
+{
+ Vector3d pt1;
+ Triangle3d tri(Vector3d(0, 0, 0),
+ Vector3d(0, 1, 0),
+ Vector3d(1, 0, 0));
+
+ EXPECT_TRUE(tri.Intersects(tri.Side(0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0, 0, 0));
+
+ EXPECT_TRUE(tri.Intersects(tri.Side(1), pt1));
+ EXPECT_EQ(pt1, Vector3d(0, 1, 0));
+
+ EXPECT_TRUE(tri.Intersects(tri.Side(2), pt1));
+ EXPECT_EQ(pt1, Vector3d(1, 0, 0));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(0.1, 0.1, 0, 0.5, 0.5, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.1, 0.1, 0));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(0.1, 0.1, 0, 0.6, 0.6, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.5, 0.5, 0));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(0.6, 0.6, 0, 0.1, 0.1, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.5, 0.5, 0));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(0.1, 0.1, 0, -0.6, 0.1, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.0, 0.1, 0));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(0.1, 0.1, 0, 0.1, -0.1, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.1, 0, 0));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(-0.1, -0.1, 0, 0.5, 0.5, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.0, 0.0, 0));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(-2, -2, 0, 0.2, 0.2, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.0, 0.0, 0));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(0.1, 0.1, -1, 0.1, 0.1, 1), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.1, 0.1, 0));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(0.1, 0.1, -1, 0.3, 0.3, 1), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.2, 0.2, 0));
+
+ EXPECT_FALSE(tri.Intersects(Line3d(-0.1, 0, 0, -0.1, 1, 0), pt1));
+
+ // A flat triangle raised along the z-axis
+ {
+ tri.Set(Vector3d(0, 0, 0.5),
+ Vector3d(0, 1, 0.5),
+ Vector3d(1, 0, 0.5));
+ EXPECT_TRUE(tri.Intersects(Line3d(0.5, 0.5, 2, 0.5, 0.5, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.5, 0.5, 0.5));
+ }
+
+ // An angled triangle
+ {
+ tri.Set(Vector3d(0, 0, 1),
+ Vector3d(1, 0, 0),
+ Vector3d(0, 1, 0));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(1, 0, 2, 1, 0, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(1, 0, 0));
+
+ EXPECT_FALSE(tri.Intersects(Line3d(1.1, 0, 2, 1.1, 0, 0), pt1));
+ EXPECT_FALSE(tri.Intersects(Line3d(0, 1.1, 2, 0, 1.1, 0), pt1));
+ EXPECT_FALSE(tri.Intersects(Line3d(-0.1, 0, 2, -0.1, 0, 0), pt1));
+ EXPECT_FALSE(tri.Intersects(Line3d(0.51, 0.51, 2, 0.51, 0.51, 0), pt1));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(0, 1, 2, 0, 1, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0, 1, 0));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(0, 0, 2, 0, 0, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0, 0, 1.0));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(0.1, 0.1, 2, 0.1, 0.1, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.1, 0.1, 0.8));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(0.2, 0.2, 2, 0.2, 0.2, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.2, 0.2, 0.6));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(0.3, 0.3, 2, 0.3, 0.3, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.3, 0.3, 0.4));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(0.4, 0.4, 2, 0.4, 0.4, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.4, 0.4, 0.2));
+
+ EXPECT_TRUE(tri.Intersects(Line3d(0.5, 0.5, 2, 0.5, 0.5, 0), pt1));
+ EXPECT_EQ(pt1, Vector3d(0.5, 0.5, 0));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(Triangle3Test, ContainsPt)
+{
+ Triangle3d tri(Vector3d(0, 0, 0),
+ Vector3d(0, 1, 0),
+ Vector3d(1, 0, 0));
+
+ EXPECT_TRUE(tri.Contains(tri[0]));
+ EXPECT_TRUE(tri.Contains(tri[1]));
+ EXPECT_TRUE(tri.Contains(tri[2]));
+
+ EXPECT_TRUE(tri.Contains(Vector3d(0.1, 0.1, 0)));
+ EXPECT_TRUE(tri.Contains(Vector3d(0, 0.5, 0)));
+ EXPECT_TRUE(tri.Contains(Vector3d(0.5, 0, 0)));
+ EXPECT_TRUE(tri.Contains(Vector3d(0.5, 0.5, 0)));
+
+ EXPECT_FALSE(tri.Contains(Vector3d(-0.01, -0.01, 0)));
+ EXPECT_FALSE(tri.Contains(Vector3d(1.01, 0, 0)));
+ EXPECT_FALSE(tri.Contains(Vector3d(0, 1.01, 0)));
+ EXPECT_FALSE(tri.Contains(Vector3d(0.1, 0.1, 0.1)));
+ EXPECT_FALSE(tri.Contains(Vector3d(0.1, 0.1, -0.1)));
+}
+
+/////////////////////////////////////////////////
+TEST(Triangle3Test, Perimeter)
+{
+ {
+ Triangle3d tri(Vector3d(0, 0, 0),
+ Vector3d(0, 1, 0),
+ Vector3d(1, 0, 0));
+
+ EXPECT_DOUBLE_EQ(tri.Perimeter(), 2.0 + sqrt(2.0));
+ }
+ {
+ Triangle3d tri(Vector3d(0, 0, 1),
+ Vector3d(0, 1, 0),
+ Vector3d(1, 0, 0));
+
+ EXPECT_DOUBLE_EQ(tri.Perimeter(), 3*sqrt(2.0));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(Triangle3Test, Area)
+{
+ {
+ Triangle3d tri(Vector3d(0, 0, 0), Vector3d(0, 1, 0), Vector3d(1, 0, 0));
+
+ EXPECT_NEAR(tri.Area(), 0.5, 1e-6);
+ }
+ {
+ Triangle3d tri(Vector3d(0, 0, 1), Vector3d(0, 1, 0), Vector3d(1, 0, 0));
+
+ // (base * height) / 2
+ EXPECT_NEAR(tri.Area(), (sqrt(2) * sqrt(1.5))*0.5, 1e-6);
+ }
+}
diff --git a/src/Triangle_TEST.cc b/src/Triangle_TEST.cc
new file mode 100644
index 0000000..0e2e337
--- /dev/null
+++ b/src/Triangle_TEST.cc
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2014 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Triangle.hh"
+#include "ignition/math/Helpers.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(TriangleTest, Constructor)
+{
+ {
+ // Default constructor
+ math::Triangled tri;
+ EXPECT_EQ(tri[0], math::Vector2d(0, 0));
+ EXPECT_EQ(tri[1], math::Vector2d(0, 0));
+ EXPECT_EQ(tri[2], math::Vector2d(0, 0));
+ }
+
+ {
+ // Construct from three points
+ math::Triangled tri(math::Vector2d(0, 0),
+ math::Vector2d(0, 1),
+ math::Vector2d(1, 0));
+
+ EXPECT_TRUE(tri.Valid());
+
+ EXPECT_EQ(tri[0], math::Vector2d(0, 0));
+ EXPECT_EQ(tri[1], math::Vector2d(0, 1));
+ EXPECT_EQ(tri[2], math::Vector2d(1, 0));
+ EXPECT_NO_THROW(tri[3]);
+ EXPECT_EQ(tri[3], tri[2]);
+ }
+
+ {
+ // Construct degenerate from 3 collinear points
+ math::Triangled tri(math::Vector2d(0, 0),
+ math::Vector2d(0, 1),
+ math::Vector2d(0, 2));
+
+ // Expect not Valid
+ EXPECT_FALSE(tri.Valid());
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(TriangleTest, Set)
+{
+ math::Triangled tri;
+
+ tri.Set(0, math::Vector2d(3, 4));
+ tri.Set(1, math::Vector2d(5, 6));
+ tri.Set(2, math::Vector2d(7, 8));
+ EXPECT_EQ(tri[0], math::Vector2d(3, 4));
+ EXPECT_EQ(tri[1], math::Vector2d(5, 6));
+ EXPECT_EQ(tri[2], math::Vector2d(7, 8));
+
+ tri.Set(math::Vector2d(0.1, 0.2),
+ math::Vector2d(0.3, 0.4),
+ math::Vector2d(1.5, 2.6));
+ EXPECT_EQ(tri[0], math::Vector2d(0.1, 0.2));
+ EXPECT_EQ(tri[1], math::Vector2d(0.3, 0.4));
+ EXPECT_EQ(tri[2], math::Vector2d(1.5, 2.6));
+
+ EXPECT_NO_THROW(tri.Set(3, math::Vector2d(1.5, 2.6)));
+}
+
+/////////////////////////////////////////////////
+TEST(TriangleTest, Side)
+{
+ math::Triangled tri(math::Vector2d(0, 0),
+ math::Vector2d(0, 1),
+ math::Vector2d(1, 0));
+
+ EXPECT_TRUE(tri.Side(0) == math::Line2d(0, 0, 0, 1));
+ EXPECT_TRUE(tri.Side(1) == math::Line2d(0, 1, 1, 0));
+ EXPECT_TRUE(tri.Side(2) == math::Line2d(1, 0, 0, 0));
+
+ EXPECT_NO_THROW(tri.Side(3));
+}
+
+/////////////////////////////////////////////////
+TEST(TriangleTest, ContainsLine)
+{
+ math::Triangled tri(math::Vector2d(0, 0),
+ math::Vector2d(0, 1),
+ math::Vector2d(1, 0));
+
+ EXPECT_TRUE(tri.Contains(tri.Side(0)));
+ EXPECT_TRUE(tri.Contains(tri.Side(1)));
+ EXPECT_TRUE(tri.Contains(tri.Side(2)));
+
+ EXPECT_TRUE(tri.Contains(math::Line2d(0.1, 0.1, 0.5, 0.5)));
+
+ EXPECT_FALSE(tri.Contains(math::Line2d(0.1, 0.1, 0.6, 0.6)));
+ EXPECT_FALSE(tri.Contains(math::Line2d(-0.1, -0.1, 0.5, 0.5)));
+}
+
+/////////////////////////////////////////////////
+TEST(TriangleTest, Intersects)
+{
+ math::Vector2d pt1, pt2;
+ math::Triangled tri(math::Vector2d(0, 0),
+ math::Vector2d(0, 1),
+ math::Vector2d(1, 0));
+
+ EXPECT_TRUE(tri.Intersects(tri.Side(0), pt1, pt2));
+ EXPECT_EQ(pt1, math::Vector2d(0, 0));
+ EXPECT_EQ(pt2, math::Vector2d(0, 1));
+
+ EXPECT_TRUE(tri.Intersects(tri.Side(1), pt1, pt2));
+ EXPECT_EQ(pt1, math::Vector2d(0, 1));
+ EXPECT_EQ(pt2, math::Vector2d(1, 0));
+
+ EXPECT_TRUE(tri.Intersects(tri.Side(2), pt1, pt2));
+ EXPECT_EQ(pt1, math::Vector2d(1, 0));
+ EXPECT_EQ(pt2, math::Vector2d(0, 0));
+
+
+ EXPECT_TRUE(tri.Intersects(math::Line2d(0.1, 0.1, 0.5, 0.5), pt1, pt2));
+ EXPECT_EQ(pt1, math::Vector2d(0.1, 0.1));
+ EXPECT_EQ(pt2, math::Vector2d(0.5, 0.5));
+
+ EXPECT_TRUE(tri.Intersects(math::Line2d(0.1, 0.1, 0.6, 0.6), pt1, pt2));
+ EXPECT_EQ(pt1, math::Vector2d(0.5, 0.5));
+ EXPECT_EQ(pt2, math::Vector2d(0.1, 0.1));
+
+ EXPECT_TRUE(tri.Intersects(math::Line2d(-0.1, -0.1, 0.5, 0.5), pt1, pt2));
+ EXPECT_EQ(pt1, math::Vector2d(0.0, 0.0));
+ EXPECT_EQ(pt2, math::Vector2d(0.5, 0.5));
+
+ EXPECT_TRUE(tri.Intersects(math::Line2d(-2, -2, 0.2, 0.2), pt1, pt2));
+ EXPECT_EQ(pt1, math::Vector2d(0.0, 0.0));
+ EXPECT_EQ(pt2, math::Vector2d(0.2, 0.2));
+
+ EXPECT_FALSE(tri.Intersects(math::Line2d(-0.1, 0, -0.1, 1), pt1, pt2));
+}
+
+/////////////////////////////////////////////////
+TEST(TriangleTest, ContainsPt)
+{
+ math::Triangled tri(math::Vector2d(0, 0),
+ math::Vector2d(0, 1),
+ math::Vector2d(1, 0));
+
+ EXPECT_TRUE(tri.Contains(tri[0]));
+ EXPECT_TRUE(tri.Contains(tri[1]));
+ EXPECT_TRUE(tri.Contains(tri[2]));
+
+ EXPECT_TRUE(tri.Contains(math::Vector2d(0.1, 0.1)));
+ EXPECT_TRUE(tri.Contains(math::Vector2d(0, 0.5)));
+ EXPECT_TRUE(tri.Contains(math::Vector2d(0.5, 0)));
+ EXPECT_TRUE(tri.Contains(math::Vector2d(0.5, 0.5)));
+
+ EXPECT_FALSE(tri.Contains(math::Vector2d(-0.01, -0.01)));
+ EXPECT_FALSE(tri.Contains(math::Vector2d(1.01, 0)));
+ EXPECT_FALSE(tri.Contains(math::Vector2d(0, 1.01)));
+}
+
+/////////////////////////////////////////////////
+TEST(TriangleTest, Perimeter)
+{
+ math::Triangled tri(math::Vector2d(0, 0),
+ math::Vector2d(0, 1),
+ math::Vector2d(1, 0));
+
+ EXPECT_DOUBLE_EQ(tri.Perimeter(), 2.0 + sqrt(2.0));
+}
+
+/////////////////////////////////////////////////
+TEST(TriangleTest, Area)
+{
+ math::Triangled tri(math::Vector2d(0, 0),
+ math::Vector2d(0, 1),
+ math::Vector2d(1, 0));
+
+ EXPECT_NEAR(tri.Area(), 0.499999, 1e-6);
+}
diff --git a/src/Vector2_TEST.cc b/src/Vector2_TEST.cc
new file mode 100644
index 0000000..9c1cd74
--- /dev/null
+++ b/src/Vector2_TEST.cc
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+#include <cmath>
+
+#include "ignition/math/Helpers.hh"
+#include "ignition/math/Vector2.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(Vector2Test, Vector2)
+{
+ {
+ math::Vector2d v;
+ EXPECT_DOUBLE_EQ(0, v.X());
+ EXPECT_DOUBLE_EQ(0, v.Y());
+ }
+
+ // Constructor
+ math::Vector2d v(1, 2);
+ EXPECT_DOUBLE_EQ(1, v.X());
+ EXPECT_DOUBLE_EQ(2, v.Y());
+
+ // ::Distance
+ EXPECT_TRUE(math::equal(2.236, v.Distance(math::Vector2d(0, 0)), 1e-2));
+
+ // ::Normalize
+ v.Normalize();
+ EXPECT_TRUE(v == math::Vector2d(0.447214, 0.894427));
+
+ // ::Set
+ v.Set(4, 5);
+ EXPECT_TRUE(v == math::Vector2d(4, 5));
+
+ // ::operator=
+ v = math::Vector2d(6, 7);
+ EXPECT_TRUE(v == math::Vector2d(6, 7));
+
+ // ::operator= int
+ v = 5;
+ EXPECT_TRUE(v == math::Vector2d(5, 5));
+
+ // ::operator+
+ v = v + math::Vector2d(1, 2);
+ EXPECT_TRUE(v == math::Vector2d(6, 7));
+
+ // ::operator +=
+ v += math::Vector2d(5, 6);
+ EXPECT_TRUE(v == math::Vector2d(11, 13));
+
+ // ::operator -
+ v = v - math::Vector2d(2, 4);
+ EXPECT_TRUE(v == math::Vector2d(9, 9));
+
+ // ::operator -=
+ v.Set(2, 4);
+ v -= math::Vector2d(1, 6);
+ EXPECT_TRUE(v == math::Vector2d(1, -2));
+
+ // ::operator /
+ v.Set(10, 6);
+ v = v / math::Vector2d(2, 3);
+ EXPECT_TRUE(v == math::Vector2d(5, 2));
+
+ // ::operator /=
+ v.Set(10, 6);
+ v /= math::Vector2d(2, 3);
+ EXPECT_TRUE(v == math::Vector2d(5, 2));
+
+ // ::operator / int
+ v.Set(10, 6);
+ v = v / 2;
+ EXPECT_TRUE(v == math::Vector2d(5, 3));
+
+ // ::operator /= int
+ v.Set(10, 6);
+ v /= 2;
+ EXPECT_TRUE(v == math::Vector2d(5, 3));
+
+ // ::operator * int
+ v.Set(10, 6);
+ v = v * 2;
+ EXPECT_TRUE(v == math::Vector2d(20, 12));
+
+ // ::operator *= int
+ v.Set(10, 6);
+ v *= 2;
+ EXPECT_TRUE(v == math::Vector2d(20, 12));
+
+ // ::operator * vector2i
+ v.Set(10, 6);
+ v = v * math::Vector2d(2, 4);
+ EXPECT_TRUE(v == math::Vector2d(20, 24));
+
+ // ::operator *= vector2i
+ v.Set(10, 6);
+ v *= math::Vector2d(2, 4);
+ EXPECT_TRUE(v == math::Vector2d(20, 24));
+
+
+ // ::IsFinite
+ EXPECT_TRUE(v.IsFinite());
+
+ // ::operator[]
+ v[0] = 6;
+ v[1] = 7;
+ EXPECT_DOUBLE_EQ(6, v[0]);
+ EXPECT_DOUBLE_EQ(7, v[1]);
+}
+
+/////////////////////////////////////////////////
+TEST(Vector2Test, NoException)
+{
+ math::Vector2d v(1, 2);
+ EXPECT_NO_THROW(math::equal(v[0], 1.0));
+ EXPECT_NO_THROW(math::equal(v[1], 2.0));
+
+ EXPECT_NO_THROW(math::equal(v[2], 1.0));
+ EXPECT_DOUBLE_EQ(v[2], 2.0);
+}
+
+/////////////////////////////////////////////////
+// Test Equal function with specified tolerance
+TEST(Vector2Test, EqualTolerance)
+{
+ EXPECT_FALSE(math::Vector2d::Zero.Equal(math::Vector2d::One, 1e-6));
+ EXPECT_FALSE(math::Vector2d::Zero.Equal(math::Vector2d::One, 1e-3));
+ EXPECT_FALSE(math::Vector2d::Zero.Equal(math::Vector2d::One, 1e-1));
+ EXPECT_TRUE(math::Vector2d::Zero.Equal(math::Vector2d::One, 1));
+ EXPECT_TRUE(math::Vector2d::Zero.Equal(math::Vector2d::One, 1.1));
+}
+
+/////////////////////////////////////////////////
+TEST(Vector2Test, Dot)
+{
+ math::Vector2d v(1, 2);
+
+ EXPECT_DOUBLE_EQ(v.Dot(math::Vector2d(3, 4)), 11.0);
+ EXPECT_DOUBLE_EQ(v.Dot(math::Vector2d(0, 0)), 0.0);
+ EXPECT_DOUBLE_EQ(v.Dot(math::Vector2d(1, 0)), 1.0);
+ EXPECT_DOUBLE_EQ(v.Dot(math::Vector2d(0, 1)), 2.0);
+}
+
+/////////////////////////////////////////////////
+TEST(Vector2Test, OperatorStreamOut)
+{
+ math::Vector2d v(0.1, 1.2);
+ std::ostringstream stream;
+ stream << v;
+ EXPECT_EQ(stream.str(), "0.1 1.2");
+}
+
+/////////////////////////////////////////////////
+TEST(Vector2dTest, Add)
+{
+ math::Vector2d vec1(0.1, 0.2);
+ math::Vector2d vec2(1.1, 2.2);
+
+ math::Vector2d vec3 = vec1;
+ vec3 += vec2;
+
+ EXPECT_EQ(vec1 + vec2, math::Vector2d(1.2, 2.4));
+ EXPECT_EQ(vec3, math::Vector2d(1.2, 2.4));
+
+ // Add zeros
+ {
+ // Scalar left and right
+ EXPECT_EQ(0 + vec1, vec1);
+ EXPECT_EQ(vec1 + 0, vec1);
+
+ // Vector left and right
+ EXPECT_EQ(math::Vector2d::Zero + vec1, vec1);
+ EXPECT_EQ(vec1 + math::Vector2d::Zero, vec1);
+
+ // Addition assignment
+ math::Vector2d vec4(vec1);
+ vec4 += 0;
+ EXPECT_EQ(vec4, vec1);
+ vec4 += math::Vector2d::Zero;
+ EXPECT_EQ(vec4, vec1);
+ }
+
+ // Add non-trivial scalar values left and right
+ {
+ EXPECT_EQ(2.5 + vec1, math::Vector2d(2.6, 2.7));
+ EXPECT_EQ(vec1 + 2.5, math::Vector2d(2.6, 2.7));
+
+ math::Vector2d vec4(vec1);
+ vec4 += 2.5;
+ EXPECT_EQ(vec4, math::Vector2d(2.6, 2.7));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(Vector2dTest, Sub)
+{
+ math::Vector2d vec1(0.1, 0.2);
+ math::Vector2d vec2(1.1, 2.2);
+
+ math::Vector2d vec3 = vec2;
+ vec3 -= vec1;
+
+ EXPECT_EQ(vec2 - vec1, math::Vector2d(1.0, 2.0));
+ EXPECT_EQ(vec3, math::Vector2d(1.0, 2.0));
+
+ // Subtraction with zeros
+ {
+ // Scalar left and right
+ EXPECT_EQ(0 - vec1, -vec1);
+ EXPECT_EQ(vec1 - 0, vec1);
+
+ // Vector left and right
+ EXPECT_EQ(math::Vector2d::Zero - vec1, -vec1);
+ EXPECT_EQ(vec1 - math::Vector2d::Zero, vec1);
+
+ // Subtraction assignment
+ math::Vector2d vec4(vec1);
+ vec4 -= 0;
+ EXPECT_EQ(vec4, vec1);
+ vec4 -= math::Vector2d::Zero;
+ EXPECT_EQ(vec4, vec1);
+ }
+
+ // Subtract non-trivial scalar values left and right
+ {
+ EXPECT_EQ(2.5 - vec1, math::Vector2d(2.4, 2.3));
+ EXPECT_EQ(vec1 - 2.5, -math::Vector2d(2.4, 2.3));
+
+ math::Vector2d vec4(vec1);
+ vec4 -= 2.5;
+ EXPECT_EQ(vec4, -math::Vector2d(2.4, 2.3));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(Vector2Test, Multiply)
+{
+ math::Vector2d v(0.1, -4.2);
+
+ // Multiply by zero
+ {
+ // Scalar left and right
+ EXPECT_EQ(0 * v, math::Vector2d::Zero);
+ EXPECT_EQ(v * 0, math::Vector2d::Zero);
+
+ // Element-wise vector multiplication
+ EXPECT_EQ(v * math::Vector2d::Zero, math::Vector2d::Zero);
+ }
+
+ // Multiply by one
+ {
+ // Scalar left and right
+ EXPECT_EQ(1 * v, v);
+ EXPECT_EQ(v * 1, v);
+
+ // Element-wise vector multiplication
+ EXPECT_EQ(v * math::Vector2d::One, v);
+ }
+
+ // Multiply by non-trivial scalar value
+ {
+ const double scalar = 2.5;
+ math::Vector2d expect(0.25, -10.5);
+ EXPECT_EQ(scalar * v, expect);
+ EXPECT_EQ(v * scalar, expect);
+ }
+
+ // Multiply by itself element-wise
+ EXPECT_EQ(v*v, math::Vector2d(0.01, 17.64));
+}
+
+/////////////////////////////////////////////////
+TEST(Vector2dTest, Length)
+{
+ // Zero vector
+ EXPECT_DOUBLE_EQ(math::Vector2d::Zero.Length(), 0.0);
+ EXPECT_DOUBLE_EQ(math::Vector2d::Zero.SquaredLength(), 0.0);
+
+ // One vector
+ EXPECT_NEAR(math::Vector2d::One.Length(), IGN_SQRT2, 1e-10);
+ EXPECT_DOUBLE_EQ(math::Vector2d::One.SquaredLength(), 2.0);
+
+ // Arbitrary vector
+ math::Vector2d v(0.1, -4.2);
+ EXPECT_NEAR(v.Length(), 4.20119030752, 1e-10);
+ EXPECT_DOUBLE_EQ(v.SquaredLength(), 17.65);
+}
+
diff --git a/src/Vector3Stats.cc b/src/Vector3Stats.cc
new file mode 100644
index 0000000..10ad3a2
--- /dev/null
+++ b/src/Vector3Stats.cc
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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 <ignition/math/Vector3Stats.hh>
+#include "Vector3StatsPrivate.hh"
+
+using namespace ignition;
+using namespace math;
+
+//////////////////////////////////////////////////
+Vector3Stats::Vector3Stats()
+ : dataPtr(new Vector3StatsPrivate)
+{
+}
+
+//////////////////////////////////////////////////
+Vector3Stats::~Vector3Stats()
+{
+ delete this->dataPtr;
+ this->dataPtr = 0;
+}
+
+//////////////////////////////////////////////////
+void Vector3Stats::InsertData(const Vector3d &_data)
+{
+ this->dataPtr->x.InsertData(_data.X());
+ this->dataPtr->y.InsertData(_data.Y());
+ this->dataPtr->z.InsertData(_data.Z());
+ this->dataPtr->mag.InsertData(_data.Length());
+}
+
+//////////////////////////////////////////////////
+bool Vector3Stats::InsertStatistic(const std::string &_name)
+{
+ bool x = this->dataPtr->x.InsertStatistic(_name);
+ bool y = this->dataPtr->y.InsertStatistic(_name);
+ bool z = this->dataPtr->z.InsertStatistic(_name);
+ bool mag = this->dataPtr->mag.InsertStatistic(_name);
+ return x && y && z && mag;
+}
+
+//////////////////////////////////////////////////
+bool Vector3Stats::InsertStatistics(const std::string &_names)
+{
+ bool x = this->dataPtr->x.InsertStatistics(_names);
+ bool y = this->dataPtr->y.InsertStatistics(_names);
+ bool z = this->dataPtr->z.InsertStatistics(_names);
+ bool mag = this->dataPtr->mag.InsertStatistics(_names);
+ return x && y && z && mag;
+}
+
+//////////////////////////////////////////////////
+void Vector3Stats::Reset()
+{
+ this->dataPtr->x.Reset();
+ this->dataPtr->y.Reset();
+ this->dataPtr->z.Reset();
+ this->dataPtr->mag.Reset();
+}
+
+//////////////////////////////////////////////////
+const SignalStats &Vector3Stats::X() const
+{
+ return this->dataPtr->x;
+}
+
+//////////////////////////////////////////////////
+const SignalStats &Vector3Stats::Y() const
+{
+ return this->dataPtr->y;
+}
+
+//////////////////////////////////////////////////
+const SignalStats &Vector3Stats::Z() const
+{
+ return this->dataPtr->z;
+}
+
+//////////////////////////////////////////////////
+const SignalStats &Vector3Stats::Mag() const
+{
+ return this->dataPtr->mag;
+}
+
+//////////////////////////////////////////////////
+SignalStats &Vector3Stats::X()
+{
+ return this->dataPtr->x;
+}
+
+//////////////////////////////////////////////////
+SignalStats &Vector3Stats::Y()
+{
+ return this->dataPtr->y;
+}
+
+//////////////////////////////////////////////////
+SignalStats &Vector3Stats::Z()
+{
+ return this->dataPtr->z;
+}
+
+//////////////////////////////////////////////////
+SignalStats &Vector3Stats::Mag()
+{
+ return this->dataPtr->mag;
+}
+
diff --git a/src/Vector3StatsPrivate.hh b/src/Vector3StatsPrivate.hh
new file mode 100644
index 0000000..d4439b3
--- /dev/null
+++ b/src/Vector3StatsPrivate.hh
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+#ifndef IGNITION_MATH_VECTOR3STATSPRIVATE_HH_
+#define IGNITION_MATH_VECTOR3STATSPRIVATE_HH_
+
+#include <ignition/math/SignalStats.hh>
+
+namespace ignition
+{
+ namespace math
+ {
+ /// \brief Private data class for the Vector3Stats class.
+ class Vector3StatsPrivate
+ {
+ /// \brief Statistics for x component of signal.
+ public: SignalStats x;
+
+ /// \brief Statistics for y component of signal.
+ public: SignalStats y;
+
+ /// \brief Statistics for z component of signal.
+ public: SignalStats z;
+
+ /// \brief Statistics for magnitude of signal.
+ public: SignalStats mag;
+ };
+ }
+}
+#endif
+
diff --git a/src/Vector3Stats_TEST.cc b/src/Vector3Stats_TEST.cc
new file mode 100644
index 0000000..39fc5a5
--- /dev/null
+++ b/src/Vector3Stats_TEST.cc
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include <ignition/math/Vector3Stats.hh>
+
+using namespace ignition;
+
+class Vector3StatsTest : public ::testing::Test
+{
+ /// \brief Get X value of statistic _name.
+ /// \param[in] _name Name of statistic.
+ /// \return X value of statistic.
+ public: double X(const std::string &_name) const;
+
+ /// \brief Get Y value of statistic _name.
+ /// \param[in] _name Name of statistic.
+ /// \return Y value of statistic.
+ public: double Y(const std::string &_name) const;
+
+ /// \brief Get Z value of statistic _name.
+ /// \param[in] _name Name of statistic.
+ /// \return Z value of statistic.
+ public: double Z(const std::string &_name) const;
+
+ /// \brief Get Magnitude value of statistic _name.
+ /// \param[in] _name Name of statistic.
+ /// \return Magnitude value of statistic.
+ public: double Mag(const std::string &_name) const;
+
+ /// \brief Stats instance.
+ public: math::Vector3Stats stats;
+};
+
+//////////////////////////////////////////////////
+double Vector3StatsTest::X(const std::string &_name) const
+{
+ return this->stats.X().Map()[_name];
+}
+
+//////////////////////////////////////////////////
+double Vector3StatsTest::Y(const std::string &_name) const
+{
+ return this->stats.Y().Map()[_name];
+}
+
+//////////////////////////////////////////////////
+double Vector3StatsTest::Z(const std::string &_name) const
+{
+ return this->stats.Z().Map()[_name];
+}
+
+//////////////////////////////////////////////////
+double Vector3StatsTest::Mag(const std::string &_name) const
+{
+ return this->stats.Mag().Map()[_name];
+}
+
+//////////////////////////////////////////////////
+TEST_F(Vector3StatsTest, Vector3Stats)
+{
+ {
+ // Constructor
+ math::Vector3Stats v3stats;
+ EXPECT_TRUE(v3stats.X().Map().empty());
+ EXPECT_TRUE(v3stats.Y().Map().empty());
+ EXPECT_TRUE(v3stats.Z().Map().empty());
+ EXPECT_TRUE(v3stats.Mag().Map().empty());
+ EXPECT_EQ(v3stats.X().Count(), 0u);
+ EXPECT_EQ(v3stats.Y().Count(), 0u);
+ EXPECT_EQ(v3stats.Z().Count(), 0u);
+ EXPECT_EQ(v3stats.Mag().Count(), 0u);
+
+ // Reset
+ v3stats.Reset();
+ EXPECT_TRUE(v3stats.X().Map().empty());
+ EXPECT_TRUE(v3stats.Y().Map().empty());
+ EXPECT_TRUE(v3stats.Z().Map().empty());
+ EXPECT_TRUE(v3stats.Mag().Map().empty());
+ EXPECT_EQ(v3stats.X().Count(), 0u);
+ EXPECT_EQ(v3stats.Y().Count(), 0u);
+ EXPECT_EQ(v3stats.Z().Count(), 0u);
+ EXPECT_EQ(v3stats.Mag().Count(), 0u);
+ }
+
+ {
+ // InsertStatistics
+ math::Vector3Stats v3stats;
+ EXPECT_TRUE(v3stats.X().Map().empty());
+ EXPECT_TRUE(v3stats.Y().Map().empty());
+ EXPECT_TRUE(v3stats.Z().Map().empty());
+ EXPECT_TRUE(v3stats.Mag().Map().empty());
+
+ EXPECT_TRUE(v3stats.InsertStatistics("maxAbs"));
+ EXPECT_FALSE(v3stats.InsertStatistics("maxAbs"));
+ EXPECT_FALSE(v3stats.InsertStatistic("maxAbs"));
+ EXPECT_FALSE(v3stats.X().Map().empty());
+ EXPECT_FALSE(v3stats.Y().Map().empty());
+ EXPECT_FALSE(v3stats.Z().Map().empty());
+ EXPECT_FALSE(v3stats.Mag().Map().empty());
+
+ // Map with no data
+ {
+ std::map<std::string, double> map = v3stats.X().Map();
+ EXPECT_EQ(map.size(), 1u);
+ EXPECT_EQ(map.count("maxAbs"), 1u);
+ }
+ {
+ std::map<std::string, double> map = v3stats.Y().Map();
+ EXPECT_EQ(map.size(), 1u);
+ EXPECT_EQ(map.count("maxAbs"), 1u);
+ }
+ {
+ std::map<std::string, double> map = v3stats.Z().Map();
+ EXPECT_EQ(map.size(), 1u);
+ EXPECT_EQ(map.count("maxAbs"), 1u);
+ }
+ {
+ std::map<std::string, double> map = v3stats.Mag().Map();
+ EXPECT_EQ(map.size(), 1u);
+ EXPECT_EQ(map.count("maxAbs"), 1u);
+ }
+
+ // Insert some data
+ EXPECT_EQ(v3stats.X().Count(), 0u);
+ EXPECT_EQ(v3stats.Y().Count(), 0u);
+ EXPECT_EQ(v3stats.Z().Count(), 0u);
+ EXPECT_EQ(v3stats.Mag().Count(), 0u);
+
+ v3stats.InsertData(math::Vector3d::UnitX);
+ v3stats.InsertData(math::Vector3d::UnitX);
+ v3stats.InsertData(math::Vector3d::UnitY);
+
+ EXPECT_EQ(v3stats.X().Count(), 3u);
+ EXPECT_EQ(v3stats.Y().Count(), 3u);
+ EXPECT_EQ(v3stats.Z().Count(), 3u);
+ EXPECT_EQ(v3stats.Mag().Count(), 3u);
+
+ EXPECT_NEAR(v3stats.X().Map()["maxAbs"], 1.0, 1e-10);
+ EXPECT_NEAR(v3stats.Y().Map()["maxAbs"], 1.0, 1e-10);
+ EXPECT_DOUBLE_EQ(v3stats.Z().Map()["maxAbs"], 0.0);
+ EXPECT_NEAR(v3stats.Mag().Map()["maxAbs"], 1.0, 1e-10);
+ }
+
+ // Const accessors
+ {
+ EXPECT_TRUE(this->stats.X().Map().empty());
+ EXPECT_TRUE(this->stats.Y().Map().empty());
+ EXPECT_TRUE(this->stats.Z().Map().empty());
+ EXPECT_TRUE(this->stats.Mag().Map().empty());
+
+ const std::string name("maxAbs");
+ EXPECT_TRUE(this->stats.InsertStatistics(name));
+
+ this->stats.InsertData(math::Vector3d::UnitX);
+ this->stats.InsertData(math::Vector3d::UnitX);
+ this->stats.InsertData(math::Vector3d::UnitY);
+
+ EXPECT_EQ(this->stats.X().Count(), 3u);
+ EXPECT_EQ(this->stats.Y().Count(), 3u);
+ EXPECT_EQ(this->stats.Z().Count(), 3u);
+ EXPECT_EQ(this->stats.Mag().Count(), 3u);
+
+ EXPECT_NEAR(this->stats.X().Map()[name], 1.0, 1e-10);
+ EXPECT_NEAR(this->stats.Y().Map()[name], 1.0, 1e-10);
+ EXPECT_DOUBLE_EQ(this->stats.Z().Map()[name], 0.0);
+ EXPECT_NEAR(this->stats.Mag().Map()[name], 1.0, 1e-10);
+
+ EXPECT_NEAR(this->X(name), 1.0, 1e-10);
+ EXPECT_NEAR(this->Y(name), 1.0, 1e-10);
+ EXPECT_DOUBLE_EQ(this->Z(name), 0.0);
+ EXPECT_NEAR(this->Mag(name), 1.0, 1e-10);
+ }
+}
diff --git a/src/Vector3_TEST.cc b/src/Vector3_TEST.cc
new file mode 100644
index 0000000..20529dc
--- /dev/null
+++ b/src/Vector3_TEST.cc
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Vector3.hh"
+#include "ignition/math/Helpers.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(Vector3Test, Vector3d)
+{
+ math::Vector3d v;
+
+ // ::Distance, ::Length()
+ v.Set(1, 2, 3);
+ EXPECT_DOUBLE_EQ(v.Length(), v.Distance(math::Vector3d(0, 0, 0)));
+
+ // ::Round
+ v.Set(1.23, 2.34, 3.55);
+ EXPECT_TRUE(v.Rounded() == math::Vector3d(1, 2, 4));
+
+ // ::Round
+ v.Round();
+ EXPECT_TRUE(v.Round() == math::Vector3d(1, 2, 4));
+
+ // ::DotProd
+ EXPECT_TRUE(math::equal(17.0, v.Dot(math::Vector3d(1, 2, 3)), 1e-2));
+
+ // ::DistToLine
+ v.Set(0, 0, 0);
+ EXPECT_DOUBLE_EQ(1.0, v.DistToLine(math::Vector3d(1, -1, 0),
+ math::Vector3d(1, 1, 0)));
+
+ // ::operator= double
+ v = 4.0;
+ EXPECT_TRUE(v == math::Vector3d(4, 4, 4));
+
+ // ::operator/ vector3
+ v = v / math::Vector3d(1, 2, 4);
+ EXPECT_TRUE(v == math::Vector3d(4, 2, 1));
+
+ // ::operator / double
+ v = v / 2;
+ EXPECT_TRUE(v == math::Vector3d(2, 1, .5));
+
+ // ::operator * vector3
+ v = v * math::Vector3d(2, 3, 4);
+ EXPECT_TRUE(v == math::Vector3d(4, 3, 2));
+
+ // ::operator[]
+ v[0] = 40;
+ v[1] = 30;
+ v[2] = 20;
+ EXPECT_DOUBLE_EQ(v[0], 40);
+ EXPECT_DOUBLE_EQ(v[1], 30);
+ EXPECT_DOUBLE_EQ(v[2], 20);
+
+ v.Set(1.23, 2.35, 3.654321);
+ v.Round(1);
+ EXPECT_TRUE(v == math::Vector3d(1.2, 2.4, 3.7));
+
+ // operator GetAbs
+ v.Set(-1, -2, -3);
+ EXPECT_TRUE(v.Abs() == math::Vector3d(1, 2, 3));
+
+ // operator /=
+ v.Set(1, 2, 4);
+ v /= math::Vector3d(1, 4, 4);
+ EXPECT_TRUE(v == math::Vector3d(1, .5, 1));
+
+ // operator *=
+ v.Set(1, 2, 4);
+ v *= math::Vector3d(2, .5, .1);
+ EXPECT_TRUE(v.Equal(math::Vector3d(2, 1, .4)));
+
+ // Test the static defines.
+ EXPECT_TRUE(math::Vector3d::Zero == math::Vector3d(0, 0, 0));
+ EXPECT_TRUE(math::Vector3d::One == math::Vector3d(1, 1, 1));
+ EXPECT_TRUE(math::Vector3d::UnitX == math::Vector3d(1, 0, 0));
+ EXPECT_TRUE(math::Vector3d::UnitY == math::Vector3d(0, 1, 0));
+ EXPECT_TRUE(math::Vector3d::UnitZ == math::Vector3d(0, 0, 1));
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, Distance)
+{
+ math::Vector3d vec1(0, 0, 0);
+ math::Vector3d vec2(1, 2, 3);
+
+ double dist = vec1.Distance(vec2);
+ EXPECT_NEAR(dist, 3.74165738677, 1e-6);
+
+ double dist2 = vec1.Distance(1, 2, 3);
+ EXPECT_DOUBLE_EQ(dist, dist2);
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, Sum)
+{
+ math::Vector3d vec1(0, 0, 0);
+ math::Vector3d vec2(1, 2, 3);
+
+ double sum1 = vec1.Sum();
+ double sum2 = vec2.Sum();
+
+ EXPECT_DOUBLE_EQ(sum1, 0);
+ EXPECT_DOUBLE_EQ(sum2, 6);
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, SquaredLength)
+{
+ math::Vector3d vec1(0, 0, 0);
+ math::Vector3d vec2(1, 2, 3);
+
+ double sum1 = vec1.SquaredLength();
+ double sum2 = vec2.SquaredLength();
+
+ EXPECT_DOUBLE_EQ(sum1, 0);
+ EXPECT_DOUBLE_EQ(sum2, 14);
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, Length)
+{
+ // Zero vector
+ EXPECT_DOUBLE_EQ(math::Vector3d::Zero.Length(), 0.0);
+ EXPECT_DOUBLE_EQ(math::Vector3d::Zero.SquaredLength(), 0.0);
+
+ // UnitXYZ vectors
+ EXPECT_DOUBLE_EQ(math::Vector3d::UnitX.Length(), 1.0);
+ EXPECT_DOUBLE_EQ(math::Vector3d::UnitY.Length(), 1.0);
+ EXPECT_DOUBLE_EQ(math::Vector3d::UnitZ.Length(), 1.0);
+ EXPECT_DOUBLE_EQ(math::Vector3d::UnitX.SquaredLength(), 1.0);
+ EXPECT_DOUBLE_EQ(math::Vector3d::UnitY.SquaredLength(), 1.0);
+ EXPECT_DOUBLE_EQ(math::Vector3d::UnitZ.SquaredLength(), 1.0);
+
+ // One vector
+ EXPECT_NEAR(math::Vector3d::One.Length(), sqrt(3.0), 1e-10);
+ EXPECT_DOUBLE_EQ(math::Vector3d::One.SquaredLength(), 3.0);
+
+ // Arbitrary vector
+ math::Vector3d v(0.1, -4.2, 2.5);
+ EXPECT_NEAR(v.Length(), 4.88876262463, 1e-10);
+ EXPECT_DOUBLE_EQ(v.SquaredLength(), 23.9);
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, Normalize)
+{
+ math::Vector3d vec1(0, 0, 0);
+ math::Vector3d vec2(1, 2, 3);
+
+ math::Vector3d vec3 = vec1.Normalize();
+ EXPECT_EQ(vec3, vec1);
+ EXPECT_EQ(vec1, math::Vector3d(0, 0, 0));
+
+ vec3 = vec2.Normalize();
+ EXPECT_EQ(vec3, vec2);
+ EXPECT_EQ(vec2, math::Vector3d(0.267261, 0.534522, 0.801784));
+
+ const math::Vector3d vecConst(1, 2, 3);
+ EXPECT_EQ(vecConst.Normalized(), vec3);
+ EXPECT_EQ(vecConst, math::Vector3d(1, 2, 3));
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, GetNormal)
+{
+ math::Vector3d vec1(0, 0, 0);
+ math::Vector3d vec2(0, 1, 0);
+ math::Vector3d vec3(1, 1, 0);
+
+ math::Vector3d norm = math::Vector3d::Normal(vec1, vec2, vec3);
+ EXPECT_EQ(norm, math::Vector3d(0, 0, -1));
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, Perpendicular)
+{
+ math::Vector3d vec1(1, 1, 0);
+ math::Vector3d vec2(0, 1, 1);
+ math::Vector3d vec3(1e-7, 1e-7, 1e-7);
+ math::Vector3d vec4(1, 0, 0);
+
+ EXPECT_EQ(vec1.Perpendicular(), math::Vector3d(0, 0, -1));
+ EXPECT_EQ(vec2.Perpendicular(), math::Vector3d(0, 1, -1));
+ EXPECT_EQ(vec3.Perpendicular(), math::Vector3d(0, 0, 0));
+ EXPECT_EQ(vec4.Perpendicular(), math::Vector3d(0, 0, 1));
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, Max)
+{
+ math::Vector3d vec1(0.1, 0.2, 0.3);
+ math::Vector3d vec2(0.2, 0.3, 0.4);
+ math::Vector3d vec3(0.1, 0.2, 0.3);
+
+ EXPECT_DOUBLE_EQ(vec1.Max(), 0.3);
+
+ vec1.Max(vec2);
+ EXPECT_EQ(vec1, math::Vector3d(0.2, 0.3, 0.4));
+
+ vec1.Max(vec3);
+ EXPECT_EQ(vec1, math::Vector3d(0.2, 0.3, 0.4));
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, Min)
+{
+ math::Vector3d vec1(0.1, 0.2, 0.3);
+ math::Vector3d vec2(0.2, 0.3, 0.4);
+ math::Vector3d vec3(0.05, 0.1, 0.2);
+
+ EXPECT_DOUBLE_EQ(vec1.Min(), 0.1);
+
+ vec1.Min(vec2);
+ EXPECT_EQ(vec1, math::Vector3d(0.1, 0.2, 0.3));
+
+ vec1.Min(vec3);
+ EXPECT_EQ(vec1, math::Vector3d(0.05, 0.1, 0.2));
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, Add)
+{
+ math::Vector3d vec1(0.1, 0.2, 0.4);
+ math::Vector3d vec2(1.1, 2.2, 3.4);
+
+ math::Vector3d vec3 = vec1;
+ vec3 += vec2;
+
+ EXPECT_EQ(vec1 + vec2, math::Vector3d(1.2, 2.4, 3.8));
+ EXPECT_EQ(vec3, math::Vector3d(1.2, 2.4, 3.8));
+
+ // Add zeros
+ {
+ // Scalar left and right
+ EXPECT_EQ(0 + vec1, vec1);
+ EXPECT_EQ(vec1 + 0, vec1);
+
+ // Vector left and right
+ EXPECT_EQ(math::Vector3d::Zero + vec1, vec1);
+ EXPECT_EQ(vec1 + math::Vector3d::Zero, vec1);
+
+ // Addition assignment
+ math::Vector3d vec4(vec1);
+ vec4 += 0;
+ EXPECT_EQ(vec4, vec1);
+ vec4 += math::Vector3d::Zero;
+ EXPECT_EQ(vec4, vec1);
+ }
+
+ // Add non-trivial scalar values left and right
+ {
+ EXPECT_EQ(2.5 + vec1, math::Vector3d(2.6, 2.7, 2.9));
+ EXPECT_EQ(vec1 + 2.5, math::Vector3d(2.6, 2.7, 2.9));
+
+ math::Vector3d vec4(vec1);
+ vec4 += 2.5;
+ EXPECT_EQ(vec4, math::Vector3d(2.6, 2.7, 2.9));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, Sub)
+{
+ math::Vector3d vec1(0.1, 0.2, 0.4);
+ math::Vector3d vec2(1.1, 2.2, 3.4);
+
+ math::Vector3d vec3 = vec2;
+ vec3 -= vec1;
+
+ EXPECT_EQ(vec2 - vec1, math::Vector3d(1.0, 2.0, 3.0));
+ EXPECT_EQ(vec3, math::Vector3d(1.0, 2.0, 3.0));
+
+ // Subtraction with zeros
+ {
+ // Scalar left and right
+ EXPECT_EQ(0 - vec1, -vec1);
+ EXPECT_EQ(vec1 - 0, vec1);
+
+ // Vector left and right
+ EXPECT_EQ(math::Vector3d::Zero - vec1, -vec1);
+ EXPECT_EQ(vec1 - math::Vector3d::Zero, vec1);
+
+ // Subtraction assignment
+ math::Vector3d vec4(vec1);
+ vec4 -= 0;
+ EXPECT_EQ(vec4, vec1);
+ vec4 -= math::Vector3d::Zero;
+ EXPECT_EQ(vec4, vec1);
+ }
+
+ // Subtract non-trivial scalar values left and right
+ {
+ EXPECT_EQ(2.5 - vec1, math::Vector3d(2.4, 2.3, 2.1));
+ EXPECT_EQ(vec1 - 2.5, -math::Vector3d(2.4, 2.3, 2.1));
+
+ math::Vector3d vec4(vec1);
+ vec4 -= 2.5;
+ EXPECT_EQ(vec4, -math::Vector3d(2.4, 2.3, 2.1));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, Divide)
+{
+ math::Vector3d vec1(0.1, 0.2, 0.4);
+
+ math::Vector3d vec3 = vec1 / 2.0;
+ EXPECT_EQ(vec3, math::Vector3d(0.05, 0.1, 0.2));
+
+ vec3 /= 4.0;
+ EXPECT_EQ(vec3, math::Vector3d(0.0125, 0.025, 0.05));
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, Multiply)
+{
+ math::Vector3d v(0.1, 0.2, 0.3);
+
+ math::Vector3d vec3 = v * 2.0;
+ EXPECT_EQ(vec3, math::Vector3d(0.2, 0.4, 0.6));
+
+ vec3 *= 4.0;
+ EXPECT_EQ(vec3, math::Vector3d(0.8, 1.6, 2.4));
+
+ // Multiply by zero
+ {
+ // Scalar left and right
+ EXPECT_EQ(0 * v, math::Vector3d::Zero);
+ EXPECT_EQ(v * 0, math::Vector3d::Zero);
+
+ // Element-wise vector multiplication
+ EXPECT_EQ(v * math::Vector3d::Zero, math::Vector3d::Zero);
+ }
+
+ // Multiply by one
+ {
+ // Scalar left and right
+ EXPECT_EQ(1 * v, v);
+ EXPECT_EQ(v * 1, v);
+
+ // Element-wise vector multiplication
+ EXPECT_EQ(v * math::Vector3d::One, v);
+ }
+
+ // Multiply by non-trivial scalar value
+ {
+ const double scalar = 2.5;
+ math::Vector3d expect(0.25, 0.5, 0.75);
+ EXPECT_EQ(scalar * v, expect);
+ EXPECT_EQ(v * scalar, expect);
+ }
+
+ // Multiply by itself element-wise
+ EXPECT_EQ(v*v, math::Vector3d(0.01, 0.04, 0.09));
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, NotEqual)
+{
+ math::Vector3d vec1(0.1, 0.2, 0.3);
+ math::Vector3d vec2(0.2, 0.2, 0.3);
+ math::Vector3d vec3(0.1, 0.2, 0.3);
+
+ EXPECT_TRUE(vec1 != vec2);
+ EXPECT_FALSE(vec1 != vec3);
+}
+
+/////////////////////////////////////////////////
+// Test Equal function with specified tolerance
+TEST(Vector2Test, EqualTolerance)
+{
+ EXPECT_FALSE(math::Vector3d::Zero.Equal(math::Vector3d::One, 1e-6));
+ EXPECT_FALSE(math::Vector3d::Zero.Equal(math::Vector3d::One, 1e-3));
+ EXPECT_FALSE(math::Vector3d::Zero.Equal(math::Vector3d::One, 1e-1));
+ EXPECT_TRUE(math::Vector3d::Zero.Equal(math::Vector3d::One, 1));
+ EXPECT_TRUE(math::Vector3d::Zero.Equal(math::Vector3d::One, 1.1));
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, Finite)
+{
+ math::Vector3d vec1(0.1, 0.2, 0.3);
+
+ EXPECT_TRUE(vec1.IsFinite());
+}
+
+/////////////////////////////////////////////////
+TEST(Vector3dTest, NoException)
+{
+ math::Vector3d v(1, 2, 3);
+ EXPECT_NO_THROW(math::equal(v[0], 1.0));
+ EXPECT_NO_THROW(math::equal(v[1], 2.0));
+ EXPECT_NO_THROW(math::equal(v[2], 3.0));
+
+ EXPECT_NO_THROW(math::equal(v[3], 4.0));
+ EXPECT_DOUBLE_EQ(v[3], 3.0);
+}
+
diff --git a/src/Vector4_TEST.cc b/src/Vector4_TEST.cc
new file mode 100644
index 0000000..9c7c679
--- /dev/null
+++ b/src/Vector4_TEST.cc
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2012 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+
+#include "ignition/math/Helpers.hh"
+#include "ignition/math/Matrix4.hh"
+#include "ignition/math/Vector4.hh"
+
+using namespace ignition;
+
+/////////////////////////////////////////////////
+TEST(Vector4dTest, Vector4d)
+{
+ {
+ math::Vector4d v;
+ EXPECT_TRUE(math::equal(v.X(), 0.0));
+ EXPECT_TRUE(math::equal(v.Y(), 0.0));
+ EXPECT_TRUE(math::equal(v.Z(), 0.0));
+ EXPECT_TRUE(math::equal(v.W(), 0.0));
+ }
+
+ math::Vector4d v1(1, 2, 3, 4);
+ EXPECT_TRUE(math::equal(v1.X(), 1.0));
+ EXPECT_TRUE(math::equal(v1.Y(), 2.0));
+ EXPECT_TRUE(math::equal(v1.Z(), 3.0));
+ EXPECT_TRUE(math::equal(v1.W(), 4.0));
+
+ math::Vector4d v(v1);
+ EXPECT_EQ(v, v1);
+
+ EXPECT_TRUE(math::equal(v.Distance(
+ math::Vector4d(0, 0, 0, 0)), 5.4772, 1e-3));
+
+ // ::Length()
+ v.Set(1, 2, 3, 4);
+ EXPECT_TRUE(math::equal(v.Length(), 5.4772, 1e-3));
+
+ // ::SquaredLength()
+ EXPECT_TRUE(math::equal(v.SquaredLength(), 30.0));
+
+ // ::Normalize
+ v.Normalize();
+ EXPECT_EQ(v, math::Vector4d(0.182574, 0.365148, 0.547723, 0.730297));
+
+ // ::Set
+ v.Set(2, 4, 6, 8);
+ EXPECT_EQ(v, math::Vector4d(2, 4, 6, 8));
+
+ // ::operator= vector4
+ v = v1;
+ EXPECT_EQ(v, v1);
+
+ // ::operator= double
+ v = 1.2;
+ EXPECT_EQ(v, math::Vector4d(1.2, 1.2, 1.2, 1.2));
+
+ // ::operator+ vector4
+ v = v + math::Vector4d(1, 2, 3, 4);
+ EXPECT_EQ(v, math::Vector4d(2.2, 3.2, 4.2, 5.2));
+
+ // ::operator+=
+ v += v;
+ EXPECT_EQ(v, math::Vector4d(4.4, 6.4, 8.4, 10.4));
+
+ // ::operator- vector4
+ v = v - math::Vector4d(1, 1, 1, 1);
+ EXPECT_EQ(v, math::Vector4d(3.4, 5.4, 7.4, 9.4));
+
+ // ::operator-= vector4
+ v -= math::Vector4d(1, 1, 1, 1);
+ EXPECT_EQ(v, math::Vector4d(2.4, 4.4, 6.4, 8.4));
+
+
+ // ::operator/ vector4
+ v = v / math::Vector4d(.1, .1, .1, .1);
+ EXPECT_EQ(v, math::Vector4d(24, 44, 64, 84));
+
+ // ::operator/ double
+ v = v / 2.0;
+ EXPECT_EQ(v, math::Vector4d(12, 22, 32, 42));
+
+ // ::operator/= vector4
+ v /= math::Vector4d(4, 4, 4, 4);
+ EXPECT_EQ(v, math::Vector4d(3, 5.5, 8, 10.5));
+
+ // ::operator/= double
+ v /= .1;
+ EXPECT_EQ(v, math::Vector4d(30, 55, 80, 105));
+
+ // ::operator * matrix4
+ v = v * math::Matrix4d(1, 2, 3, 4,
+ 5, 6, 7, 8,
+ 9, 10, 11, 12,
+ 13, 14, 15, 16);
+ EXPECT_EQ(v, math::Vector4d(2390, 2660, 2930, 3200));
+
+
+ // ::operator * vector4
+ v = v * math::Vector4d(.2, .3, .4, .5);
+ EXPECT_EQ(v, math::Vector4d(478, 798, 1172, 1600));
+
+ // ::operator *= vector4
+ v *= math::Vector4d(2, 4, 6, 8);
+ EXPECT_EQ(v, math::Vector4d(956, 3192, 7032, 12800));
+
+ // ::operator * double
+ v = v * 5.2;
+ EXPECT_EQ(v, math::Vector4d(4971.2, 16598.4, 36566.4, 66560));
+
+ // ::operator *= double
+ v *= 10.0;
+ EXPECT_EQ(v, math::Vector4d(49712, 1.65984e+05, 3.65664e+05, 6.656e+05));
+
+ // ::operator != vector4
+ EXPECT_NE(v, math::Vector4d());
+
+ // ::IsFinite
+ EXPECT_TRUE(v.IsFinite());
+
+ // ::operator[]
+ v[0] = 1;
+ v[1] = 2;
+ v[2] = 3;
+ v[3] = 4;
+ EXPECT_DOUBLE_EQ(v[0], 1);
+ EXPECT_DOUBLE_EQ(v[1], 2);
+ EXPECT_DOUBLE_EQ(v[2], 3);
+ EXPECT_DOUBLE_EQ(v[3], 4);
+}
+
+/////////////////////////////////////////////////
+TEST(Vector4dTest, NoException)
+{
+ math::Vector4d v(1, 2, 3, 4);
+ EXPECT_NO_THROW(math::equal(v[0], 1.0));
+ EXPECT_NO_THROW(math::equal(v[1], 2.0));
+ EXPECT_NO_THROW(math::equal(v[2], 3.0));
+ EXPECT_NO_THROW(math::equal(v[3], 4.0));
+
+ EXPECT_NO_THROW(math::equal(v[4], 5.0));
+ EXPECT_DOUBLE_EQ(v[4], 4.0);
+}
+
+/////////////////////////////////////////////////
+// Test Equal function with specified tolerance
+TEST(Vector2Test, EqualTolerance)
+{
+ EXPECT_FALSE(math::Vector4d::Zero.Equal(math::Vector4d::One, 1e-6));
+ EXPECT_FALSE(math::Vector4d::Zero.Equal(math::Vector4d::One, 1e-3));
+ EXPECT_FALSE(math::Vector4d::Zero.Equal(math::Vector4d::One, 1e-1));
+ EXPECT_TRUE(math::Vector4d::Zero.Equal(math::Vector4d::One, 1));
+ EXPECT_TRUE(math::Vector4d::Zero.Equal(math::Vector4d::One, 1.1));
+}
+
+/////////////////////////////////////////////////
+TEST(Vector4dTest, Add)
+{
+ math::Vector4d vec1(0.1, 0.2, 0.4, 0.8);
+ math::Vector4d vec2(1.1, 2.2, 3.4, 4.8);
+
+ math::Vector4d vec3 = vec1;
+ vec3 += vec2;
+
+ EXPECT_EQ(vec1 + vec2, math::Vector4d(1.2, 2.4, 3.8, 5.6));
+ EXPECT_EQ(vec3, math::Vector4d(1.2, 2.4, 3.8, 5.6));
+
+ // Add zeros
+ {
+ // Scalar left and right
+ EXPECT_EQ(0 + vec1, vec1);
+ EXPECT_EQ(vec1 + 0, vec1);
+
+ // Vector left and right
+ EXPECT_EQ(math::Vector4d::Zero + vec1, vec1);
+ EXPECT_EQ(vec1 + math::Vector4d::Zero, vec1);
+
+ // Addition assignment
+ math::Vector4d vec4(vec1);
+ vec4 += 0;
+ EXPECT_EQ(vec4, vec1);
+ vec4 += math::Vector4d::Zero;
+ EXPECT_EQ(vec4, vec1);
+ }
+
+ // Add non-trivial scalar values left and right
+ {
+ EXPECT_EQ(2.5 + vec1, math::Vector4d(2.6, 2.7, 2.9, 3.3));
+ EXPECT_EQ(vec1 + 2.5, math::Vector4d(2.6, 2.7, 2.9, 3.3));
+
+ math::Vector4d vec4(vec1);
+ vec4 += 2.5;
+ EXPECT_EQ(vec4, math::Vector4d(2.6, 2.7, 2.9, 3.3));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(Vector4dTest, Sub)
+{
+ math::Vector4d vec1(0.1, 0.2, 0.4, 0.8);
+ math::Vector4d vec2(1.1, 2.2, 3.4, 4.8);
+
+ math::Vector4d vec3 = vec2;
+ vec3 -= vec1;
+
+ EXPECT_EQ(vec2 - vec1, math::Vector4d(1.0, 2.0, 3.0, 4.0));
+ EXPECT_EQ(vec3, math::Vector4d(1.0, 2.0, 3.0, 4.0));
+
+ // Subtraction with zeros
+ {
+ // Scalar left and right
+ EXPECT_EQ(0 - vec1, -vec1);
+ EXPECT_EQ(vec1 - 0, vec1);
+
+ // Vector left and right
+ EXPECT_EQ(math::Vector4d::Zero - vec1, -vec1);
+ EXPECT_EQ(vec1 - math::Vector4d::Zero, vec1);
+
+ // Subtraction assignment
+ math::Vector4d vec4(vec1);
+ vec4 -= 0;
+ EXPECT_EQ(vec4, vec1);
+ vec4 -= math::Vector4d::Zero;
+ EXPECT_EQ(vec4, vec1);
+ }
+
+ // Subtract non-trivial scalar values left and right
+ {
+ EXPECT_EQ(2.5 - vec1, math::Vector4d(2.4, 2.3, 2.1, 1.7));
+ EXPECT_EQ(vec1 - 2.5, -math::Vector4d(2.4, 2.3, 2.1, 1.7));
+
+ math::Vector4d vec4(vec1);
+ vec4 -= 2.5;
+ EXPECT_EQ(vec4, -math::Vector4d(2.4, 2.3, 2.1, 1.7));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(Vector4dTest, OperatorStreamOut)
+{
+ math::Vector4d v(0.1, 1.2, 2.3, 0.0);
+ std::ostringstream stream;
+ stream << v;
+ EXPECT_EQ(stream.str(), "0.1 1.2 2.3 0");
+}
+
+/////////////////////////////////////////////////
+TEST(Vector4Test, Multiply)
+{
+ math::Vector4d v(0.1, -4.2, 11.1, 8.4);
+
+ // Multiply by zero
+ {
+ // Scalar left and right
+ EXPECT_EQ(0 * v, math::Vector4d::Zero);
+ EXPECT_EQ(v * 0, math::Vector4d::Zero);
+
+ // Element-wise vector multiplication
+ EXPECT_EQ(v * math::Vector4d::Zero, math::Vector4d::Zero);
+ }
+
+ // Multiply by one
+ {
+ // Scalar left and right
+ EXPECT_EQ(1 * v, v);
+ EXPECT_EQ(v * 1, v);
+
+ // Element-wise vector multiplication
+ EXPECT_EQ(v * math::Vector4d::One, v);
+ }
+
+ // Multiply by non-trivial scalar value
+ {
+ const double scalar = 2.5;
+ math::Vector4d expect(0.25, -10.5, 27.75, 21.0);
+ EXPECT_EQ(scalar * v, expect);
+ EXPECT_EQ(v * scalar, expect);
+ }
+
+ // Multiply by itself element-wise
+ EXPECT_EQ(v*v, math::Vector4d(0.01, 17.64, 123.21, 70.56));
+}
+
+/////////////////////////////////////////////////
+TEST(Vector4dTest, Length)
+{
+ // Zero vector
+ EXPECT_DOUBLE_EQ(math::Vector4d::Zero.Length(), 0.0);
+ EXPECT_DOUBLE_EQ(math::Vector4d::Zero.SquaredLength(), 0.0);
+
+ // One vector
+ EXPECT_DOUBLE_EQ(math::Vector4d::One.Length(), 2.0);
+ EXPECT_DOUBLE_EQ(math::Vector4d::One.SquaredLength(), 4.0);
+
+ // Arbitrary vector
+ math::Vector4d v(0.1, -4.2, 2.5, 1.0);
+ EXPECT_NEAR(v.Length(), 4.98998997995, 1e-10);
+ EXPECT_DOUBLE_EQ(v.SquaredLength(), 24.9);
+}
+
diff --git a/src/graph/CMakeLists.txt b/src/graph/CMakeLists.txt
new file mode 100644
index 0000000..70ba555
--- /dev/null
+++ b/src/graph/CMakeLists.txt
@@ -0,0 +1,7 @@
+
+# Collect source files into the "sources" variable and unit test files into the
+# "gtest_sources" variable
+ign_get_libsources_and_unittests(sources gtest_sources)
+
+# Build the unit tests
+ign_build_tests(TYPE UNIT SOURCES ${gtest_sources})
diff --git a/src/graph/Edge_TEST.cc b/src/graph/Edge_TEST.cc
new file mode 100644
index 0000000..ac6c1a2
--- /dev/null
+++ b/src/graph/Edge_TEST.cc
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+#include <iostream>
+#include <string>
+
+#include "ignition/math/graph/Edge.hh"
+#include "ignition/math/graph/Vertex.hh"
+
+using namespace ignition;
+using namespace math;
+using namespace graph;
+
+// Define a test fixture class template.
+template <class T>
+class EdgeTestFixture : public testing::Test
+{
+};
+
+// The list of edges we want to test.
+using EdgeTypes = ::testing::Types<DirectedEdge<int>,
+ UndirectedEdge<int>>;
+TYPED_TEST_CASE(EdgeTestFixture, EdgeTypes);
+
+/////////////////////////////////////////////////
+TYPED_TEST(EdgeTestFixture, Accessors)
+{
+ {
+ EdgeId id = 1;
+ double weight = 2.0;
+ double newWeight = 3.0;
+ VertexId_P vertices = {0, 1};
+ int data = 3;
+ TypeParam edge(vertices, data, weight, id);
+
+ // Id.
+ EXPECT_EQ(id, edge.Id());
+
+ // Weight.
+ EXPECT_DOUBLE_EQ(weight, edge.Weight());
+ edge.SetWeight(newWeight);
+ EXPECT_DOUBLE_EQ(newWeight, edge.Weight());
+
+ // Vertices.
+ EXPECT_EQ(vertices, edge.Vertices());
+
+ // Data.
+ EXPECT_EQ(data, edge.Data());
+ edge.Data() += 1;
+ EXPECT_EQ(data + 1, edge.Data());
+
+ // Validation.
+ EXPECT_TRUE(edge.Valid());
+ }
+
+ {
+ double weight = 2.0;
+ VertexId_P vertices = {0, 1};
+ int data = 3;
+ TypeParam edge(vertices, data, weight);
+
+ // Id.
+ EXPECT_EQ(kNullId, edge.Id());
+
+ // Weight.
+ EXPECT_DOUBLE_EQ(weight, edge.Weight());
+
+ // Vertices.
+ VertexId_P expectedVertices = {kNullId, kNullId};
+ EXPECT_EQ(expectedVertices, edge.Vertices());
+
+ // Data.
+ EXPECT_EQ(data, edge.Data());
+ edge.Data() += 1;
+ EXPECT_EQ(data + 1, edge.Data());
+
+ // Validation. It does not have a valid Id.
+ EXPECT_FALSE(edge.Valid());
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(EdgeTest, Initializer)
+{
+ VertexId_P vertices = {1, 2};
+ std::string data = "hi";
+
+ {
+ double weight = 2.0;
+ EdgeInitializer<std::string> edgeInitializer(vertices, data, weight);
+ EXPECT_EQ(vertices, edgeInitializer.vertices);
+ EXPECT_EQ(data, edgeInitializer.data);
+ EXPECT_DOUBLE_EQ(weight, edgeInitializer.weight);
+ }
+ {
+ EdgeInitializer<std::string> edgeInitializer(vertices, "hi");
+ EXPECT_EQ(vertices, edgeInitializer.vertices);
+ EXPECT_EQ("hi", edgeInitializer.data);
+ EXPECT_DOUBLE_EQ(1.0, edgeInitializer.weight);
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(EdgeTest, FromToDirected)
+{
+ {
+ EdgeId id = 1;
+ double weight = 2.0;
+ VertexId_P vertices = {0, 1};
+ int data = 3;
+ DirectedEdge<int> edge(vertices, data, weight, id);
+
+ EXPECT_EQ(0u, edge.Tail());
+ EXPECT_EQ(1u, edge.Head());
+
+ EXPECT_EQ(edge.Head(), edge.From(edge.Tail()));
+ EXPECT_EQ(edge.Tail(), edge.To(edge.Head()));
+
+ // It's a directed edge, you cannot go in this direction.
+ EXPECT_EQ(kNullId, edge.From(edge.Head()));
+ EXPECT_EQ(kNullId, edge.To(edge.Tail()));
+
+ // The Id doesn't exit.
+ EXPECT_EQ(kNullId, edge.From(99));
+ EXPECT_EQ(kNullId, edge.To(99));
+ }
+
+ {
+ double weight = 2.0;
+ VertexId_P vertices = {0, 1};
+ int data = 3;
+ DirectedEdge<int> edge(vertices, data, weight);
+ // The edge is not valid because the Id == kNullId.
+ EXPECT_FALSE(edge.Valid());
+
+ EXPECT_EQ(kNullId, edge.From(edge.Tail()));
+ EXPECT_EQ(kNullId, edge.To(edge.Head()));
+ EXPECT_EQ(kNullId, edge.From(edge.Head()));
+ EXPECT_EQ(kNullId, edge.To(edge.Tail()));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(EdgeTest, FromToUndirected)
+{
+ {
+ EdgeId id = 1;
+ double weight = 2.0;
+ VertexId_P vertices = {0, 1};
+ int data = 3;
+ UndirectedEdge<int> edge(vertices, data, weight, id);
+
+ EXPECT_EQ(1u, edge.From(0));
+ EXPECT_EQ(0u, edge.To(1));
+ EXPECT_EQ(0u, edge.From(1));
+ EXPECT_EQ(1u, edge.To(0));
+
+ // The Id doesn't exit.
+ EXPECT_EQ(kNullId, edge.From(99));
+ EXPECT_EQ(kNullId, edge.To(99));
+ }
+
+ {
+ EdgeId id = kNullId;
+ double weight = 2.0;
+ // Only one vertex.
+ VertexId_P vertices = {0, 1};
+ int data = 3;
+ UndirectedEdge<int> edge(vertices, data, weight, id);
+ // The edge is not valid because the Id == kNullId.
+ EXPECT_FALSE(edge.Valid());
+
+ EXPECT_EQ(kNullId, edge.From(0));
+ EXPECT_EQ(kNullId, edge.To(1));
+ EXPECT_EQ(kNullId, edge.From(1));
+ EXPECT_EQ(kNullId, edge.To(0));
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(EdgeTest, StreamInsertionDirected)
+{
+ EdgeId id = 1;
+ double weight = 2.0;
+ VertexId_P vertices = {0, 1};
+ int data = 3;
+ DirectedEdge<int> edge(vertices, data, weight, id);
+
+ std::ostringstream output;
+ output << edge;
+
+ std::string expectedOutput = " 0 -> 1 [label=2];\n";
+ EXPECT_EQ(expectedOutput, output.str());
+}
+
+/////////////////////////////////////////////////
+TEST(EdgeTest, StreamInsertionUndirected)
+{
+ EdgeId id = 1;
+ double weight = 2.0;
+ VertexId_P vertices = {0, 1};
+ int data = 3;
+ UndirectedEdge<int> edge(vertices, data, weight, id);
+
+ std::ostringstream output;
+ output << edge;
+
+ std::string expectedOutput = " 0 -- 1 [label=2];\n";
+ EXPECT_EQ(expectedOutput, output.str());
+}
diff --git a/src/graph/GraphAlgorithms_TEST.cc b/src/graph/GraphAlgorithms_TEST.cc
new file mode 100644
index 0000000..898ce65
--- /dev/null
+++ b/src/graph/GraphAlgorithms_TEST.cc
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+#include <string>
+
+#include "ignition/math/graph/Graph.hh"
+#include "ignition/math/graph/GraphAlgorithms.hh"
+
+using namespace ignition;
+using namespace math;
+using namespace graph;
+
+// Define a test fixture class template.
+template <class T>
+class GraphTestFixture : public testing::Test
+{
+};
+
+// The list of graphs we want to test.
+using GraphTypes = ::testing::Types<DirectedGraph<int, double>,
+ UndirectedGraph<int, double>>;
+TYPED_TEST_CASE(GraphTestFixture, GraphTypes);
+
+/////////////////////////////////////////////////
+TYPED_TEST(GraphTestFixture, BreadthFirstSort)
+{
+ TypeParam graph(
+ {
+ // Vertices.
+ {{"A", 0, 0}, {"B", 1, 1}, {"C", 2, 2}, {"D", 3, 3}, {"E", 4, 4},
+ {"F", 5, 5}, {"G", 6, 6}},
+ // Edges.
+ {{{0, 1}, 2.0}, {{0, 2}, 3.0}, {{0, 4}, 4.0},
+ {{1, 3}, 2.0}, {{1, 5}, 3.0}, {{2, 6}, 4.0},
+ {{5, 4}, 2.0}}
+ });
+
+ auto res = BreadthFirstSort(graph, 0);
+ std::vector<VertexId> expected = {0, 1, 2, 4, 3, 5, 6};
+ EXPECT_EQ(expected, res);
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTest, DepthFirstSortDirected)
+{
+ DirectedGraph<int, double> graph(
+ {
+ // Vertices.
+ {{"A", 0, 0}, {"B", 1, 1}, {"C", 2, 2}, {"D", 3, 3}, {"E", 4, 4},
+ {"F", 5, 5}, {"G", 6, 6}},
+ // Edges.
+ {{{0, 1}, 2.0}, {{0, 2}, 3.0}, {{0, 4}, 4.0}, {{1, 3}, 2.0},
+ {{1, 5}, 3.0}, {{2, 6}, 4.0}, {{5, 4}, 2.0}}
+ });
+
+ auto res = DepthFirstSort(graph, 0);
+ std::vector<VertexId> expected = {0, 4, 2, 6, 1, 5, 3};
+ EXPECT_EQ(expected, res);
+}
+
+/////////////////////////////////////////////////
+TEST(UndirectedGraphTest, DepthFirstSortUndirected)
+{
+ UndirectedGraph<int, double> graph(
+ {
+ // Vertices.
+ {{"A", 0, 0}, {"B", 1, 1}, {"C", 2, 2}, {"D", 3, 3}, {"E", 4, 4},
+ {"F", 5, 5}, {"G", 6, 6}},
+ // Edges.
+ {{{0, 1}, 2.0}, {{0, 2}, 3.0}, {{0, 4}, 4.0}, {{1, 3}, 2.0},
+ {{1, 5}, 3.0}, {{2, 6}, 4.0}, {{5, 4}, 2.0}}
+ });
+
+ auto res = DepthFirstSort(graph, 0);
+ std::vector<VertexId> expected = {0, 4, 5, 1, 3, 2, 6};
+ EXPECT_EQ(expected, res);
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTestFixture, DijkstraUndirected)
+{
+ /// (6) |
+ /// 0-------1 |
+ /// | /|\ |
+ /// | / | \(5) |
+ /// | (2)/ | \ |
+ /// | / | 2 |
+ /// (1)| / (2)| / |
+ /// | / | /(5) |
+ /// |/ |/ |
+ /// 3-------4 |
+ /// (1) |
+ UndirectedGraph<int, double> graph(
+ {
+ // Vertices.
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}, {"3", 3, 3}, {"4", 4, 4}},
+ // Edges.
+ {{{0, 1}, 2.0, 6.0}, {{0, 3}, 3.0, 1.0},
+ {{1, 2}, 4.0, 5.0}, {{1, 3}, 4.0, 2.0}, {{1, 4}, 4.0, 2.0},
+ {{2, 4}, 2.0, 5.0},
+ {{3, 4}, 2.0, 1.0}}
+ });
+
+ // Inexistent source vertex.
+ auto res = Dijkstra(graph, 99);
+ EXPECT_TRUE(res.empty());
+
+ // Inexistent destination vertex.
+ res = Dijkstra(graph, 0, 99);
+ EXPECT_TRUE(res.empty());
+
+ // Calculate all shortest paths from 0.
+ res = Dijkstra(graph, 0);
+
+ ASSERT_NE(res.end(), res.find(0));
+ EXPECT_DOUBLE_EQ(0, res.at(0).first);
+ EXPECT_EQ(0u, res.at(0).second);
+ ASSERT_NE(res.end(), res.find(1));
+ EXPECT_DOUBLE_EQ(3, res.at(1).first);
+ EXPECT_EQ(3u, res.at(1).second);
+ ASSERT_NE(res.end(), res.find(2));
+ EXPECT_DOUBLE_EQ(7, res.at(2).first);
+ EXPECT_EQ(4u, res.at(2).second);
+ ASSERT_NE(res.end(), res.find(3));
+ EXPECT_DOUBLE_EQ(1, res.at(3).first);
+ EXPECT_EQ(0u, res.at(3).second);
+ ASSERT_NE(res.end(), res.find(4));
+ EXPECT_DOUBLE_EQ(2, res.at(4).first);
+ EXPECT_EQ(3u, res.at(4).second);
+
+ // Calculate the shortest path between 0 and 1.
+ res = Dijkstra(graph, 0, 1);
+
+ ASSERT_NE(res.end(), res.find(1));
+ EXPECT_DOUBLE_EQ(3, res.at(1).first);
+ EXPECT_EQ(3u, res.at(1).second);
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTestFixture, DijkstraDirected)
+{
+ /// (6) |
+ /// 0------>1 |
+ /// | /|\ |
+ /// | / | \(5) |
+ /// | (2)/ | ┘ |
+ /// | / | 2 |
+ /// (1)| / (2)| / |
+ /// | / | /(5) |
+ /// VL VL |
+ /// 3------>4 |
+ /// (1) |
+ DirectedGraph<int, double> graph(
+ {
+ // Vertices.
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}, {"3", 3, 3}, {"4", 4, 4}},
+ // Edges.
+ {{{0, 1}, 2.0, 6.0}, {{0, 3}, 3.0, 1.0},
+ {{1, 2}, 4.0, 5.0}, {{1, 3}, 4.0, 2.0}, {{1, 4}, 4.0, 2.0},
+ {{2, 4}, 2.0, 5.0},
+ {{3, 4}, 2.0, 1.0}}
+ });
+
+ // Inexistent source vertex.
+ auto res = Dijkstra(graph, 99);
+ EXPECT_TRUE(res.empty());
+
+ // Inexistent destination vertex.
+ res = Dijkstra(graph, 0, 99);
+ EXPECT_TRUE(res.empty());
+
+ // Calculate all shortest paths from 0.
+ res = Dijkstra(graph, 0);
+
+ ASSERT_NE(res.end(), res.find(0));
+ EXPECT_DOUBLE_EQ(0, res.at(0).first);
+ EXPECT_EQ(0u, res.at(0).second);
+ ASSERT_NE(res.end(), res.find(1));
+ EXPECT_DOUBLE_EQ(6, res.at(1).first);
+ EXPECT_EQ(0u, res.at(1).second);
+ ASSERT_NE(res.end(), res.find(2));
+ EXPECT_DOUBLE_EQ(11, res.at(2).first);
+ EXPECT_EQ(1u, res.at(2).second);
+ ASSERT_NE(res.end(), res.find(3));
+ EXPECT_DOUBLE_EQ(1, res.at(3).first);
+ EXPECT_EQ(0u, res.at(3).second);
+ ASSERT_NE(res.end(), res.find(4));
+ EXPECT_DOUBLE_EQ(2, res.at(4).first);
+ EXPECT_EQ(3u, res.at(4).second);
+
+ // Calculate the shortest path between 0 and 1.
+ res = Dijkstra(graph, 0, 1);
+
+ ASSERT_NE(res.end(), res.find(1));
+ EXPECT_DOUBLE_EQ(6, res.at(1).first);
+ EXPECT_EQ(0u, res.at(1).second);
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTestFixture, ConnectedComponents)
+{
+ // Connected components of an empty graph.
+ UndirectedGraph<int, double> emptyGraph;
+ auto components = ConnectedComponents(emptyGraph);
+ EXPECT_TRUE(components.empty());
+
+ UndirectedGraph<int, double> graph(
+ {
+ // Vertices.
+ {{"A", 0, 0}, {"B", 1, 1}, {"C", 2, 2}, {"D", 3, 3}, {"E", 4, 4}},
+ // Edges.
+ {{{0, 2}, 2.0, 6.0},
+ {{1, 4}, 4.0, 5.0}}
+ });
+
+ // Connected components of a graph with three components.
+ components = ConnectedComponents(graph);
+ ASSERT_EQ(3u, components.size());
+
+ // Component #0.
+ auto component = components.at(0);
+ auto vertices = component.Vertices();
+ EXPECT_EQ(2u, vertices.size());
+ EXPECT_NE(vertices.end(), vertices.find(0));
+ EXPECT_NE(vertices.end(), vertices.find(2));
+ for (auto const &vertexPair : vertices)
+ {
+ auto &vertex = vertexPair.second.get();
+ switch (vertex.Id())
+ {
+ case 0:
+ {
+ EXPECT_EQ("A", vertex.Name());
+ EXPECT_EQ(0, vertex.Data());
+ break;
+ }
+ case 2:
+ {
+ EXPECT_EQ("C", vertex.Name());
+ EXPECT_EQ(2, vertex.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+
+ // Component #1.
+ component = components.at(1);
+ vertices = component.Vertices();
+ EXPECT_EQ(2u, vertices.size());
+ EXPECT_NE(vertices.end(), vertices.find(1));
+ EXPECT_NE(vertices.end(), vertices.find(4));
+ for (auto const &vertexPair : vertices)
+ {
+ auto &vertex = vertexPair.second.get();
+ switch (vertex.Id())
+ {
+ case 1:
+ {
+ EXPECT_EQ("B", vertex.Name());
+ EXPECT_EQ(1, vertex.Data());
+ break;
+ }
+ case 4:
+ {
+ EXPECT_EQ("E", vertex.Name());
+ EXPECT_EQ(4, vertex.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+
+ // Component #2.
+ component = components.at(2);
+ vertices = component.Vertices();
+ EXPECT_EQ(1u, vertices.size());
+ EXPECT_NE(vertices.end(), vertices.find(3));
+ for (auto const &vertexPair : vertices)
+ {
+ auto &vertex = vertexPair.second.get();
+ switch (vertex.Id())
+ {
+ case 3:
+ {
+ EXPECT_EQ("D", vertex.Name());
+ EXPECT_EQ(3, vertex.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+}
+
diff --git a/src/graph/GraphDirected_TEST.cc b/src/graph/GraphDirected_TEST.cc
new file mode 100644
index 0000000..81984f5
--- /dev/null
+++ b/src/graph/GraphDirected_TEST.cc
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+#include <iostream>
+#include <string>
+
+#include "ignition/math/graph/Graph.hh"
+
+using namespace ignition;
+using namespace math;
+using namespace graph;
+
+/////////////////////////////////////////////////
+TEST(GraphTest, Edges)
+{
+ // Create a graph with edges [(v0-->v0), (v0-->v1), (v1-->v2), (v2-->v0)]
+ DirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 0}, 1.0}, {{0, 1}, 2.0}, {{1, 2}, 3.0}, {{2, 0}, 4.0}}
+ });
+
+ auto edges = graph.Edges();
+ EXPECT_EQ(4u, edges.size());
+
+ // Check the Ids.
+ for (auto i = 0; i < 4; ++i)
+ EXPECT_NE(edges.end(), edges.find(i));
+
+ // Check the references.
+ for (auto const &edgePair : edges)
+ {
+ auto &edge = edgePair.second.get();
+ switch (edge.Id())
+ {
+ case 0:
+ {
+ EXPECT_EQ(0u, edge.Tail());
+ EXPECT_EQ(0u, edge.Head());
+ EXPECT_DOUBLE_EQ(1.0, edge.Data());
+ break;
+ }
+ case 1:
+ {
+ EXPECT_EQ(0u, edge.Tail());
+ EXPECT_EQ(1u, edge.Head());
+ EXPECT_DOUBLE_EQ(2.0, edge.Data());
+ break;
+ }
+ case 2:
+ {
+ EXPECT_EQ(1u, edge.Tail());
+ EXPECT_EQ(2u, edge.Head());
+ EXPECT_DOUBLE_EQ(3.0, edge.Data());
+ break;
+ }
+ case 3:
+ {
+ EXPECT_EQ(2u, edge.Tail());
+ EXPECT_EQ(0u, edge.Head());
+ EXPECT_DOUBLE_EQ(4.0, edge.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTest, AdjacentsFrom)
+{
+ // Create a graph with edges [(v0-->v0), (v0-->v1), (v1-->v2), (v2-->v0)]
+ DirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 0}, 1.0}, {{0, 1}, 2.0}, {{1, 2}, 3.0}, {{2, 0}, 4.0}}
+ });
+
+ // Try to get the adjacents from an inexistent vertex.
+ auto adjacents = graph.AdjacentsFrom(kNullId);
+ EXPECT_TRUE(adjacents.empty());
+
+ adjacents = graph.AdjacentsFrom(0);
+ EXPECT_EQ(2u, adjacents.size());
+ EXPECT_NE(adjacents.end(), adjacents.find(0));
+ EXPECT_NE(adjacents.end(), adjacents.find(1));
+
+ auto vertex = graph.VertexFromId(0);
+ adjacents = graph.AdjacentsFrom(vertex);
+ EXPECT_EQ(2u, adjacents.size());
+ EXPECT_NE(adjacents.end(), adjacents.find(0));
+ EXPECT_NE(adjacents.end(), adjacents.find(1));
+
+ // Check the references.
+ for (auto const &vertexPair : adjacents)
+ {
+ auto &neighborVertex = vertexPair.second.get();
+ switch (neighborVertex.Id())
+ {
+ case 0:
+ {
+ EXPECT_EQ("0", neighborVertex.Name());
+ EXPECT_EQ(0, neighborVertex.Data());
+ break;
+ }
+ case 1:
+ {
+ EXPECT_EQ("1", neighborVertex.Name());
+ EXPECT_EQ(1, neighborVertex.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTest, AdjacentsTo)
+{
+ // Create a graph with edges [(v0-->v0), (v0-->v1), (v1-->v2), (v2-->v1)]
+ DirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 0}, 1.0}, {{0, 1}, 2.0}, {{1, 2}, 3.0}, {{2, 1}, 4.0}}
+ });
+
+ // Try to get the adjacents to an inexistent vertex.
+ auto adjacents = graph.AdjacentsTo(kNullId);
+ EXPECT_TRUE(adjacents.empty());
+
+ adjacents = graph.AdjacentsTo(0);
+ EXPECT_EQ(1u, adjacents.size());
+ EXPECT_NE(adjacents.end(), adjacents.find(0));
+
+ adjacents = graph.AdjacentsTo(1);
+ EXPECT_EQ(2u, adjacents.size());
+ EXPECT_NE(adjacents.end(), adjacents.find(0));
+ EXPECT_NE(adjacents.end(), adjacents.find(2));
+
+ auto vertex = graph.VertexFromId(2);
+ adjacents = graph.AdjacentsTo(vertex);
+ EXPECT_EQ(1u, adjacents.size());
+ EXPECT_NE(adjacents.end(), adjacents.find(1));
+
+ // Check the references.
+ for (auto const &vertexPair : adjacents)
+ {
+ auto &neighborVertex = vertexPair.second.get();
+ switch (neighborVertex.Id())
+ {
+ case 1:
+ {
+ EXPECT_EQ("1", neighborVertex.Name());
+ EXPECT_EQ(1, neighborVertex.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTest, IncidentsFrom)
+{
+ // Create a graph with edges [(v0-->v0), (v0-->v1), (v1-->v0), (v1-->v2)]
+ DirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 0}, 1.0}, {{0, 1}, 2.0}, {{1, 0}, 3.0}, {{1, 2}, 4.0}}
+ });
+
+ auto incidents = graph.IncidentsFrom(0);
+ EXPECT_EQ(2u, incidents.size());
+ EXPECT_NE(incidents.end(), incidents.find(0));
+ EXPECT_NE(incidents.end(), incidents.find(1));
+
+ auto vertex = graph.VertexFromId(1);
+ incidents = graph.IncidentsFrom(vertex);
+ EXPECT_EQ(2u, incidents.size());
+ EXPECT_NE(incidents.end(), incidents.find(2));
+ EXPECT_NE(incidents.end(), incidents.find(3));
+
+ // Check the references.
+ for (auto const &edgePair : incidents)
+ {
+ auto &edge = edgePair.second.get();
+ switch (edge.Id())
+ {
+ case 2:
+ {
+ auto vertices = edge.Vertices();
+ EXPECT_EQ(1u, vertices.first);
+ EXPECT_EQ(0u, vertices.second);
+ EXPECT_DOUBLE_EQ(3.0, edge.Data());
+ break;
+ }
+ case 3:
+ {
+ auto vertices = edge.Vertices();
+ EXPECT_EQ(1u, vertices.first);
+ EXPECT_EQ(2u, vertices.second);
+ EXPECT_DOUBLE_EQ(4.0, edge.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+
+ incidents = graph.IncidentsFrom(2);
+ EXPECT_TRUE(incidents.empty());
+
+ // Try an inexistent vertex.
+ incidents = graph.IncidentsFrom(kNullId);
+ EXPECT_EQ(0u, incidents.size());
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTest, IncidentsTo)
+{
+ // Create a graph with edges [(v0-->v0), (v0-->v1), (v1-->v2), (v2-->v0)]
+ DirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 0}, 1.0}, {{0, 1}, 2.0}, {{1, 2}, 3.0}, {{2, 0}, 4.0}}
+ });
+
+ auto incidents = graph.IncidentsTo(0);
+ EXPECT_EQ(2u, incidents.size());
+ EXPECT_NE(incidents.end(), incidents.find(0));
+ EXPECT_NE(incidents.end(), incidents.find(3));
+
+ auto vertex = graph.VertexFromId(0);
+ incidents = graph.IncidentsTo(vertex);
+ EXPECT_EQ(2u, incidents.size());
+ EXPECT_NE(incidents.end(), incidents.find(0));
+ EXPECT_NE(incidents.end(), incidents.find(3));
+
+ // Check the references.
+ for (auto const &edgePair : incidents)
+ {
+ auto &edge = edgePair.second.get();
+ switch (edge.Id())
+ {
+ case 0:
+ {
+ auto vertices = edge.Vertices();
+ EXPECT_EQ(0u, vertices.first);
+ EXPECT_EQ(0u, vertices.second);
+ EXPECT_DOUBLE_EQ(1.0, edge.Data());
+ break;
+ }
+ case 3:
+ {
+ auto vertices = edge.Vertices();
+ EXPECT_EQ(2u, vertices.first);
+ EXPECT_EQ(0u, vertices.second);
+ EXPECT_DOUBLE_EQ(4.0, edge.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTest, InDegree)
+{
+ // Create a graph with edges [(v0-->v0), (v0-->v1), (v1-->v2), (v2-->v1) x 2]
+ DirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 0}, 1.0}, {{0, 1}, 2.0}, {{1, 2}, 3.0}, {{2, 1}, 4.0}, {{2, 1}, 4.0}}
+ });
+
+ EXPECT_EQ(1u, graph.InDegree(0));
+ EXPECT_EQ(1u, graph.InDegree(graph.VertexFromId(0)));
+ EXPECT_EQ(3u, graph.InDegree(1));
+ EXPECT_EQ(3u, graph.InDegree(graph.VertexFromId(1)));
+ EXPECT_EQ(1u, graph.InDegree(2));
+ EXPECT_EQ(1u, graph.InDegree(graph.VertexFromId(2)));
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTest, OutDegree)
+{
+ // Create a graph with edges [(v0-->v0), (v0-->v1), (v1-->v0), (v1-->v2) x 2]
+ DirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 0}, 1.0}, {{0, 1}, 2.0}, {{1, 0}, 3.0}, {{1, 2}, 4.0}, {{1, 2}, 5.0}}
+ });
+
+ EXPECT_EQ(2u, graph.OutDegree(0));
+ EXPECT_EQ(2u, graph.OutDegree(graph.VertexFromId(0)));
+ EXPECT_EQ(3u, graph.OutDegree(1));
+ EXPECT_EQ(3u, graph.OutDegree(graph.VertexFromId(1)));
+ EXPECT_EQ(0u, graph.OutDegree(2));
+ EXPECT_EQ(0u, graph.OutDegree(graph.VertexFromId(2)));
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTest, AddEdge)
+{
+ // Create a graph with three vertices.
+ DirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {}
+ });
+
+ // Create some edges [(v0-->v1), (v1-->v2), (v2-->v0)]
+ auto &e0 = graph.AddEdge({0, 1}, 2.0);
+ auto &e1 = graph.AddEdge({1, 2}, 3.0);
+ auto &e2 = graph.AddEdge({2, 0}, 4.0);
+
+ // Check the edge content.
+ EXPECT_DOUBLE_EQ(2.0, e0.Data());
+ EXPECT_DOUBLE_EQ(3.0, e1.Data());
+ EXPECT_DOUBLE_EQ(4.0, e2.Data());
+
+ // Change some content and verity it.
+ e2.Data() = 5.0;
+ e2.SetWeight(6.0);
+ auto edge = graph.EdgeFromId(e2.Id());
+ EXPECT_DOUBLE_EQ(5.0, edge.Data());
+ EXPECT_DOUBLE_EQ(6.0, edge.Weight());
+
+ // Check that the edges point to the right vertices.
+ EXPECT_EQ(0u, e0.Tail());
+ EXPECT_EQ(1u, e0.Head());
+
+ auto edges = graph.Edges();
+ EXPECT_EQ(3u, edges.size());
+
+ // Try to add an edge with an incorrect tail.
+ edge = graph.AddEdge({kNullId, 1}, 2.0);
+ EXPECT_EQ(kNullId, edge.Id());
+ EXPECT_EQ(3u, graph.Edges().size());
+
+ // Try to add an edge with an incorrect head.
+ edge = graph.AddEdge({0, kNullId}, 2.0);
+ EXPECT_EQ(kNullId, edge.Id());
+ EXPECT_EQ(3u, graph.Edges().size());
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTest, RemoveEdge)
+{
+ // Create a graph with edges [(v0-->v1), (v1-->v2), (v2-->v0)]
+ DirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 1}, 2.0}, {{1, 2}, 3.0}, {{2, 0}, 4.0}}
+ });
+
+ // Remove a nonexistent edge shouldn't cause any effect.
+ EXPECT_FALSE(graph.RemoveEdge(kNullId));
+ EXPECT_EQ(3u, graph.Edges().size());
+ EXPECT_EQ(1u, graph.IncidentsTo(1).size());
+
+ // Remove the edge (v0-->v1)
+ EXPECT_TRUE(graph.RemoveEdge(0));
+ EXPECT_EQ(2u, graph.Edges().size());
+ EXPECT_EQ(0u, graph.IncidentsTo(1).size());
+
+ // Remove the edge (v1-->v2)
+ auto edge = graph.EdgeFromId(1);
+ EXPECT_TRUE(graph.RemoveEdge(edge));
+ EXPECT_EQ(1u, graph.Edges().size());
+
+ // Try to remove an edge that doesn't exist anymore.
+ EXPECT_FALSE(graph.RemoveEdge(1));
+ EXPECT_EQ(1u, graph.Edges().size());
+
+ // Remove the edge (v2-->v0)
+ EXPECT_TRUE(graph.RemoveEdge(2));
+ EXPECT_EQ(0u, graph.Edges().size());
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTest, RemoveVertex)
+{
+ // Create a graph with edges [(v0-->v1), (v1-->v2), (v2-->v0)]
+ DirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 1}, 2.0}, {{1, 2}, 3.0}, {{2, 0}, 4.0}}
+ });
+
+ // Remove a nonexistent vertex shouldn't cause any effect.
+ EXPECT_FALSE(graph.RemoveVertex(kNullId));
+ EXPECT_EQ(3u, graph.Vertices().size());
+ EXPECT_EQ(1u, graph.AdjacentsFrom(1).size());
+
+ // Remove vertex #2.
+ EXPECT_TRUE(graph.RemoveVertex(2));
+ EXPECT_EQ(2u, graph.Vertices().size());
+ EXPECT_EQ(1u, graph.Edges().size());
+ EXPECT_EQ(0u, graph.AdjacentsFrom(1).size());
+
+ // Remove vertex #1.
+ auto vertex = graph.VertexFromId(1);
+ EXPECT_TRUE(graph.RemoveVertex(vertex));
+ EXPECT_EQ(1u, graph.Vertices().size());
+ EXPECT_TRUE(graph.Edges().empty());
+
+ // Try to remove a vertex (#1) that doesn't exist anymore.
+ EXPECT_FALSE(graph.RemoveVertex(1));
+ EXPECT_EQ(1u, graph.Vertices().size());
+ EXPECT_TRUE(graph.Edges().empty());
+
+ // Remove vertex #0.
+ EXPECT_TRUE(graph.RemoveVertex(0));
+ EXPECT_TRUE(graph.Vertices().empty());
+
+ EXPECT_TRUE(graph.Empty());
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTest, RemoveVertices)
+{
+ // Create a graph with edges [(v0-->v1), (v1-->v2), (v2-->v3), (v3-->v0)]
+ DirectedGraph<int, double> graph(
+ {
+ {{"v0", 0, 0}, {"v1", 1, 1}, {"common", 2, 2}, {"common", 3, 3}},
+ {{{0, 1}, 2.0}, {{1, 2}, 3.0}, {{2, 3}, 4.0}, {{3, 0}, 5.0}}
+ });
+
+ // Try to remove a node with a name that doesn't exist.
+ EXPECT_EQ(graph.RemoveVertices("wrong_name"), 0u);
+ EXPECT_EQ(4u, graph.Vertices().size());
+ EXPECT_EQ(1u, graph.AdjacentsFrom(1).size());
+
+ // Remove two vertices at the same time.
+ EXPECT_EQ(2u, graph.RemoveVertices("common"));
+ EXPECT_EQ(2u, graph.Vertices().size());
+ EXPECT_EQ(1u, graph.Edges().size());
+
+ EXPECT_EQ(0u, graph.AdjacentsFrom(1).size());
+
+ EXPECT_EQ(1u, graph.RemoveVertices("v1"));
+ EXPECT_EQ(1u, graph.Vertices().size());
+ EXPECT_TRUE(graph.Edges().empty());
+
+ EXPECT_EQ(1u, graph.RemoveVertices("v0"));
+ EXPECT_TRUE(graph.Vertices().empty());
+
+ EXPECT_TRUE(graph.Empty());
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTest, StreamInsertion)
+{
+ // Create a graph with 4 vertices and
+ // edges [(v0-->v0), (v0-->v1), (v1-->v2), (v2-->v3)]
+ DirectedGraph<int, double> graph(
+ {
+ {{"v0", 0, 0}, {"v1", 1, 1}, {"v2", 2, 2}, {"v3", 3, 3}},
+ {{{0, 1}, 2.0, 4.0}, {{0, 0}, 2.0, 6.0}, {{1, 2}, 3.0}, {{2, 0}, 4.0}}
+ });
+
+ std::ostringstream output;
+ output << graph;
+
+ std::cout << "# Use this snippet with your favorite DOT tool." << std::endl;
+ std::cout << graph << std::endl;
+
+ for (auto const &s : {"digraph {\n",
+ " 0 [label=\"v0 (0)\"];\n",
+ " 1 [label=\"v1 (1)\"];\n",
+ " 2 [label=\"v2 (2)\"];\n",
+ " 3 [label=\"v3 (3)\"];\n",
+ " 0 -> 1 [label=4];\n",
+ " 0 -> 0 [label=6];\n",
+ " 1 -> 2 [label=1];\n",
+ " 2 -> 0 [label=1];\n"})
+ {
+ EXPECT_NE(std::string::npos, output.str().find(s));
+ }
+}
diff --git a/src/graph/GraphUndirected_TEST.cc b/src/graph/GraphUndirected_TEST.cc
new file mode 100644
index 0000000..dd34afc
--- /dev/null
+++ b/src/graph/GraphUndirected_TEST.cc
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+#include <iostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "ignition/math/graph/Graph.hh"
+
+using namespace ignition;
+using namespace math;
+using namespace graph;
+
+/////////////////////////////////////////////////
+TEST(UndirectedGraphTest, Edges)
+{
+ // Create a graph with edges [(v0--v0), (v0--v1), (v1--v2), (v2--v0)]
+ UndirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 0}, 1.0}, {{0, 1}, 2.0}, {{1, 2}, 3.0}, {{2, 0}, 4.0}}
+ });
+
+ auto edges = graph.Edges();
+ EXPECT_EQ(4u, edges.size());
+
+ // Check the Ids.
+ for (auto i = 0; i < 4; ++i)
+ EXPECT_NE(edges.end(), edges.find(i));
+
+ // Check the references.
+ for (auto const &edgePair : edges)
+ {
+ auto &edge = edgePair.second.get();
+ switch (edge.Id())
+ {
+ case 0:
+ {
+ auto vertices = edge.Vertices();
+ EXPECT_EQ(0u, vertices.first);
+ EXPECT_EQ(0u, vertices.second);
+ EXPECT_DOUBLE_EQ(1.0, edge.Data());
+ break;
+ }
+ case 1:
+ {
+ auto vertices = edge.Vertices();
+ EXPECT_EQ(0u, vertices.first);
+ EXPECT_EQ(1u, vertices.second);
+ EXPECT_DOUBLE_EQ(2.0, edge.Data());
+ break;
+ }
+ case 2:
+ {
+ auto vertices = edge.Vertices();
+ EXPECT_EQ(1u, vertices.first);
+ EXPECT_EQ(2u, vertices.second);
+ EXPECT_DOUBLE_EQ(3.0, edge.Data());
+ break;
+ }
+ case 3:
+ {
+ auto vertices = edge.Vertices();
+ EXPECT_EQ(2u, vertices.first);
+ EXPECT_EQ(0u, vertices.second);
+ EXPECT_DOUBLE_EQ(4.0, edge.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(UndirectedGraphTest, AdjacentsFrom)
+{
+ // Create a graph with edges [(v0--v0), (v0--v1), (v1--v2), (v2--v0)]
+ UndirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 0}, 1.0}, {{0, 1}, 2.0}, {{1, 2}, 3.0}, {{2, 0}, 4.0}}
+ });
+
+ // Try to get the adjacents from an inexistent vertex.
+ auto adjacents = graph.AdjacentsFrom(kNullId);
+ EXPECT_TRUE(adjacents.empty());
+
+ adjacents = graph.AdjacentsFrom(0);
+ EXPECT_EQ(3u, adjacents.size());
+ EXPECT_NE(adjacents.end(), adjacents.find(0));
+ EXPECT_NE(adjacents.end(), adjacents.find(1));
+ EXPECT_NE(adjacents.end(), adjacents.find(2));
+
+ auto vertex = graph.VertexFromId(0);
+ adjacents = graph.AdjacentsFrom(vertex);
+ EXPECT_EQ(3u, adjacents.size());
+ EXPECT_NE(adjacents.end(), adjacents.find(0));
+ EXPECT_NE(adjacents.end(), adjacents.find(1));
+ EXPECT_NE(adjacents.end(), adjacents.find(2));
+
+ // Check the references.
+ for (auto const &vertexPair : adjacents)
+ {
+ auto &neighborVertex = vertexPair.second.get();
+ switch (neighborVertex.Id())
+ {
+ case 0:
+ {
+ EXPECT_EQ("0", neighborVertex.Name());
+ EXPECT_EQ(0, neighborVertex.Data());
+ break;
+ }
+ case 1:
+ {
+ EXPECT_EQ("1", neighborVertex.Name());
+ EXPECT_EQ(1, neighborVertex.Data());
+ break;
+ }
+ case 2:
+ {
+ EXPECT_EQ("2", neighborVertex.Name());
+ EXPECT_EQ(2, neighborVertex.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(UndirectedGraphTest, AdjacentsTo)
+{
+ // Create a graph with edges [(v0--v0), (v0--v1), (v1--v2)]
+ UndirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 0}, 1.0}, {{0, 1}, 2.0}, {{1, 2}, 3.0}}
+ });
+
+ // Try to get the adjacents from an inexistent vertex.
+ auto adjacents = graph.AdjacentsTo(kNullId);
+ EXPECT_TRUE(adjacents.empty());
+
+ adjacents = graph.AdjacentsTo(0);
+ EXPECT_EQ(2u, adjacents.size());
+ EXPECT_NE(adjacents.end(), adjacents.find(0));
+ EXPECT_NE(adjacents.end(), adjacents.find(1));
+
+ auto vertex = graph.VertexFromId(1);
+ adjacents = graph.AdjacentsTo(vertex);
+ EXPECT_EQ(2u, adjacents.size());
+ EXPECT_NE(adjacents.end(), adjacents.find(0));
+ EXPECT_NE(adjacents.end(), adjacents.find(2));
+
+ // Check the references.
+ for (auto const &vertexPair : adjacents)
+ {
+ auto &neighborVertex = vertexPair.second.get();
+ switch (neighborVertex.Id())
+ {
+ case 0:
+ {
+ EXPECT_EQ("0", neighborVertex.Name());
+ EXPECT_EQ(0, neighborVertex.Data());
+ break;
+ }
+ case 2:
+ {
+ EXPECT_EQ("2", neighborVertex.Name());
+ EXPECT_EQ(2, neighborVertex.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(UndirectedGraphTest, IncidentsFrom)
+{
+ // Create a graph with edges [(v0--v0), (v0--v1), (v1--v2)]
+ UndirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 0}, 1.0}, {{0, 1}, 2.0}, {{1, 2}, 3.0}}
+ });
+
+ auto incidents = graph.IncidentsFrom(0);
+ EXPECT_EQ(2u, incidents.size());
+ EXPECT_NE(incidents.end(), incidents.find(0));
+ EXPECT_NE(incidents.end(), incidents.find(1));
+
+ auto vertex = graph.VertexFromId(1);
+ incidents = graph.IncidentsFrom(vertex);
+ EXPECT_EQ(2u, incidents.size());
+ EXPECT_NE(incidents.end(), incidents.find(1));
+ EXPECT_NE(incidents.end(), incidents.find(2));
+
+ // Check the references.
+ for (auto const &edgePair : incidents)
+ {
+ auto &edge = edgePair.second.get();
+ switch (edge.Id())
+ {
+ case 1:
+ {
+ auto vertices = edge.Vertices();
+ EXPECT_EQ(0u, vertices.first);
+ EXPECT_EQ(1u, vertices.second);
+ EXPECT_DOUBLE_EQ(2.0, edge.Data());
+ break;
+ }
+ case 2:
+ {
+ auto vertices = edge.Vertices();
+ EXPECT_EQ(1u, vertices.first);
+ EXPECT_EQ(2u, vertices.second);
+ EXPECT_DOUBLE_EQ(3.0, edge.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+
+ // Try an inexistent vertex.
+ incidents = graph.IncidentsFrom(kNullId);
+ EXPECT_EQ(0u, incidents.size());
+}
+
+/////////////////////////////////////////////////
+TEST(UndirectedGraphTest, IncidentsTo)
+{
+ // Create a graph with edges [(v0--v0), (v0--v1), (v1--v2), (v2--v0)]
+ UndirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 0}, 1.0}, {{0, 1}, 2.0}, {{1, 2}, 3.0}, {{2, 0}, 4.0}}
+ });
+
+ auto incidents = graph.IncidentsTo(0);
+ EXPECT_EQ(3u, incidents.size());
+ EXPECT_NE(incidents.end(), incidents.find(0));
+ EXPECT_NE(incidents.end(), incidents.find(1));
+ EXPECT_NE(incidents.end(), incidents.find(3));
+
+ auto vertex = graph.VertexFromId(0);
+ incidents = graph.IncidentsTo(vertex);
+ EXPECT_EQ(3u, incidents.size());
+ EXPECT_NE(incidents.end(), incidents.find(0));
+ EXPECT_NE(incidents.end(), incidents.find(1));
+ EXPECT_NE(incidents.end(), incidents.find(3));
+
+ // Check the references.
+ for (auto const &edgePair : incidents)
+ {
+ auto &edge = edgePair.second.get();
+ switch (edge.Id())
+ {
+ case 0:
+ {
+ auto vertices = edge.Vertices();
+ EXPECT_EQ(0u, vertices.first);
+ EXPECT_EQ(0u, vertices.second);
+ EXPECT_DOUBLE_EQ(1.0, edge.Data());
+ break;
+ }
+ case 1:
+ {
+ auto vertices = edge.Vertices();
+ EXPECT_EQ(0u, vertices.first);
+ EXPECT_EQ(1u, vertices.second);
+ EXPECT_DOUBLE_EQ(2.0, edge.Data());
+ break;
+ }
+ case 3:
+ {
+ auto vertices = edge.Vertices();
+ EXPECT_EQ(2u, vertices.first);
+ EXPECT_EQ(0u, vertices.second);
+ EXPECT_DOUBLE_EQ(4.0, edge.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(UndirectedGraphTest, InDegree)
+{
+ // Create a graph with edges [(v0--v0), (v0--v1), (v1--v2) x 2]
+ UndirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 0}, 1.0}, {{0, 1}, 2.0}, {{1, 2}, 3.0}, {{1, 2}, 4.0}}
+ });
+
+ EXPECT_EQ(2u, graph.InDegree(0));
+ EXPECT_EQ(2u, graph.InDegree(graph.VertexFromId(0)));
+ EXPECT_EQ(3u, graph.InDegree(1));
+ EXPECT_EQ(3u, graph.InDegree(graph.VertexFromId(1)));
+ EXPECT_EQ(2u, graph.InDegree(2));
+ EXPECT_EQ(2u, graph.InDegree(graph.VertexFromId(2)));
+}
+
+/////////////////////////////////////////////////
+TEST(UndirectedGraphTest, OutDegree)
+{
+ // Create a graph with edges [(v0--v0), (v0--v1), (v1--v2) x 2]
+ UndirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 0}, 1.0}, {{0, 1}, 2.0}, {{1, 2}, 3.0}, {{1, 2}, 4.0}}
+ });
+
+ EXPECT_EQ(2u, graph.OutDegree(0));
+ EXPECT_EQ(2u, graph.OutDegree(graph.VertexFromId(0)));
+ EXPECT_EQ(3u, graph.OutDegree(1));
+ EXPECT_EQ(3u, graph.OutDegree(graph.VertexFromId(1)));
+ EXPECT_EQ(2u, graph.OutDegree(2));
+ EXPECT_EQ(2u, graph.OutDegree(graph.VertexFromId(2)));
+}
+
+/////////////////////////////////////////////////
+TEST(UndirectedGraphTest, AddEdge)
+{
+ // Create a graph with three vertices.
+ UndirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {}
+ });
+
+ // Create some edges [(v0--v1), (v1--v2), (v2--v0)]
+ auto &e0 = graph.AddEdge({0, 1}, 2.0);
+ auto &e1 = graph.AddEdge({1, 2}, 3.0);
+ auto &e2 = graph.AddEdge({2, 0}, 4.0);
+
+ // Check the edge content.
+ EXPECT_DOUBLE_EQ(2.0, e0.Data());
+ EXPECT_DOUBLE_EQ(3.0, e1.Data());
+ EXPECT_DOUBLE_EQ(4.0, e2.Data());
+
+ // Change some content and verity it.
+ e2.Data() = 5.0;
+ e2.SetWeight(6.0);
+ auto edge = graph.EdgeFromId(e2.Id());
+ EXPECT_DOUBLE_EQ(5.0, edge.Data());
+ EXPECT_DOUBLE_EQ(6.0, edge.Weight());
+
+ // Check that the edges point to the right vertices.
+ EXPECT_EQ(0u, e0.Vertices().first);
+
+ auto edges = graph.Edges();
+ EXPECT_EQ(3u, edges.size());
+
+ // Try to add an edge with an incorrect tail.
+ edge = graph.AddEdge({kNullId, 1}, 2.0);
+ EXPECT_EQ(kNullId, edge.Id());
+ EXPECT_EQ(3u, graph.Edges().size());
+
+ // Try to add an edge with an incorrect head.
+ edge = graph.AddEdge({0, kNullId}, 2.0);
+ EXPECT_EQ(kNullId, edge.Id());
+ EXPECT_EQ(3u, graph.Edges().size());
+}
+
+/////////////////////////////////////////////////
+TEST(UndirectedGraphTest, RemoveEdge)
+{
+ // Create a graph with edges [(v0--v1), (v1--v2), (v2--v0)]
+ UndirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 1}, 2.0}, {{1, 2}, 3.0}, {{2, 0}, 4.0}}
+ });
+
+ // Remove a nonexistent edge shouldn't cause any effect.
+ EXPECT_FALSE(graph.RemoveEdge(kNullId));
+ EXPECT_EQ(3u, graph.Edges().size());
+ EXPECT_EQ(2u, graph.IncidentsTo(1).size());
+
+ // Remove the edge (v0--v1)
+ EXPECT_TRUE(graph.RemoveEdge(0));
+ EXPECT_EQ(2u, graph.Edges().size());
+ EXPECT_EQ(1u, graph.IncidentsTo(1).size());
+
+ // Remove the edge (v1--v2)
+ auto edge = graph.EdgeFromId(1);
+ EXPECT_TRUE(graph.RemoveEdge(edge));
+ EXPECT_EQ(1u, graph.Edges().size());
+
+ // Try to remove an edge that doesn't exist anymore.
+ EXPECT_FALSE(graph.RemoveEdge(1));
+ EXPECT_EQ(1u, graph.Edges().size());
+
+ // Remove the edge (v2--v0)
+ EXPECT_TRUE(graph.RemoveEdge(2));
+ EXPECT_EQ(0u, graph.Edges().size());
+}
+
+/////////////////////////////////////////////////
+TEST(UndirectedGraphTest, RemoveVertex)
+{
+ // Create a graph with edges [(v0--v1), (v1--v2), (v2--v0)]
+ UndirectedGraph<int, double> graph(
+ {
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ {{{0, 1}, 2.0}, {{1, 2}, 3.0}, {{2, 0}, 4.0}}
+ });
+
+ // Remove a nonexistent vertex shouldn't cause any effect.
+ EXPECT_FALSE(graph.RemoveVertex(kNullId));
+ EXPECT_EQ(3u, graph.Vertices().size());
+ EXPECT_EQ(2u, graph.AdjacentsFrom(1).size());
+
+ // Remove vertex #2.
+ EXPECT_TRUE(graph.RemoveVertex(2));
+ EXPECT_EQ(2u, graph.Vertices().size());
+ EXPECT_EQ(1u, graph.Edges().size());
+ EXPECT_EQ(1u, graph.AdjacentsFrom(1).size());
+
+ // Remove vertex #1.
+ auto vertex = graph.VertexFromId(1);
+ EXPECT_TRUE(graph.RemoveVertex(vertex));
+ EXPECT_EQ(1u, graph.Vertices().size());
+ EXPECT_TRUE(graph.Edges().empty());
+
+ // Try to remove a vertex (#1) that doesn't exist anymore.
+ EXPECT_FALSE(graph.RemoveVertex(1));
+ EXPECT_EQ(1u, graph.Vertices().size());
+ EXPECT_TRUE(graph.Edges().empty());
+
+ // Remove vertex #0.
+ EXPECT_TRUE(graph.RemoveVertex(0));
+ EXPECT_TRUE(graph.Vertices().empty());
+ EXPECT_TRUE(graph.Empty());
+}
+
+/////////////////////////////////////////////////
+TEST(UndirectedGraphTest, RemoveVertices)
+{
+ // Create a graph with edges [(v0--v1), (v1--v2), (v2--v3), (v3--v0)]
+ UndirectedGraph<int, double> graph(
+ {
+ {{"v0", 0, 0}, {"v1", 1, 1}, {"common", 2, 2}, {"common", 3, 3}},
+ {{{0, 1}, 2.0}, {{1, 2}, 3.0}, {{2, 3}, 4.0}, {{3, 0}, 5.0}}
+ });
+
+ // Try to remove a node with a name that doesn't exist.
+ EXPECT_EQ(0u, graph.RemoveVertices("wrong_name"));
+ EXPECT_EQ(4u, graph.Vertices().size());
+ EXPECT_EQ(2u, graph.AdjacentsFrom(1).size());
+
+ // Remove two vertices at the same time.
+ EXPECT_EQ(2u, graph.RemoveVertices("common"));
+ EXPECT_EQ(2u, graph.Vertices().size());
+ EXPECT_EQ(1u, graph.Edges().size());
+ EXPECT_EQ(1u, graph.AdjacentsFrom(1).size());
+
+ // Remove vertex #1.
+ EXPECT_EQ(1u, graph.RemoveVertices("v1"));
+ EXPECT_EQ(1u, graph.Vertices().size());
+ EXPECT_TRUE(graph.Edges().empty());
+
+ // Remove vertex #0.
+ EXPECT_EQ(1u, graph.RemoveVertices("v0"));
+ EXPECT_TRUE(graph.Vertices().empty());
+ EXPECT_TRUE(graph.Empty());
+}
+
+/////////////////////////////////////////////////
+TEST(UndirectedGraphTest, StreamInsertion)
+{
+ // Create a graph with 4 vertices and
+ // edges [(v0-->v0), (v0-->v1), (v1-->v2), (v2-->v3)]
+ UndirectedGraph<int, double> graph(
+ {
+ {{"v0", 0, 0}, {"v1", 1, 1}, {"v2", 2, 2}, {"v3", 3, 3}},
+ {{{0, 1}, 2.0, 4.0}, {{0, 0}, 2.0, 6.0}, {{1, 2}, 3.0}, {{2, 0}, 4.0}}
+ });
+
+ std::ostringstream output;
+ output << graph;
+
+ std::cout << "# Use this snippet with your favorite DOT tool." << std::endl;
+ std::cout << graph << std::endl;
+
+ for (auto const &s : {"graph {\n",
+ " 0 [label=\"v0 (0)\"];\n",
+ " 1 [label=\"v1 (1)\"];\n",
+ " 2 [label=\"v2 (2)\"];\n",
+ " 3 [label=\"v3 (3)\"];\n"})
+ {
+ EXPECT_NE(std::string::npos, output.str().find(s));
+ }
+
+ // We don't really know the order in which the edges will be printed.
+ // We also don't know the order in which the vertices on each edge will be
+ // printed.
+ std::vector<std::pair<std::string, std::string>> expectedEdges =
+ {
+ {" 0 -- 0 [label=6];\n", " 0 -- 0 [label=6];\n"},
+ {" 0 -- 1 [label=4];\n", " 1 -- 0 [label=4];\n"},
+ {" 1 -- 2 [label=1];\n", " 2 -- 1 [label=1];\n"},
+ {" 0 -- 2 [label=1];\n", " 2 -- 0 [label=1];\n"}
+ };
+ for (auto const &edge : expectedEdges)
+ {
+ EXPECT_TRUE((output.str().find(std::get<0>(edge)) != std::string::npos) ||
+ (output.str().find(std::get<1>(edge)) != std::string::npos));
+ }
+}
diff --git a/src/graph/Graph_TEST.cc b/src/graph/Graph_TEST.cc
new file mode 100644
index 0000000..1d75e70
--- /dev/null
+++ b/src/graph/Graph_TEST.cc
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+#include <string>
+
+#include "ignition/math/graph/Graph.hh"
+
+using namespace ignition;
+using namespace math;
+using namespace graph;
+
+// Define a test fixture class template.
+template <class T>
+class GraphTestFixture : public testing::Test
+{
+};
+
+// Simulate a class with the maximum allowed number of vertices used.
+template <typename V, typename E>
+class MockVerticesFullUndirectedGraph : public UndirectedGraph<V, E>
+{
+ // Default constructor.
+ public: MockVerticesFullUndirectedGraph()
+ {
+ this->nextVertexId = MAX_UI64;
+ }
+};
+
+// Simulate a class with the maximum allowed number of edges used.
+template <typename V, typename E>
+class MockEdgesFullUndirectedGraph : public UndirectedGraph<V, E>
+{
+ // Default constructor.
+ public: MockEdgesFullUndirectedGraph()
+ {
+ this->nextEdgeId = MAX_UI64;
+ }
+};
+
+// The list of graphs we want to test.
+using GraphTypes = ::testing::Types<DirectedGraph<int, double>,
+ UndirectedGraph<int, double>>;
+TYPED_TEST_CASE(GraphTestFixture, GraphTypes);
+
+/////////////////////////////////////////////////
+TYPED_TEST(GraphTestFixture, UniformInitialization)
+{
+ {
+ TypeParam graph(
+ {
+ // Create vertices with custom Ids.
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ // Create edges.
+ {{{0, 1}, 0.0}, {{1, 2}, 0.0}}
+ });
+
+ // Verify the vertices.
+ auto vertices = graph.Vertices();
+ EXPECT_EQ(3u, vertices.size());
+
+ for (int i = 0; i < 3; ++i)
+ {
+ unsigned int iu = i;
+ ASSERT_NE(vertices.find(i), vertices.end());
+ auto v = vertices.at(i).get();
+ EXPECT_EQ(std::to_string(i), v.Name());
+ EXPECT_EQ(iu, v.Id());
+ EXPECT_EQ(i, v.Data());
+ }
+
+ // Verify the edges.
+ auto edges = graph.Edges();
+ EXPECT_EQ(2u, edges.size());
+ }
+ {
+ TypeParam graph(
+ {
+ // Create vertices with automatic Id selection.
+ {{"0", 0}, {"1", 1}, {"2", 2}},
+ // Create edges.
+ {{{0, 1}, 0.0}, {{1, 2}, 0.0}}
+ });
+
+ // Verify the vertices.
+ auto vertices = graph.Vertices();
+ EXPECT_EQ(3u, vertices.size());
+
+ for (int i = 0; i < 3; ++i)
+ {
+ unsigned int iu = i;
+ ASSERT_NE(vertices.find(i), vertices.end());
+ auto v = vertices.at(i).get();
+ EXPECT_EQ(std::to_string(i), v.Name());
+ EXPECT_EQ(iu, v.Id());
+ EXPECT_EQ(i, v.Data());
+ }
+
+ // Verify the edges.
+ auto edges = graph.Edges();
+ EXPECT_EQ(2u, edges.size());
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(GraphTest, BadUniformInitializationMock)
+{
+ // There's no space for more vertices (mocked).
+ {
+ MockVerticesFullUndirectedGraph<int, double> graph;
+ graph.AddVertex("0", 0);
+ auto vertices = graph.Vertices();
+ EXPECT_EQ(0u, vertices.size());
+ }
+
+ // There's no space for more edges (mocked).
+ {
+ MockEdgesFullUndirectedGraph<int, double> graph;
+ graph.AddVertex("0", 0);
+ graph.AddVertex("1", 1);
+ graph.AddEdge({0, 1}, 1.0);
+ auto edges = graph.Edges();
+ EXPECT_EQ(0u, edges.size());
+ }
+}
+
+/////////////////////////////////////////////////
+TYPED_TEST(GraphTestFixture, BadUniformInitialization)
+{
+ // Bad graph initialization: repeated vertex Id.
+ {
+ TypeParam graph(
+ {
+ // Create vertices with custom Ids.
+ {{"0", 0, 0}, {"1", 1, 0}, {"1", 1, 1}},
+ // Create edges.
+ {{{0, 1}, 0.0}}
+ });
+
+ // Verify the vertices.
+ auto vertices = graph.Vertices();
+ EXPECT_EQ(2u, vertices.size());
+
+ for (int i = 0; i < 2; ++i)
+ {
+ unsigned int iu = i;
+ ASSERT_NE(vertices.find(i), vertices.end());
+ auto v = vertices.at(i).get();
+ EXPECT_EQ(std::to_string(i), v.Name());
+ EXPECT_EQ(iu, v.Id());
+ EXPECT_EQ(i, v.Data());
+ }
+ }
+ // Bad graph initialization: edges referencing an inexistent vertex.
+ {
+ TypeParam graph(
+ {
+ // Create vertices with custom Ids.
+ {{"0", 0, 0}, {"1", 1, 1}, {"2", 2, 2}},
+ // Create edges.
+ {{{0, 3}, 0.0}}
+ });
+
+ // Verify the edges.
+ auto edges = graph.Edges();
+ EXPECT_EQ(0u, edges.size());
+ }
+}
+
+/////////////////////////////////////////////////
+TYPED_TEST(GraphTestFixture, VertexFromId)
+{
+ // Mutable version of VertexFromId().
+ {
+ TypeParam graph;
+
+ // Create some vertices.
+ auto &v0 = graph.AddVertex("0", 0, 0);
+ EXPECT_EQ("0", v0.Name());
+ graph.AddVertex("1", 1, 1);
+ graph.AddVertex("2", 2, 2);
+
+ auto v = graph.VertexFromId(v0.Id());
+ EXPECT_EQ(v0.Id(), v.Id());
+
+ // Id not found.
+ v = graph.VertexFromId(500);
+ EXPECT_EQ(kNullId, v.Id());
+ }
+
+ // Non-mutable version of VertexFromId().
+ {
+ const TypeParam graph(
+ {
+ {{"0", 0}, {"1", 1}, {"2", 2}},
+ {}
+ });
+
+ auto v = graph.VertexFromId(0);
+ EXPECT_EQ(0u, v.Id());
+
+ // Id not found.
+ v = graph.VertexFromId(500);
+ EXPECT_EQ(kNullId, v.Id());
+ }
+}
+
+/////////////////////////////////////////////////
+TYPED_TEST(GraphTestFixture, Vertices)
+{
+ TypeParam graph(
+ {
+ {{"0", 10, 0}, {"1", 20, 1}, {"2", 30, 2}},
+ {{{0, 1}, 0.0}, {{1, 2}, 0.0}}
+ });
+
+ auto vertices = graph.Vertices();
+ EXPECT_EQ(3u, vertices.size());
+
+ // Check that the vertex Ids start from 0.
+ EXPECT_NE(vertices.end(), vertices.find(0));
+ EXPECT_NE(vertices.end(), vertices.find(1));
+ EXPECT_NE(vertices.end(), vertices.find(2));
+
+ // Check the references.
+ for (auto const &vertexPair : vertices)
+ {
+ auto &vertex = vertexPair.second.get();
+ switch (vertex.Id())
+ {
+ case 0:
+ {
+ EXPECT_EQ("0", vertex.Name());
+ EXPECT_EQ(10, vertex.Data());
+ break;
+ }
+ case 1:
+ {
+ EXPECT_EQ("1", vertex.Name());
+ EXPECT_EQ(20, vertex.Data());
+ break;
+ }
+ case 2:
+ {
+ EXPECT_EQ("2", vertex.Name());
+ EXPECT_EQ(30, vertex.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+}
+
+/////////////////////////////////////////////////
+TYPED_TEST(GraphTestFixture, VerticesNames)
+{
+ // Create a few vertices with two of them sharing the same name.
+ TypeParam graph(
+ {
+ {{"vertex_0", 0}, {"vertex_1", 1}, {"common", 2}, {"common", 3}},
+ {}
+ });
+
+ auto vertices = graph.Vertices("common");
+ EXPECT_EQ(2u, vertices.size());
+
+ // Check the Ids.
+ EXPECT_NE(vertices.end(), vertices.find(2));
+ EXPECT_NE(vertices.end(), vertices.find(3));
+
+ // Check the references.
+ for (auto const &vertexPair : vertices)
+ {
+ auto &vertex = vertexPair.second.get();
+ switch (vertex.Id())
+ {
+ case 2:
+ {
+ EXPECT_EQ("common", vertex.Name());
+ EXPECT_EQ(2, vertex.Data());
+ break;
+ }
+ case 3:
+ {
+ EXPECT_EQ("common", vertex.Name());
+ EXPECT_EQ(3, vertex.Data());
+ break;
+ }
+ default:
+ FAIL();
+ };
+ }
+}
+
+/////////////////////////////////////////////////
+TYPED_TEST(GraphTestFixture, Empty)
+{
+ TypeParam graph;
+
+ EXPECT_TRUE(graph.Empty());
+
+ // Create a vertex.
+ auto &v0 = graph.AddVertex("0", 0);
+ ASSERT_TRUE(v0.Valid());
+ EXPECT_FALSE(graph.Empty());
+}
+
+/////////////////////////////////////////////////
+TYPED_TEST(GraphTestFixture, AddVertex)
+{
+ TypeParam graph;
+
+ // Create some vertices without Id.
+ auto &v0 = graph.AddVertex("0", 0);
+ EXPECT_TRUE(v0.Id() != kNullId);
+ auto &v1 = graph.AddVertex("1", 1);
+ EXPECT_TRUE(v1.Id() != kNullId);
+ auto &v2 = graph.AddVertex("2", 2);
+ EXPECT_TRUE(v2.Id() != kNullId);
+
+ // Create a vertex with Id.
+ auto &v3 = graph.AddVertex("3", 5, 3);
+ EXPECT_EQ(3u, v3.Id());
+ EXPECT_EQ(5, v3.Data());
+ EXPECT_EQ("3", v3.Name());
+
+ // Create a vertex with an already used Id.
+ auto &v4 = graph.AddVertex("3", 0, 3);
+ ASSERT_TRUE(v4.Id() == kNullId);
+
+ auto vertices = graph.Vertices();
+ EXPECT_EQ(4u, vertices.size());
+
+ // Change data in v3 and verify that is propagated into the graph.
+ v3.Data() = 10;
+ auto &vertex = graph.VertexFromId(v3.Id());
+ EXPECT_EQ(10, vertex.Data());
+
+ // Try to change data in v4 and verify that is not propagated into the graph.
+ v4.Data() = 20;
+ for (auto const &vertexPair : vertices)
+ {
+ auto &v = vertexPair.second.get();
+ EXPECT_NE(20, v.Data());
+ }
+}
+
+/////////////////////////////////////////////////
+TYPED_TEST(GraphTestFixture, EdgeFromId)
+{
+ // Mutable version of EdgeFromId().
+ {
+ TypeParam graph(
+ {
+ {{"0", 0}, {"1", 1}, {"2", 2}},
+ {{{0, 1}, 2.0}, {{1, 2}, 3.0}}
+ });
+
+ auto e = graph.EdgeFromId(1);
+ EXPECT_EQ(1u, e.Id());
+
+ // Id not found.
+ e = graph.EdgeFromId(500);
+ EXPECT_EQ(kNullId, e.Id());
+ }
+
+ // Non-mutable version of EdgeFromId().
+ {
+ const TypeParam graph(
+ {
+ {{"0", 0}, {"1", 1}, {"2", 2}},
+ {{{0, 1}, 2.0}, {{1, 2}, 3.0}}
+ });
+
+ auto e = graph.EdgeFromId(1);
+ EXPECT_EQ(1u, e.Id());
+
+ // Id not found.
+ e = graph.EdgeFromId(500);
+ EXPECT_EQ(kNullId, e.Id());
+ }
+}
+
+/////////////////////////////////////////////////
+TYPED_TEST(GraphTestFixture, EdgelessInDegree)
+{
+ TypeParam graph;
+
+ // add a bunch of vertices but no edges
+ const int vertexCount = 10000;
+ for (int i = 0; i < vertexCount; ++i)
+ {
+ auto &v = graph.AddVertex(std::to_string(i), i);
+ EXPECT_TRUE(v.Valid());
+ }
+
+ for (auto const &idVertex : graph.Vertices())
+ {
+ EXPECT_EQ(0u, graph.InDegree(idVertex.first));
+ }
+}
+
+/////////////////////////////////////////////////
+TYPED_TEST(GraphTestFixture, EdgelessOutDegree)
+{
+ TypeParam graph;
+
+ // add a bunch of vertices but no edges
+ const int vertexCount = 10000;
+ for (int i = 0; i < vertexCount; ++i)
+ {
+ auto &v = graph.AddVertex(std::to_string(i), i);
+ EXPECT_TRUE(v.Valid());
+ }
+
+ for (auto const &idVertex : graph.Vertices())
+ {
+ EXPECT_EQ(0u, graph.OutDegree(idVertex.first));
+ }
+}
diff --git a/src/graph/Vertex_TEST.cc b/src/graph/Vertex_TEST.cc
new file mode 100644
index 0000000..9b24cd6
--- /dev/null
+++ b/src/graph/Vertex_TEST.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 Open Source Robotics Foundation
+ *
+ * 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 <gtest/gtest.h>
+#include <iostream>
+#include <string>
+
+#include "ignition/math/graph/Vertex.hh"
+
+using namespace ignition;
+using namespace math;
+using namespace graph;
+
+/////////////////////////////////////////////////
+TEST(VertexTest, Accessors)
+{
+ {
+ int data = 5;
+ Vertex<int> vertex("", data);
+ EXPECT_TRUE(vertex.Name().empty());
+ EXPECT_EQ(vertex.Data(), data);
+ EXPECT_EQ(vertex.Id(), kNullId);
+ EXPECT_FALSE(vertex.Valid());
+ }
+
+ {
+ std::string name = "my_vertex";
+ int data = 10;
+ VertexId id = 2;
+ Vertex<int> vertex(name, data, id);
+ EXPECT_EQ(vertex.Name(), name);
+ EXPECT_EQ(vertex.Id(), id);
+ EXPECT_EQ(vertex.Data(), data);
+ // Modify the data
+ vertex.Data() += 1;
+ EXPECT_EQ(vertex.Data(), data + 1);
+ EXPECT_TRUE(vertex.Valid());
+ }
+}
+
+/////////////////////////////////////////////////
+TEST(VertexTest, StreamInsertion)
+{
+ std::string name = "my_vertex";
+ int data = 10;
+ VertexId id = 2;
+ Vertex<int> vertex(name, data, id);
+
+ std::ostringstream output;
+ output << vertex;
+
+ std::string expectedOutput = " " + std::to_string(id) + " [label=\"" + name +
+ " (" + std::to_string(id) + ")\"];\n";
+ EXPECT_EQ(output.str(), expectedOutput);
+}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000..b40c9d6
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,22 @@
+include_directories (
+ ${PROJECT_SOURCE_DIR}/test/gtest/include
+ ${PROJECT_SOURCE_DIR}/test/gtest
+ ${PROJECT_SOURCE_DIR}/test
+)
+
+configure_file (test_config.h.in ${PROJECT_BINARY_DIR}/test_config.h)
+
+# Build gtest
+add_library(gtest STATIC gtest/src/gtest-all.cc)
+add_library(gtest_main STATIC gtest/src/gtest_main.cc)
+target_link_libraries(gtest_main gtest)
+set(GTEST_LIBRARY "${PROJECT_BINARY_DIR}/test/libgtest.a")
+set(GTEST_MAIN_LIBRARY "${PROJECT_BINARY_DIR}/test/libgtest_main.a")
+
+execute_process(COMMAND cmake -E remove_directory ${CMAKE_BINARY_DIR}/test_results)
+execute_process(COMMAND cmake -E make_directory ${CMAKE_BINARY_DIR}/test_results)
+include_directories(${GTEST_INCLUDE_DIRS})
+
+add_subdirectory(integration)
+add_subdirectory(performance)
+add_subdirectory(regression)
diff --git a/test/gtest/cmake/internal_utils.cmake b/test/gtest/cmake/internal_utils.cmake
new file mode 100644
index 0000000..8cb2189
--- /dev/null
+++ b/test/gtest/cmake/internal_utils.cmake
@@ -0,0 +1,227 @@
+# Defines functions and macros useful for building Google Test and
+# Google Mock.
+#
+# Note:
+#
+# - This file will be run twice when building Google Mock (once via
+# Google Test's CMakeLists.txt, and once via Google Mock's).
+# Therefore it shouldn't have any side effects other than defining
+# the functions and macros.
+#
+# - The functions/macros defined in this file may depend on Google
+# Test and Google Mock's option() definitions, and thus must be
+# called *after* the options have been defined.
+
+# Tweaks CMake's default compiler/linker settings to suit Google Test's needs.
+#
+# This must be a macro(), as inside a function string() can only
+# update variables in the function scope.
+macro(fix_default_compiler_settings_)
+ if (MSVC)
+ # For MSVC, CMake sets certain flags to defaults we want to override.
+ # This replacement code is taken from sample in the CMake Wiki at
+ # http://www.cmake.org/Wiki/CMake_FAQ#Dynamic_Replace.
+ foreach (flag_var
+ CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
+ CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+ if (NOT BUILD_SHARED_LIBS AND NOT gtest_force_shared_crt)
+ # When Google Test is built as a shared library, it should also use
+ # shared runtime libraries. Otherwise, it may end up with multiple
+ # copies of runtime library data in different modules, resulting in
+ # hard-to-find crashes. When it is built as a static library, it is
+ # preferable to use CRT as static libraries, as we don't have to rely
+ # on CRT DLLs being available. CMake always defaults to using shared
+ # CRT libraries, so we override that default here.
+ string(REPLACE "/MD" "-MT" ${flag_var} "${${flag_var}}")
+ endif()
+
+ # We prefer more strict warning checking for building Google Test.
+ # Replaces /W3 with /W4 in defaults.
+ string(REPLACE "/W3" "-W4" ${flag_var} "${${flag_var}}")
+ endforeach()
+ endif()
+endmacro()
+
+# Defines the compiler/linker flags used to build Google Test and
+# Google Mock. You can tweak these definitions to suit your need. A
+# variable's value is empty before it's explicitly assigned to.
+macro(config_compiler_and_linker)
+ if (NOT gtest_disable_pthreads)
+ # Defines CMAKE_USE_PTHREADS_INIT and CMAKE_THREAD_LIBS_INIT.
+ find_package(Threads)
+ endif()
+
+ fix_default_compiler_settings_()
+ if (MSVC)
+ # Newlines inside flags variables break CMake's NMake generator.
+ # TODO(vladl at google.com): Add -RTCs and -RTCu to debug builds.
+ set(cxx_base_flags "-GS -W4 -WX -wd4127 -wd4251 -wd4275 -nologo -J -Zi")
+ if (MSVC_VERSION LESS 1400)
+ # Suppress spurious warnings MSVC 7.1 sometimes issues.
+ # Forcing value to bool.
+ set(cxx_base_flags "${cxx_base_flags} -wd4800")
+ # Copy constructor and assignment operator could not be generated.
+ set(cxx_base_flags "${cxx_base_flags} -wd4511 -wd4512")
+ # Compatibility warnings not applicable to Google Test.
+ # Resolved overload was found by argument-dependent lookup.
+ set(cxx_base_flags "${cxx_base_flags} -wd4675")
+ endif()
+ set(cxx_base_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32")
+ set(cxx_base_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN")
+ set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1")
+ set(cxx_no_exception_flags "-D_HAS_EXCEPTIONS=0")
+ set(cxx_no_rtti_flags "-GR-")
+ elseif (CMAKE_COMPILER_IS_GNUCXX)
+ set(cxx_base_flags "-Wall -Wshadow")
+ set(cxx_exception_flags "-fexceptions")
+ set(cxx_no_exception_flags "-fno-exceptions")
+ # Until version 4.3.2, GCC doesn't define a macro to indicate
+ # whether RTTI is enabled. Therefore we define GTEST_HAS_RTTI
+ # explicitly.
+ set(cxx_no_rtti_flags "-fno-rtti -DGTEST_HAS_RTTI=0")
+ set(cxx_strict_flags
+ "-Wextra -Wno-unused-parameter -Wno-missing-field-initializers")
+ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "SunPro")
+ set(cxx_exception_flags "-features=except")
+ # Sun Pro doesn't provide macros to indicate whether exceptions and
+ # RTTI are enabled, so we define GTEST_HAS_* explicitly.
+ set(cxx_no_exception_flags "-features=no%except -DGTEST_HAS_EXCEPTIONS=0")
+ set(cxx_no_rtti_flags "-features=no%rtti -DGTEST_HAS_RTTI=0")
+ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "VisualAge" OR
+ CMAKE_CXX_COMPILER_ID STREQUAL "XL")
+ # CMake 2.8 changes Visual Age's compiler ID to "XL".
+ set(cxx_exception_flags "-qeh")
+ set(cxx_no_exception_flags "-qnoeh")
+ # Until version 9.0, Visual Age doesn't define a macro to indicate
+ # whether RTTI is enabled. Therefore we define GTEST_HAS_RTTI
+ # explicitly.
+ set(cxx_no_rtti_flags "-qnortti -DGTEST_HAS_RTTI=0")
+ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "HP")
+ set(cxx_base_flags "-AA -mt")
+ set(cxx_exception_flags "-DGTEST_HAS_EXCEPTIONS=1")
+ set(cxx_no_exception_flags "+noeh -DGTEST_HAS_EXCEPTIONS=0")
+ # RTTI can not be disabled in HP aCC compiler.
+ set(cxx_no_rtti_flags "")
+ endif()
+
+ if (CMAKE_USE_PTHREADS_INIT) # The pthreads library is available and allowed.
+ set(cxx_base_flags "${cxx_base_flags} -DGTEST_HAS_PTHREAD=1")
+ else()
+ set(cxx_base_flags "${cxx_base_flags} -DGTEST_HAS_PTHREAD=0")
+ endif()
+
+ # For building gtest's own tests and samples.
+ set(cxx_exception "${CMAKE_CXX_FLAGS} ${cxx_base_flags} ${cxx_exception_flags}")
+ set(cxx_no_exception
+ "${CMAKE_CXX_FLAGS} ${cxx_base_flags} ${cxx_no_exception_flags}")
+ set(cxx_default "${cxx_exception}")
+ set(cxx_no_rtti "${cxx_default} ${cxx_no_rtti_flags}")
+ set(cxx_use_own_tuple "${cxx_default} -DGTEST_USE_OWN_TR1_TUPLE=1")
+
+ # For building the gtest libraries.
+ set(cxx_strict "${cxx_default} ${cxx_strict_flags}")
+endmacro()
+
+# Defines the gtest & gtest_main libraries. User tests should link
+# with one of them.
+function(cxx_library_with_type name type cxx_flags)
+ # type can be either STATIC or SHARED to denote a static or shared library.
+ # ARGN refers to additional arguments after 'cxx_flags'.
+ add_library(${name} ${type} ${ARGN})
+ set_target_properties(${name}
+ PROPERTIES
+ COMPILE_FLAGS "${cxx_flags}")
+ if (BUILD_SHARED_LIBS OR type STREQUAL "SHARED")
+ set_target_properties(${name}
+ PROPERTIES
+ COMPILE_DEFINITIONS "GTEST_CREATE_SHARED_LIBRARY=1")
+ endif()
+ if (CMAKE_USE_PTHREADS_INIT)
+ target_link_libraries(${name} ${CMAKE_THREAD_LIBS_INIT})
+ endif()
+endfunction()
+
+########################################################################
+#
+# Helper functions for creating build targets.
+
+function(cxx_shared_library name cxx_flags)
+ cxx_library_with_type(${name} SHARED "${cxx_flags}" ${ARGN})
+endfunction()
+
+function(cxx_library name cxx_flags)
+ cxx_library_with_type(${name} "" "${cxx_flags}" ${ARGN})
+endfunction()
+
+# cxx_executable_with_flags(name cxx_flags libs srcs...)
+#
+# creates a named C++ executable that depends on the given libraries and
+# is built from the given source files with the given compiler flags.
+function(cxx_executable_with_flags name cxx_flags libs)
+ add_executable(${name} ${ARGN})
+ if (cxx_flags)
+ set_target_properties(${name}
+ PROPERTIES
+ COMPILE_FLAGS "${cxx_flags}")
+ endif()
+ if (BUILD_SHARED_LIBS)
+ set_target_properties(${name}
+ PROPERTIES
+ COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1")
+ endif()
+ # To support mixing linking in static and dynamic libraries, link each
+ # library in with an extra call to target_link_libraries.
+ foreach (lib "${libs}")
+ target_link_libraries(${name} ${lib})
+ endforeach()
+endfunction()
+
+# cxx_executable(name dir lib srcs...)
+#
+# creates a named target that depends on the given libs and is built
+# from the given source files. dir/name.cc is implicitly included in
+# the source file list.
+function(cxx_executable name dir libs)
+ cxx_executable_with_flags(
+ ${name} "${cxx_default}" "${libs}" "${dir}/${name}.cc" ${ARGN})
+endfunction()
+
+# Sets PYTHONINTERP_FOUND and PYTHON_EXECUTABLE.
+find_package(PythonInterp)
+
+# cxx_test_with_flags(name cxx_flags libs srcs...)
+#
+# creates a named C++ test that depends on the given libs and is built
+# from the given source files with the given compiler flags.
+function(cxx_test_with_flags name cxx_flags libs)
+ cxx_executable_with_flags(${name} "${cxx_flags}" "${libs}" ${ARGN})
+ add_test(${name} ${name})
+endfunction()
+
+# cxx_test(name libs srcs...)
+#
+# creates a named test target that depends on the given libs and is
+# built from the given source files. Unlike cxx_test_with_flags,
+# test/name.cc is already implicitly included in the source file list.
+function(cxx_test name libs)
+ cxx_test_with_flags("${name}" "${cxx_default}" "${libs}"
+ "test/${name}.cc" ${ARGN})
+endfunction()
+
+# py_test(name)
+#
+# creates a Python test with the given name whose main module is in
+# test/name.py. It does nothing if Python is not installed.
+function(py_test name)
+ # We are not supporting Python tests on Linux yet as they consider
+ # all Linux environments to be google3 and try to use google3 features.
+ if (PYTHONINTERP_FOUND)
+ # ${CMAKE_BINARY_DIR} is known at configuration time, so we can
+ # directly bind it from cmake. ${CTEST_CONFIGURATION_TYPE} is known
+ # only at ctest runtime (by calling ctest -c <Configuration>), so
+ # we have to escape $ to delay variable substitution here.
+ add_test(${name}
+ ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
+ --build_dir=${CMAKE_CURRENT_BINARY_DIR}/\${CTEST_CONFIGURATION_TYPE})
+ endif()
+endfunction()
diff --git a/test/gtest/gtest-1.7.0.diff b/test/gtest/gtest-1.7.0.diff
new file mode 100644
index 0000000..e878396
--- /dev/null
+++ b/test/gtest/gtest-1.7.0.diff
@@ -0,0 +1,44 @@
+diff -r a5e72dd0ecf3 test/gtest/include/gtest/gtest-typed-test.h
+--- a/test/gtest/include/gtest/gtest-typed-test.h Mon Nov 04 11:47:43 2013 -0800
++++ b/test/gtest/include/gtest/gtest-typed-test.h Mon Nov 04 11:49:12 2013 -0800
+@@ -31,6 +31,7 @@
+
+ #ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
+ #define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
++#pragma GCC system_header
+
+ // This header implements typed tests and type-parameterized tests.
+
+diff -r a5e72dd0ecf3 test/gtest/src/gtest.cc
+--- a/test/gtest/src/gtest.cc Mon Nov 04 11:47:43 2013 -0800
++++ b/test/gtest/src/gtest.cc Mon Nov 04 11:49:12 2013 -0800
+@@ -33,6 +33,7 @@
+
+ #include "gtest/gtest.h"
+ #include "gtest/gtest-spi.h"
++#pragma GCC system_header
+
+ #include <ctype.h>
+ #include <math.h>
+diff -r c33b44f8a9a1 test/gtest/include/gtest/internal/gtest-port.h
+--- a/test/gtest/include/gtest/internal/gtest-port.h Wed Nov 06 11:23:38 2013 -0800
++++ b/test/gtest/include/gtest/internal/gtest-port.h Wed Nov 06 17:12:57 2013 -0800
+@@ -39,6 +39,7 @@
+
+ #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
+ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
++#pragma GCC system_header
+
+ // The user can define the following macros in the build script to
+ // control Google Test's behavior. If the user doesn't define a macro
+diff -r e980730656c1 test/gtest/include/gtest/gtest-printers.h
+--- a/test/gtest/include/gtest/gtest-printers.h Wed Nov 06 17:13:57 2013 -0800
++++ b/test/gtest/include/gtest/gtest-printers.h Thu Nov 07 09:29:28 2013 -0800
+@@ -94,6 +94,7 @@
+
+ #ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
+ #define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
++#pragma GCC system_header
+
+ #include <ostream> // NOLINT
+ #include <sstream>
diff --git a/test/gtest/include/gtest/gtest-death-test.h b/test/gtest/include/gtest/gtest-death-test.h
new file mode 100644
index 0000000..957a69c
--- /dev/null
+++ b/test/gtest/include/gtest/gtest-death-test.h
@@ -0,0 +1,294 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file defines the public API for death tests. It is
+// #included by gtest.h so a user doesn't need to include this
+// directly.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
+
+#include "gtest/internal/gtest-death-test-internal.h"
+
+namespace testing {
+
+// This flag controls the style of death tests. Valid values are "threadsafe",
+// meaning that the death test child process will re-execute the test binary
+// from the start, running only a single death test, or "fast",
+// meaning that the child process will execute the test logic immediately
+// after forking.
+GTEST_DECLARE_string_(death_test_style);
+
+#if GTEST_HAS_DEATH_TEST
+
+namespace internal {
+
+// Returns a Boolean value indicating whether the caller is currently
+// executing in the context of the death test child process. Tools such as
+// Valgrind heap checkers may need this to modify their behavior in death
+// tests. IMPORTANT: This is an internal utility. Using it may break the
+// implementation of death tests. User code MUST NOT use it.
+GTEST_API_ bool InDeathTestChild();
+
+} // namespace internal
+
+// The following macros are useful for writing death tests.
+
+// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is
+// executed:
+//
+// 1. It generates a warning if there is more than one active
+// thread. This is because it's safe to fork() or clone() only
+// when there is a single thread.
+//
+// 2. The parent process clone()s a sub-process and runs the death
+// test in it; the sub-process exits with code 0 at the end of the
+// death test, if it hasn't exited already.
+//
+// 3. The parent process waits for the sub-process to terminate.
+//
+// 4. The parent process checks the exit code and error message of
+// the sub-process.
+//
+// Examples:
+//
+// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number");
+// for (int i = 0; i < 5; i++) {
+// EXPECT_DEATH(server.ProcessRequest(i),
+// "Invalid request .* in ProcessRequest()")
+// << "Failed to die on request " << i;
+// }
+//
+// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting");
+//
+// bool KilledBySIGHUP(int exit_code) {
+// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP;
+// }
+//
+// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!");
+//
+// On the regular expressions used in death tests:
+//
+// On POSIX-compliant systems (*nix), we use the <regex.h> library,
+// which uses the POSIX extended regex syntax.
+//
+// On other platforms (e.g. Windows), we only support a simple regex
+// syntax implemented as part of Google Test. This limited
+// implementation should be enough most of the time when writing
+// death tests; though it lacks many features you can find in PCRE
+// or POSIX extended regex syntax. For example, we don't support
+// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and
+// repetition count ("x{5,7}"), among others.
+//
+// Below is the syntax that we do support. We chose it to be a
+// subset of both PCRE and POSIX extended regex, so it's easy to
+// learn wherever you come from. In the following: 'A' denotes a
+// literal character, period (.), or a single \\ escape sequence;
+// 'x' and 'y' denote regular expressions; 'm' and 'n' are for
+// natural numbers.
+//
+// c matches any literal character c
+// \\d matches any decimal digit
+// \\D matches any character that's not a decimal digit
+// \\f matches \f
+// \\n matches \n
+// \\r matches \r
+// \\s matches any ASCII whitespace, including \n
+// \\S matches any character that's not a whitespace
+// \\t matches \t
+// \\v matches \v
+// \\w matches any letter, _, or decimal digit
+// \\W matches any character that \\w doesn't match
+// \\c matches any literal character c, which must be a punctuation
+// . matches any single character except \n
+// A? matches 0 or 1 occurrences of A
+// A* matches 0 or many occurrences of A
+// A+ matches 1 or many occurrences of A
+// ^ matches the beginning of a string (not that of each line)
+// $ matches the end of a string (not that of each line)
+// xy matches x followed by y
+//
+// If you accidentally use PCRE or POSIX extended regex features
+// not implemented by us, you will get a run-time failure. In that
+// case, please try to rewrite your regular expression within the
+// above syntax.
+//
+// This implementation is *not* meant to be as highly tuned or robust
+// as a compiled regex library, but should perform well enough for a
+// death test, which already incurs significant overhead by launching
+// a child process.
+//
+// Known caveats:
+//
+// A "threadsafe" style death test obtains the path to the test
+// program from argv[0] and re-executes it in the sub-process. For
+// simplicity, the current implementation doesn't search the PATH
+// when launching the sub-process. This means that the user must
+// invoke the test program via a path that contains at least one
+// path separator (e.g. path/to/foo_test and
+// /absolute/path/to/bar_test are fine, but foo_test is not). This
+// is rarely a problem as people usually don't put the test binary
+// directory in PATH.
+//
+// TODO(wan at google.com): make thread-safe death tests search the PATH.
+
+// Asserts that a given statement causes the program to exit, with an
+// integer exit status that satisfies predicate, and emitting error output
+// that matches regex.
+# define ASSERT_EXIT(statement, predicate, regex) \
+ GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_)
+
+// Like ASSERT_EXIT, but continues on to successive tests in the
+// test case, if any:
+# define EXPECT_EXIT(statement, predicate, regex) \
+ GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_)
+
+// Asserts that a given statement causes the program to exit, either by
+// explicitly exiting with a nonzero exit code or being killed by a
+// signal, and emitting error output that matches regex.
+# define ASSERT_DEATH(statement, regex) \
+ ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex)
+
+// Like ASSERT_DEATH, but continues on to successive tests in the
+// test case, if any:
+# define EXPECT_DEATH(statement, regex) \
+ EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex)
+
+// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*:
+
+// Tests that an exit code describes a normal exit with a given exit code.
+class GTEST_API_ ExitedWithCode {
+ public:
+ explicit ExitedWithCode(int exit_code);
+ bool operator()(int exit_status) const;
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ExitedWithCode& other);
+
+ const int exit_code_;
+};
+
+# if !GTEST_OS_WINDOWS
+// Tests that an exit code describes an exit due to termination by a
+// given signal.
+class GTEST_API_ KilledBySignal {
+ public:
+ explicit KilledBySignal(int signum);
+ bool operator()(int exit_status) const;
+ private:
+ const int signum_;
+};
+# endif // !GTEST_OS_WINDOWS
+
+// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode.
+// The death testing framework causes this to have interesting semantics,
+// since the sideeffects of the call are only visible in opt mode, and not
+// in debug mode.
+//
+// In practice, this can be used to test functions that utilize the
+// LOG(DFATAL) macro using the following style:
+//
+// int DieInDebugOr12(int* sideeffect) {
+// if (sideeffect) {
+// *sideeffect = 12;
+// }
+// LOG(DFATAL) << "death";
+// return 12;
+// }
+//
+// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) {
+// int sideeffect = 0;
+// // Only asserts in dbg.
+// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death");
+//
+// #ifdef NDEBUG
+// // opt-mode has sideeffect visible.
+// EXPECT_EQ(12, sideeffect);
+// #else
+// // dbg-mode no visible sideeffect.
+// EXPECT_EQ(0, sideeffect);
+// #endif
+// }
+//
+// This will assert that DieInDebugReturn12InOpt() crashes in debug
+// mode, usually due to a DCHECK or LOG(DFATAL), but returns the
+// appropriate fallback value (12 in this case) in opt mode. If you
+// need to test that a function has appropriate side-effects in opt
+// mode, include assertions against the side-effects. A general
+// pattern for this is:
+//
+// EXPECT_DEBUG_DEATH({
+// // Side-effects here will have an effect after this statement in
+// // opt mode, but none in debug mode.
+// EXPECT_EQ(12, DieInDebugOr12(&sideeffect));
+// }, "death");
+//
+# ifdef NDEBUG
+
+# define EXPECT_DEBUG_DEATH(statement, regex) \
+ GTEST_EXECUTE_STATEMENT_(statement, regex)
+
+# define ASSERT_DEBUG_DEATH(statement, regex) \
+ GTEST_EXECUTE_STATEMENT_(statement, regex)
+
+# else
+
+# define EXPECT_DEBUG_DEATH(statement, regex) \
+ EXPECT_DEATH(statement, regex)
+
+# define ASSERT_DEBUG_DEATH(statement, regex) \
+ ASSERT_DEATH(statement, regex)
+
+# endif // NDEBUG for EXPECT_DEBUG_DEATH
+#endif // GTEST_HAS_DEATH_TEST
+
+// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and
+// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if
+// death tests are supported; otherwise they just issue a warning. This is
+// useful when you are combining death test assertions with normal test
+// assertions in one test.
+#if GTEST_HAS_DEATH_TEST
+# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
+ EXPECT_DEATH(statement, regex)
+# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
+ ASSERT_DEATH(statement, regex)
+#else
+# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
+ GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, )
+# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
+ GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return)
+#endif
+
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
diff --git a/test/gtest/include/gtest/gtest-message.h b/test/gtest/include/gtest/gtest-message.h
new file mode 100644
index 0000000..fe879bc
--- /dev/null
+++ b/test/gtest/include/gtest/gtest-message.h
@@ -0,0 +1,250 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file defines the Message class.
+//
+// IMPORTANT NOTE: Due to limitation of the C++ language, we have to
+// leave some internal implementation details in this header file.
+// They are clearly marked by comments like this:
+//
+// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+//
+// Such code is NOT meant to be used by a user directly, and is subject
+// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user
+// program!
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
+#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
+
+#include <limits>
+
+#include "gtest/internal/gtest-port.h"
+
+// Ensures that there is at least one operator<< in the global namespace.
+// See Message& operator<<(...) below for why.
+void operator<<(const testing::internal::Secret&, int);
+
+namespace testing {
+
+// The Message class works like an ostream repeater.
+//
+// Typical usage:
+//
+// 1. You stream a bunch of values to a Message object.
+// It will remember the text in a stringstream.
+// 2. Then you stream the Message object to an ostream.
+// This causes the text in the Message to be streamed
+// to the ostream.
+//
+// For example;
+//
+// testing::Message foo;
+// foo << 1 << " != " << 2;
+// std::cout << foo;
+//
+// will print "1 != 2".
+//
+// Message is not intended to be inherited from. In particular, its
+// destructor is not virtual.
+//
+// Note that stringstream behaves differently in gcc and in MSVC. You
+// can stream a NULL char pointer to it in the former, but not in the
+// latter (it causes an access violation if you do). The Message
+// class hides this difference by treating a NULL char pointer as
+// "(null)".
+class GTEST_API_ Message {
+ private:
+ // The type of basic IO manipulators (endl, ends, and flush) for
+ // narrow streams.
+ typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&);
+
+ public:
+ // Constructs an empty Message.
+ Message();
+
+ // Copy constructor.
+ Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT
+ *ss_ << msg.GetString();
+ }
+
+ // Constructs a Message from a C-string.
+ explicit Message(const char* str) : ss_(new ::std::stringstream) {
+ *ss_ << str;
+ }
+
+#if GTEST_OS_SYMBIAN
+ // Streams a value (either a pointer or not) to this object.
+ template <typename T>
+ inline Message& operator <<(const T& value) {
+ StreamHelper(typename internal::is_pointer<T>::type(), value);
+ return *this;
+ }
+#else
+ // Streams a non-pointer value to this object.
+ template <typename T>
+ inline Message& operator <<(const T& val) {
+ // Some libraries overload << for STL containers. These
+ // overloads are defined in the global namespace instead of ::std.
+ //
+ // C++'s symbol lookup rule (i.e. Koenig lookup) says that these
+ // overloads are visible in either the std namespace or the global
+ // namespace, but not other namespaces, including the testing
+ // namespace which Google Test's Message class is in.
+ //
+ // To allow STL containers (and other types that has a << operator
+ // defined in the global namespace) to be used in Google Test
+ // assertions, testing::Message must access the custom << operator
+ // from the global namespace. With this using declaration,
+ // overloads of << defined in the global namespace and those
+ // visible via Koenig lookup are both exposed in this function.
+ using ::operator <<;
+ *ss_ << val;
+ return *this;
+ }
+
+ // Streams a pointer value to this object.
+ //
+ // This function is an overload of the previous one. When you
+ // stream a pointer to a Message, this definition will be used as it
+ // is more specialized. (The C++ Standard, section
+ // [temp.func.order].) If you stream a non-pointer, then the
+ // previous definition will be used.
+ //
+ // The reason for this overload is that streaming a NULL pointer to
+ // ostream is undefined behavior. Depending on the compiler, you
+ // may get "0", "(nil)", "(null)", or an access violation. To
+ // ensure consistent result across compilers, we always treat NULL
+ // as "(null)".
+ template <typename T>
+ inline Message& operator <<(T* const& pointer) { // NOLINT
+ if (pointer == NULL) {
+ *ss_ << "(null)";
+ } else {
+ *ss_ << pointer;
+ }
+ return *this;
+ }
+#endif // GTEST_OS_SYMBIAN
+
+ // Since the basic IO manipulators are overloaded for both narrow
+ // and wide streams, we have to provide this specialized definition
+ // of operator <<, even though its body is the same as the
+ // templatized version above. Without this definition, streaming
+ // endl or other basic IO manipulators to Message will confuse the
+ // compiler.
+ Message& operator <<(BasicNarrowIoManip val) {
+ *ss_ << val;
+ return *this;
+ }
+
+ // Instead of 1/0, we want to see true/false for bool values.
+ Message& operator <<(bool b) {
+ return *this << (b ? "true" : "false");
+ }
+
+ // These two overloads allow streaming a wide C string to a Message
+ // using the UTF-8 encoding.
+ Message& operator <<(const wchar_t* wide_c_str);
+ Message& operator <<(wchar_t* wide_c_str);
+
+#if GTEST_HAS_STD_WSTRING
+ // Converts the given wide string to a narrow string using the UTF-8
+ // encoding, and streams the result to this Message object.
+ Message& operator <<(const ::std::wstring& wstr);
+#endif // GTEST_HAS_STD_WSTRING
+
+#if GTEST_HAS_GLOBAL_WSTRING
+ // Converts the given wide string to a narrow string using the UTF-8
+ // encoding, and streams the result to this Message object.
+ Message& operator <<(const ::wstring& wstr);
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+ // Gets the text streamed to this object so far as an std::string.
+ // Each '\0' character in the buffer is replaced with "\\0".
+ //
+ // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+ std::string GetString() const;
+
+ private:
+
+#if GTEST_OS_SYMBIAN
+ // These are needed as the Nokia Symbian Compiler cannot decide between
+ // const T& and const T* in a function template. The Nokia compiler _can_
+ // decide between class template specializations for T and T*, so a
+ // tr1::type_traits-like is_pointer works, and we can overload on that.
+ template <typename T>
+ inline void StreamHelper(internal::true_type /*is_pointer*/, T* pointer) {
+ if (pointer == NULL) {
+ *ss_ << "(null)";
+ } else {
+ *ss_ << pointer;
+ }
+ }
+ template <typename T>
+ inline void StreamHelper(internal::false_type /*is_pointer*/,
+ const T& value) {
+ // See the comments in Message& operator <<(const T&) above for why
+ // we need this using statement.
+ using ::operator <<;
+ *ss_ << value;
+ }
+#endif // GTEST_OS_SYMBIAN
+
+ // We'll hold the text streamed to this object here.
+ const internal::scoped_ptr< ::std::stringstream> ss_;
+
+ // We declare (but don't implement) this to prevent the compiler
+ // from implementing the assignment operator.
+ void operator=(const Message&);
+};
+
+// Streams a Message to an ostream.
+inline std::ostream& operator <<(std::ostream& os, const Message& sb) {
+ return os << sb.GetString();
+}
+
+namespace internal {
+
+// Converts a streamable value to an std::string. A NULL pointer is
+// converted to "(null)". When the input value is a ::string,
+// ::std::string, ::wstring, or ::std::wstring object, each NUL
+// character in it is replaced with "\\0".
+template <typename T>
+std::string StreamableToString(const T& streamable) {
+ return (Message() << streamable).GetString();
+}
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
diff --git a/test/gtest/include/gtest/gtest-param-test.h b/test/gtest/include/gtest/gtest-param-test.h
new file mode 100644
index 0000000..d6702c8
--- /dev/null
+++ b/test/gtest/include/gtest/gtest-param-test.h
@@ -0,0 +1,1421 @@
+// This file was GENERATED by command:
+// pump.py gtest-param-test.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: vladl at google.com (Vlad Losev)
+//
+// Macros and functions for implementing parameterized tests
+// in Google C++ Testing Framework (Google Test)
+//
+// This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
+//
+#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
+
+
+// Value-parameterized tests allow you to test your code with different
+// parameters without writing multiple copies of the same test.
+//
+// Here is how you use value-parameterized tests:
+
+#if 0
+
+// To write value-parameterized tests, first you should define a fixture
+// class. It is usually derived from testing::TestWithParam<T> (see below for
+// another inheritance scheme that's sometimes useful in more complicated
+// class hierarchies), where the type of your parameter values.
+// TestWithParam<T> is itself derived from testing::Test. T can be any
+// copyable type. If it's a raw pointer, you are responsible for managing the
+// lifespan of the pointed values.
+
+class FooTest : public ::testing::TestWithParam<const char*> {
+ // You can implement all the usual class fixture members here.
+};
+
+// Then, use the TEST_P macro to define as many parameterized tests
+// for this fixture as you want. The _P suffix is for "parameterized"
+// or "pattern", whichever you prefer to think.
+
+TEST_P(FooTest, DoesBlah) {
+ // Inside a test, access the test parameter with the GetParam() method
+ // of the TestWithParam<T> class:
+ EXPECT_TRUE(foo.Blah(GetParam()));
+ ...
+}
+
+TEST_P(FooTest, HasBlahBlah) {
+ ...
+}
+
+// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test
+// case with any set of parameters you want. Google Test defines a number
+// of functions for generating test parameters. They return what we call
+// (surprise!) parameter generators. Here is a summary of them, which
+// are all in the testing namespace:
+//
+//
+// Range(begin, end [, step]) - Yields values {begin, begin+step,
+// begin+step+step, ...}. The values do not
+// include end. step defaults to 1.
+// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}.
+// ValuesIn(container) - Yields values from a C-style array, an STL
+// ValuesIn(begin,end) container, or an iterator range [begin, end).
+// Bool() - Yields sequence {false, true}.
+// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product
+// for the math savvy) of the values generated
+// by the N generators.
+//
+// For more details, see comments at the definitions of these functions below
+// in this file.
+//
+// The following statement will instantiate tests from the FooTest test case
+// each with parameter values "meeny", "miny", and "moe".
+
+INSTANTIATE_TEST_CASE_P(InstantiationName,
+ FooTest,
+ Values("meeny", "miny", "moe"));
+
+// To distinguish different instances of the pattern, (yes, you
+// can instantiate it more then once) the first argument to the
+// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the
+// actual test case name. Remember to pick unique prefixes for different
+// instantiations. The tests from the instantiation above will have
+// these names:
+//
+// * InstantiationName/FooTest.DoesBlah/0 for "meeny"
+// * InstantiationName/FooTest.DoesBlah/1 for "miny"
+// * InstantiationName/FooTest.DoesBlah/2 for "moe"
+// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny"
+// * InstantiationName/FooTest.HasBlahBlah/1 for "miny"
+// * InstantiationName/FooTest.HasBlahBlah/2 for "moe"
+//
+// You can use these names in --gtest_filter.
+//
+// This statement will instantiate all tests from FooTest again, each
+// with parameter values "cat" and "dog":
+
+const char* pets[] = {"cat", "dog"};
+INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets));
+
+// The tests from the instantiation above will have these names:
+//
+// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat"
+// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog"
+// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat"
+// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog"
+//
+// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests
+// in the given test case, whether their definitions come before or
+// AFTER the INSTANTIATE_TEST_CASE_P statement.
+//
+// Please also note that generator expressions (including parameters to the
+// generators) are evaluated in InitGoogleTest(), after main() has started.
+// This allows the user on one hand, to adjust generator parameters in order
+// to dynamically determine a set of tests to run and on the other hand,
+// give the user a chance to inspect the generated tests with Google Test
+// reflection API before RUN_ALL_TESTS() is executed.
+//
+// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc
+// for more examples.
+//
+// In the future, we plan to publish the API for defining new parameter
+// generators. But for now this interface remains part of the internal
+// implementation and is subject to change.
+//
+//
+// A parameterized test fixture must be derived from testing::Test and from
+// testing::WithParamInterface<T>, where T is the type of the parameter
+// values. Inheriting from TestWithParam<T> satisfies that requirement because
+// TestWithParam<T> inherits from both Test and WithParamInterface. In more
+// complicated hierarchies, however, it is occasionally useful to inherit
+// separately from Test and WithParamInterface. For example:
+
+class BaseTest : public ::testing::Test {
+ // You can inherit all the usual members for a non-parameterized test
+ // fixture here.
+};
+
+class DerivedTest : public BaseTest, public ::testing::WithParamInterface<int> {
+ // The usual test fixture members go here too.
+};
+
+TEST_F(BaseTest, HasFoo) {
+ // This is an ordinary non-parameterized test.
+}
+
+TEST_P(DerivedTest, DoesBlah) {
+ // GetParam works just the same here as if you inherit from TestWithParam.
+ EXPECT_TRUE(foo.Blah(GetParam()));
+}
+
+#endif // 0
+
+#include "gtest/internal/gtest-port.h"
+
+#if !GTEST_OS_SYMBIAN
+# include <utility>
+#endif
+
+// scripts/fuse_gtest.py depends on gtest's own header being #included
+// *unconditionally*. Therefore these #includes cannot be moved
+// inside #if GTEST_HAS_PARAM_TEST.
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-param-util.h"
+#include "gtest/internal/gtest-param-util-generated.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace testing {
+
+// Functions producing parameter generators.
+//
+// Google Test uses these generators to produce parameters for value-
+// parameterized tests. When a parameterized test case is instantiated
+// with a particular generator, Google Test creates and runs tests
+// for each element in the sequence produced by the generator.
+//
+// In the following sample, tests from test case FooTest are instantiated
+// each three times with parameter values 3, 5, and 8:
+//
+// class FooTest : public TestWithParam<int> { ... };
+//
+// TEST_P(FooTest, TestThis) {
+// }
+// TEST_P(FooTest, TestThat) {
+// }
+// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8));
+//
+
+// Range() returns generators providing sequences of values in a range.
+//
+// Synopsis:
+// Range(start, end)
+// - returns a generator producing a sequence of values {start, start+1,
+// start+2, ..., }.
+// Range(start, end, step)
+// - returns a generator producing a sequence of values {start, start+step,
+// start+step+step, ..., }.
+// Notes:
+// * The generated sequences never include end. For example, Range(1, 5)
+// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2)
+// returns a generator producing {1, 3, 5, 7}.
+// * start and end must have the same type. That type may be any integral or
+// floating-point type or a user defined type satisfying these conditions:
+// * It must be assignable (have operator=() defined).
+// * It must have operator+() (operator+(int-compatible type) for
+// two-operand version).
+// * It must have operator<() defined.
+// Elements in the resulting sequences will also have that type.
+// * Condition start < end must be satisfied in order for resulting sequences
+// to contain any elements.
+//
+template <typename T, typename IncrementT>
+internal::ParamGenerator<T> Range(T start, T end, IncrementT step) {
+ return internal::ParamGenerator<T>(
+ new internal::RangeGenerator<T, IncrementT>(start, end, step));
+}
+
+template <typename T>
+internal::ParamGenerator<T> Range(T start, T end) {
+ return Range(start, end, 1);
+}
+
+// ValuesIn() function allows generation of tests with parameters coming from
+// a container.
+//
+// Synopsis:
+// ValuesIn(const T (&array)[N])
+// - returns a generator producing sequences with elements from
+// a C-style array.
+// ValuesIn(const Container& container)
+// - returns a generator producing sequences with elements from
+// an STL-style container.
+// ValuesIn(Iterator begin, Iterator end)
+// - returns a generator producing sequences with elements from
+// a range [begin, end) defined by a pair of STL-style iterators. These
+// iterators can also be plain C pointers.
+//
+// Please note that ValuesIn copies the values from the containers
+// passed in and keeps them to generate tests in RUN_ALL_TESTS().
+//
+// Examples:
+//
+// This instantiates tests from test case StringTest
+// each with C-string values of "foo", "bar", and "baz":
+//
+// const char* strings[] = {"foo", "bar", "baz"};
+// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings));
+//
+// This instantiates tests from test case StlStringTest
+// each with STL strings with values "a" and "b":
+//
+// ::std::vector< ::std::string> GetParameterStrings() {
+// ::std::vector< ::std::string> v;
+// v.push_back("a");
+// v.push_back("b");
+// return v;
+// }
+//
+// INSTANTIATE_TEST_CASE_P(CharSequence,
+// StlStringTest,
+// ValuesIn(GetParameterStrings()));
+//
+//
+// This will also instantiate tests from CharTest
+// each with parameter values 'a' and 'b':
+//
+// ::std::list<char> GetParameterChars() {
+// ::std::list<char> list;
+// list.push_back('a');
+// list.push_back('b');
+// return list;
+// }
+// ::std::list<char> l = GetParameterChars();
+// INSTANTIATE_TEST_CASE_P(CharSequence2,
+// CharTest,
+// ValuesIn(l.begin(), l.end()));
+//
+template <typename ForwardIterator>
+internal::ParamGenerator<
+ typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type>
+ValuesIn(ForwardIterator begin, ForwardIterator end) {
+ typedef typename ::testing::internal::IteratorTraits<ForwardIterator>
+ ::value_type ParamType;
+ return internal::ParamGenerator<ParamType>(
+ new internal::ValuesInIteratorRangeGenerator<ParamType>(begin, end));
+}
+
+template <typename T, size_t N>
+internal::ParamGenerator<T> ValuesIn(const T (&array)[N]) {
+ return ValuesIn(array, array + N);
+}
+
+template <class Container>
+internal::ParamGenerator<typename Container::value_type> ValuesIn(
+ const Container& container) {
+ return ValuesIn(container.begin(), container.end());
+}
+
+// Values() allows generating tests from explicitly specified list of
+// parameters.
+//
+// Synopsis:
+// Values(T v1, T v2, ..., T vN)
+// - returns a generator producing sequences with elements v1, v2, ..., vN.
+//
+// For example, this instantiates tests from test case BarTest each
+// with values "one", "two", and "three":
+//
+// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three"));
+//
+// This instantiates tests from test case BazTest each with values 1, 2, 3.5.
+// The exact type of values will depend on the type of parameter in BazTest.
+//
+// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5));
+//
+// Currently, Values() supports from 1 to 50 parameters.
+//
+template <typename T1>
+internal::ValueArray1<T1> Values(T1 v1) {
+ return internal::ValueArray1<T1>(v1);
+}
+
+template <typename T1, typename T2>
+internal::ValueArray2<T1, T2> Values(T1 v1, T2 v2) {
+ return internal::ValueArray2<T1, T2>(v1, v2);
+}
+
+template <typename T1, typename T2, typename T3>
+internal::ValueArray3<T1, T2, T3> Values(T1 v1, T2 v2, T3 v3) {
+ return internal::ValueArray3<T1, T2, T3>(v1, v2, v3);
+}
+
+template <typename T1, typename T2, typename T3, typename T4>
+internal::ValueArray4<T1, T2, T3, T4> Values(T1 v1, T2 v2, T3 v3, T4 v4) {
+ return internal::ValueArray4<T1, T2, T3, T4>(v1, v2, v3, v4);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+internal::ValueArray5<T1, T2, T3, T4, T5> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+ T5 v5) {
+ return internal::ValueArray5<T1, T2, T3, T4, T5>(v1, v2, v3, v4, v5);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+internal::ValueArray6<T1, T2, T3, T4, T5, T6> Values(T1 v1, T2 v2, T3 v3,
+ T4 v4, T5 v5, T6 v6) {
+ return internal::ValueArray6<T1, T2, T3, T4, T5, T6>(v1, v2, v3, v4, v5, v6);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+internal::ValueArray7<T1, T2, T3, T4, T5, T6, T7> Values(T1 v1, T2 v2, T3 v3,
+ T4 v4, T5 v5, T6 v6, T7 v7) {
+ return internal::ValueArray7<T1, T2, T3, T4, T5, T6, T7>(v1, v2, v3, v4, v5,
+ v6, v7);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+internal::ValueArray8<T1, T2, T3, T4, T5, T6, T7, T8> Values(T1 v1, T2 v2,
+ T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8) {
+ return internal::ValueArray8<T1, T2, T3, T4, T5, T6, T7, T8>(v1, v2, v3, v4,
+ v5, v6, v7, v8);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+internal::ValueArray9<T1, T2, T3, T4, T5, T6, T7, T8, T9> Values(T1 v1, T2 v2,
+ T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9) {
+ return internal::ValueArray9<T1, T2, T3, T4, T5, T6, T7, T8, T9>(v1, v2, v3,
+ v4, v5, v6, v7, v8, v9);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+internal::ValueArray10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Values(T1 v1,
+ T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10) {
+ return internal::ValueArray10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(v1,
+ v2, v3, v4, v5, v6, v7, v8, v9, v10);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11>
+internal::ValueArray11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,
+ T11> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11) {
+ return internal::ValueArray11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,
+ T11>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12>
+internal::ValueArray12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12) {
+ return internal::ValueArray12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13>
+internal::ValueArray13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13) {
+ return internal::ValueArray13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14>
+internal::ValueArray14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) {
+ return internal::ValueArray14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
+ v14);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15>
+internal::ValueArray15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
+ T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) {
+ return internal::ValueArray15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
+ v13, v14, v15);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16>
+internal::ValueArray16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+ T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16) {
+ return internal::ValueArray16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
+ v12, v13, v14, v15, v16);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17>
+internal::ValueArray17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+ T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17) {
+ return internal::ValueArray17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,
+ v11, v12, v13, v14, v15, v16, v17);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18>
+internal::ValueArray18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6,
+ T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17, T18 v18) {
+ return internal::ValueArray18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18>(v1, v2, v3, v4, v5, v6, v7, v8, v9,
+ v10, v11, v12, v13, v14, v15, v16, v17, v18);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19>
+internal::ValueArray19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5,
+ T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14,
+ T15 v15, T16 v16, T17 v17, T18 v18, T19 v19) {
+ return internal::ValueArray19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19>(v1, v2, v3, v4, v5, v6, v7, v8,
+ v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20>
+internal::ValueArray20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+ T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+ T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20) {
+ return internal::ValueArray20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20>(v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21>
+internal::ValueArray21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+ T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+ T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21) {
+ return internal::ValueArray21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21>(v1, v2, v3, v4, v5, v6,
+ v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22>
+internal::ValueArray22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22> Values(T1 v1, T2 v2, T3 v3,
+ T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+ T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+ T21 v21, T22 v22) {
+ return internal::ValueArray22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22>(v1, v2, v3, v4,
+ v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+ v20, v21, v22);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23>
+internal::ValueArray23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> Values(T1 v1, T2 v2,
+ T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+ T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+ T21 v21, T22 v22, T23 v23) {
+ return internal::ValueArray23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23>(v1, v2, v3,
+ v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+ v20, v21, v22, v23);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24>
+internal::ValueArray24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> Values(T1 v1, T2 v2,
+ T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+ T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+ T21 v21, T22 v22, T23 v23, T24 v24) {
+ return internal::ValueArray24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24>(v1, v2,
+ v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18,
+ v19, v20, v21, v22, v23, v24);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25>
+internal::ValueArray25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Values(T1 v1,
+ T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11,
+ T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19,
+ T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25) {
+ return internal::ValueArray25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25>(v1,
+ v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
+ v18, v19, v20, v21, v22, v23, v24, v25);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26>
+internal::ValueArray26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26) {
+ return internal::ValueArray26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15,
+ v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27>
+internal::ValueArray27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27) {
+ return internal::ValueArray27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14,
+ v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28>
+internal::ValueArray28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28) {
+ return internal::ValueArray28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
+ v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27,
+ v28);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29>
+internal::ValueArray29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29) {
+ return internal::ValueArray29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
+ v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26,
+ v27, v28, v29);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30>
+internal::ValueArray30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
+ T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16,
+ T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24,
+ T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) {
+ return internal::ValueArray30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
+ v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25,
+ v26, v27, v28, v29, v30);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31>
+internal::ValueArray31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+ T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+ T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) {
+ return internal::ValueArray31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,
+ v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24,
+ v25, v26, v27, v28, v29, v30, v31);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32>
+internal::ValueArray32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+ T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+ T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+ T32 v32) {
+ return internal::ValueArray32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32>(v1, v2, v3, v4, v5, v6, v7, v8, v9,
+ v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
+ v24, v25, v26, v27, v28, v29, v30, v31, v32);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33>
+internal::ValueArray33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6,
+ T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+ T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+ T32 v32, T33 v33) {
+ return internal::ValueArray33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33>(v1, v2, v3, v4, v5, v6, v7, v8,
+ v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
+ v24, v25, v26, v27, v28, v29, v30, v31, v32, v33);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34>
+internal::ValueArray34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5,
+ T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14,
+ T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22,
+ T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30,
+ T31 v31, T32 v32, T33 v33, T34 v34) {
+ return internal::ValueArray34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34>(v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
+ v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35>
+internal::ValueArray35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+ T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+ T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21,
+ T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29,
+ T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35) {
+ return internal::ValueArray35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35>(v1, v2, v3, v4, v5, v6,
+ v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21,
+ v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36>
+internal::ValueArray36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+ T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+ T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21,
+ T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29,
+ T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36) {
+ return internal::ValueArray36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36>(v1, v2, v3, v4,
+ v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+ v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33,
+ v34, v35, v36);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37>
+internal::ValueArray37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37> Values(T1 v1, T2 v2, T3 v3,
+ T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+ T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+ T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28,
+ T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36,
+ T37 v37) {
+ return internal::ValueArray37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37>(v1, v2, v3,
+ v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+ v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33,
+ v34, v35, v36, v37);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38>
+internal::ValueArray38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> Values(T1 v1, T2 v2,
+ T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+ T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+ T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28,
+ T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36,
+ T37 v37, T38 v38) {
+ return internal::ValueArray38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38>(v1, v2,
+ v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18,
+ v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32,
+ v33, v34, v35, v36, v37, v38);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39>
+internal::ValueArray39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Values(T1 v1, T2 v2,
+ T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+ T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+ T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28,
+ T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36,
+ T37 v37, T38 v38, T39 v39) {
+ return internal::ValueArray39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39>(v1,
+ v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
+ v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31,
+ v32, v33, v34, v35, v36, v37, v38, v39);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40>
+internal::ValueArray40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Values(T1 v1,
+ T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11,
+ T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19,
+ T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27,
+ T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35,
+ T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) {
+ return internal::ValueArray40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15,
+ v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29,
+ v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41>
+internal::ValueArray41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41) {
+ return internal::ValueArray41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14,
+ v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28,
+ v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42>
+internal::ValueArray42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42) {
+ return internal::ValueArray42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
+ v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27,
+ v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41,
+ v42);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43>
+internal::ValueArray43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43) {
+ return internal::ValueArray43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
+ v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26,
+ v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40,
+ v41, v42, v43);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44>
+internal::ValueArray44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44) {
+ return internal::ValueArray44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43, T44>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
+ v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25,
+ v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39,
+ v40, v41, v42, v43, v44);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45>
+internal::ValueArray45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
+ T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16,
+ T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24,
+ T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32,
+ T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40,
+ T41 v41, T42 v42, T43 v43, T44 v44, T45 v45) {
+ return internal::ValueArray45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43, T44, T45>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,
+ v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24,
+ v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38,
+ v39, v40, v41, v42, v43, v44, v45);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46>
+internal::ValueArray46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+ T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+ T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+ T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39,
+ T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) {
+ return internal::ValueArray46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43, T44, T45, T46>(v1, v2, v3, v4, v5, v6, v7, v8, v9,
+ v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
+ v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37,
+ v38, v39, v40, v41, v42, v43, v44, v45, v46);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47>
+internal::ValueArray47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+ T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+ T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+ T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39,
+ T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) {
+ return internal::ValueArray47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43, T44, T45, T46, T47>(v1, v2, v3, v4, v5, v6, v7, v8,
+ v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
+ v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37,
+ v38, v39, v40, v41, v42, v43, v44, v45, v46, v47);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48>
+internal::ValueArray48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47, T48> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6,
+ T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+ T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+ T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39,
+ T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47,
+ T48 v48) {
+ return internal::ValueArray48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43, T44, T45, T46, T47, T48>(v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
+ v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36,
+ v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49>
+internal::ValueArray49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47, T48, T49> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5,
+ T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14,
+ T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22,
+ T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30,
+ T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38,
+ T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46,
+ T47 v47, T48 v48, T49 v49) {
+ return internal::ValueArray49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43, T44, T45, T46, T47, T48, T49>(v1, v2, v3, v4, v5, v6,
+ v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21,
+ v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35,
+ v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49, typename T50>
+internal::ValueArray50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47, T48, T49, T50> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+ T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+ T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21,
+ T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29,
+ T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37,
+ T38 v38, T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45,
+ T46 v46, T47 v47, T48 v48, T49 v49, T50 v50) {
+ return internal::ValueArray50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43, T44, T45, T46, T47, T48, T49, T50>(v1, v2, v3, v4,
+ v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+ v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33,
+ v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47,
+ v48, v49, v50);
+}
+
+// Bool() allows generating tests with parameters in a set of (false, true).
+//
+// Synopsis:
+// Bool()
+// - returns a generator producing sequences with elements {false, true}.
+//
+// It is useful when testing code that depends on Boolean flags. Combinations
+// of multiple flags can be tested when several Bool()'s are combined using
+// Combine() function.
+//
+// In the following example all tests in the test case FlagDependentTest
+// will be instantiated twice with parameters false and true.
+//
+// class FlagDependentTest : public testing::TestWithParam<bool> {
+// virtual void SetUp() {
+// external_flag = GetParam();
+// }
+// }
+// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool());
+//
+inline internal::ParamGenerator<bool> Bool() {
+ return Values(false, true);
+}
+
+# if GTEST_HAS_COMBINE
+// Combine() allows the user to combine two or more sequences to produce
+// values of a Cartesian product of those sequences' elements.
+//
+// Synopsis:
+// Combine(gen1, gen2, ..., genN)
+// - returns a generator producing sequences with elements coming from
+// the Cartesian product of elements from the sequences generated by
+// gen1, gen2, ..., genN. The sequence elements will have a type of
+// tuple<T1, T2, ..., TN> where T1, T2, ..., TN are the types
+// of elements from sequences produces by gen1, gen2, ..., genN.
+//
+// Combine can have up to 10 arguments. This number is currently limited
+// by the maximum number of elements in the tuple implementation used by Google
+// Test.
+//
+// Example:
+//
+// This will instantiate tests in test case AnimalTest each one with
+// the parameter values tuple("cat", BLACK), tuple("cat", WHITE),
+// tuple("dog", BLACK), and tuple("dog", WHITE):
+//
+// enum Color { BLACK, GRAY, WHITE };
+// class AnimalTest
+// : public testing::TestWithParam<tuple<const char*, Color> > {...};
+//
+// TEST_P(AnimalTest, AnimalLooksNice) {...}
+//
+// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest,
+// Combine(Values("cat", "dog"),
+// Values(BLACK, WHITE)));
+//
+// This will instantiate tests in FlagDependentTest with all variations of two
+// Boolean flags:
+//
+// class FlagDependentTest
+// : public testing::TestWithParam<tuple<bool, bool> > {
+// virtual void SetUp() {
+// // Assigns external_flag_1 and external_flag_2 values from the tuple.
+// tie(external_flag_1, external_flag_2) = GetParam();
+// }
+// };
+//
+// TEST_P(FlagDependentTest, TestFeature1) {
+// // Test your code using external_flag_1 and external_flag_2 here.
+// }
+// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest,
+// Combine(Bool(), Bool()));
+//
+template <typename Generator1, typename Generator2>
+internal::CartesianProductHolder2<Generator1, Generator2> Combine(
+ const Generator1& g1, const Generator2& g2) {
+ return internal::CartesianProductHolder2<Generator1, Generator2>(
+ g1, g2);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3>
+internal::CartesianProductHolder3<Generator1, Generator2, Generator3> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3) {
+ return internal::CartesianProductHolder3<Generator1, Generator2, Generator3>(
+ g1, g2, g3);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+ typename Generator4>
+internal::CartesianProductHolder4<Generator1, Generator2, Generator3,
+ Generator4> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3,
+ const Generator4& g4) {
+ return internal::CartesianProductHolder4<Generator1, Generator2, Generator3,
+ Generator4>(
+ g1, g2, g3, g4);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+ typename Generator4, typename Generator5>
+internal::CartesianProductHolder5<Generator1, Generator2, Generator3,
+ Generator4, Generator5> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3,
+ const Generator4& g4, const Generator5& g5) {
+ return internal::CartesianProductHolder5<Generator1, Generator2, Generator3,
+ Generator4, Generator5>(
+ g1, g2, g3, g4, g5);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+ typename Generator4, typename Generator5, typename Generator6>
+internal::CartesianProductHolder6<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3,
+ const Generator4& g4, const Generator5& g5, const Generator6& g6) {
+ return internal::CartesianProductHolder6<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6>(
+ g1, g2, g3, g4, g5, g6);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+ typename Generator4, typename Generator5, typename Generator6,
+ typename Generator7>
+internal::CartesianProductHolder7<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3,
+ const Generator4& g4, const Generator5& g5, const Generator6& g6,
+ const Generator7& g7) {
+ return internal::CartesianProductHolder7<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7>(
+ g1, g2, g3, g4, g5, g6, g7);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+ typename Generator4, typename Generator5, typename Generator6,
+ typename Generator7, typename Generator8>
+internal::CartesianProductHolder8<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7, Generator8> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3,
+ const Generator4& g4, const Generator5& g5, const Generator6& g6,
+ const Generator7& g7, const Generator8& g8) {
+ return internal::CartesianProductHolder8<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7, Generator8>(
+ g1, g2, g3, g4, g5, g6, g7, g8);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+ typename Generator4, typename Generator5, typename Generator6,
+ typename Generator7, typename Generator8, typename Generator9>
+internal::CartesianProductHolder9<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7, Generator8,
+ Generator9> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3,
+ const Generator4& g4, const Generator5& g5, const Generator6& g6,
+ const Generator7& g7, const Generator8& g8, const Generator9& g9) {
+ return internal::CartesianProductHolder9<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7, Generator8, Generator9>(
+ g1, g2, g3, g4, g5, g6, g7, g8, g9);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+ typename Generator4, typename Generator5, typename Generator6,
+ typename Generator7, typename Generator8, typename Generator9,
+ typename Generator10>
+internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7, Generator8, Generator9,
+ Generator10> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3,
+ const Generator4& g4, const Generator5& g5, const Generator6& g6,
+ const Generator7& g7, const Generator8& g8, const Generator9& g9,
+ const Generator10& g10) {
+ return internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7, Generator8, Generator9,
+ Generator10>(
+ g1, g2, g3, g4, g5, g6, g7, g8, g9, g10);
+}
+# endif // GTEST_HAS_COMBINE
+
+
+
+# define TEST_P(test_case_name, test_name) \
+ class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
+ : public test_case_name { \
+ public: \
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \
+ virtual void TestBody(); \
+ private: \
+ static int AddToRegistry() { \
+ ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
+ GetTestCasePatternHolder<test_case_name>(\
+ #test_case_name, __FILE__, __LINE__)->AddTestPattern(\
+ #test_case_name, \
+ #test_name, \
+ new ::testing::internal::TestMetaFactory< \
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \
+ return 0; \
+ } \
+ static int gtest_registering_dummy_; \
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(\
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \
+ }; \
+ int GTEST_TEST_CLASS_NAME_(test_case_name, \
+ test_name)::gtest_registering_dummy_ = \
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \
+ void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
+
+# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \
+ ::testing::internal::ParamGenerator<test_case_name::ParamType> \
+ gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \
+ int gtest_##prefix##test_case_name##_dummy_ = \
+ ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
+ GetTestCasePatternHolder<test_case_name>(\
+ #test_case_name, __FILE__, __LINE__)->AddTestCaseInstantiation(\
+ #prefix, \
+ >est_##prefix##test_case_name##_EvalGenerator_, \
+ __FILE__, __LINE__)
+
+} // namespace testing
+
+#endif // GTEST_HAS_PARAM_TEST
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
diff --git a/test/gtest/include/gtest/gtest-param-test.h.pump b/test/gtest/include/gtest/gtest-param-test.h.pump
new file mode 100644
index 0000000..2dc9303
--- /dev/null
+++ b/test/gtest/include/gtest/gtest-param-test.h.pump
@@ -0,0 +1,487 @@
+$$ -*- mode: c++; -*-
+$var n = 50 $$ Maximum length of Values arguments we want to support.
+$var maxtuple = 10 $$ Maximum number of Combine arguments we want to support.
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: vladl at google.com (Vlad Losev)
+//
+// Macros and functions for implementing parameterized tests
+// in Google C++ Testing Framework (Google Test)
+//
+// This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
+//
+#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
+
+
+// Value-parameterized tests allow you to test your code with different
+// parameters without writing multiple copies of the same test.
+//
+// Here is how you use value-parameterized tests:
+
+#if 0
+
+// To write value-parameterized tests, first you should define a fixture
+// class. It is usually derived from testing::TestWithParam<T> (see below for
+// another inheritance scheme that's sometimes useful in more complicated
+// class hierarchies), where the type of your parameter values.
+// TestWithParam<T> is itself derived from testing::Test. T can be any
+// copyable type. If it's a raw pointer, you are responsible for managing the
+// lifespan of the pointed values.
+
+class FooTest : public ::testing::TestWithParam<const char*> {
+ // You can implement all the usual class fixture members here.
+};
+
+// Then, use the TEST_P macro to define as many parameterized tests
+// for this fixture as you want. The _P suffix is for "parameterized"
+// or "pattern", whichever you prefer to think.
+
+TEST_P(FooTest, DoesBlah) {
+ // Inside a test, access the test parameter with the GetParam() method
+ // of the TestWithParam<T> class:
+ EXPECT_TRUE(foo.Blah(GetParam()));
+ ...
+}
+
+TEST_P(FooTest, HasBlahBlah) {
+ ...
+}
+
+// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test
+// case with any set of parameters you want. Google Test defines a number
+// of functions for generating test parameters. They return what we call
+// (surprise!) parameter generators. Here is a summary of them, which
+// are all in the testing namespace:
+//
+//
+// Range(begin, end [, step]) - Yields values {begin, begin+step,
+// begin+step+step, ...}. The values do not
+// include end. step defaults to 1.
+// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}.
+// ValuesIn(container) - Yields values from a C-style array, an STL
+// ValuesIn(begin,end) container, or an iterator range [begin, end).
+// Bool() - Yields sequence {false, true}.
+// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product
+// for the math savvy) of the values generated
+// by the N generators.
+//
+// For more details, see comments at the definitions of these functions below
+// in this file.
+//
+// The following statement will instantiate tests from the FooTest test case
+// each with parameter values "meeny", "miny", and "moe".
+
+INSTANTIATE_TEST_CASE_P(InstantiationName,
+ FooTest,
+ Values("meeny", "miny", "moe"));
+
+// To distinguish different instances of the pattern, (yes, you
+// can instantiate it more then once) the first argument to the
+// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the
+// actual test case name. Remember to pick unique prefixes for different
+// instantiations. The tests from the instantiation above will have
+// these names:
+//
+// * InstantiationName/FooTest.DoesBlah/0 for "meeny"
+// * InstantiationName/FooTest.DoesBlah/1 for "miny"
+// * InstantiationName/FooTest.DoesBlah/2 for "moe"
+// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny"
+// * InstantiationName/FooTest.HasBlahBlah/1 for "miny"
+// * InstantiationName/FooTest.HasBlahBlah/2 for "moe"
+//
+// You can use these names in --gtest_filter.
+//
+// This statement will instantiate all tests from FooTest again, each
+// with parameter values "cat" and "dog":
+
+const char* pets[] = {"cat", "dog"};
+INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets));
+
+// The tests from the instantiation above will have these names:
+//
+// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat"
+// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog"
+// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat"
+// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog"
+//
+// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests
+// in the given test case, whether their definitions come before or
+// AFTER the INSTANTIATE_TEST_CASE_P statement.
+//
+// Please also note that generator expressions (including parameters to the
+// generators) are evaluated in InitGoogleTest(), after main() has started.
+// This allows the user on one hand, to adjust generator parameters in order
+// to dynamically determine a set of tests to run and on the other hand,
+// give the user a chance to inspect the generated tests with Google Test
+// reflection API before RUN_ALL_TESTS() is executed.
+//
+// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc
+// for more examples.
+//
+// In the future, we plan to publish the API for defining new parameter
+// generators. But for now this interface remains part of the internal
+// implementation and is subject to change.
+//
+//
+// A parameterized test fixture must be derived from testing::Test and from
+// testing::WithParamInterface<T>, where T is the type of the parameter
+// values. Inheriting from TestWithParam<T> satisfies that requirement because
+// TestWithParam<T> inherits from both Test and WithParamInterface. In more
+// complicated hierarchies, however, it is occasionally useful to inherit
+// separately from Test and WithParamInterface. For example:
+
+class BaseTest : public ::testing::Test {
+ // You can inherit all the usual members for a non-parameterized test
+ // fixture here.
+};
+
+class DerivedTest : public BaseTest, public ::testing::WithParamInterface<int> {
+ // The usual test fixture members go here too.
+};
+
+TEST_F(BaseTest, HasFoo) {
+ // This is an ordinary non-parameterized test.
+}
+
+TEST_P(DerivedTest, DoesBlah) {
+ // GetParam works just the same here as if you inherit from TestWithParam.
+ EXPECT_TRUE(foo.Blah(GetParam()));
+}
+
+#endif // 0
+
+#include "gtest/internal/gtest-port.h"
+
+#if !GTEST_OS_SYMBIAN
+# include <utility>
+#endif
+
+// scripts/fuse_gtest.py depends on gtest's own header being #included
+// *unconditionally*. Therefore these #includes cannot be moved
+// inside #if GTEST_HAS_PARAM_TEST.
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-param-util.h"
+#include "gtest/internal/gtest-param-util-generated.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace testing {
+
+// Functions producing parameter generators.
+//
+// Google Test uses these generators to produce parameters for value-
+// parameterized tests. When a parameterized test case is instantiated
+// with a particular generator, Google Test creates and runs tests
+// for each element in the sequence produced by the generator.
+//
+// In the following sample, tests from test case FooTest are instantiated
+// each three times with parameter values 3, 5, and 8:
+//
+// class FooTest : public TestWithParam<int> { ... };
+//
+// TEST_P(FooTest, TestThis) {
+// }
+// TEST_P(FooTest, TestThat) {
+// }
+// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8));
+//
+
+// Range() returns generators providing sequences of values in a range.
+//
+// Synopsis:
+// Range(start, end)
+// - returns a generator producing a sequence of values {start, start+1,
+// start+2, ..., }.
+// Range(start, end, step)
+// - returns a generator producing a sequence of values {start, start+step,
+// start+step+step, ..., }.
+// Notes:
+// * The generated sequences never include end. For example, Range(1, 5)
+// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2)
+// returns a generator producing {1, 3, 5, 7}.
+// * start and end must have the same type. That type may be any integral or
+// floating-point type or a user defined type satisfying these conditions:
+// * It must be assignable (have operator=() defined).
+// * It must have operator+() (operator+(int-compatible type) for
+// two-operand version).
+// * It must have operator<() defined.
+// Elements in the resulting sequences will also have that type.
+// * Condition start < end must be satisfied in order for resulting sequences
+// to contain any elements.
+//
+template <typename T, typename IncrementT>
+internal::ParamGenerator<T> Range(T start, T end, IncrementT step) {
+ return internal::ParamGenerator<T>(
+ new internal::RangeGenerator<T, IncrementT>(start, end, step));
+}
+
+template <typename T>
+internal::ParamGenerator<T> Range(T start, T end) {
+ return Range(start, end, 1);
+}
+
+// ValuesIn() function allows generation of tests with parameters coming from
+// a container.
+//
+// Synopsis:
+// ValuesIn(const T (&array)[N])
+// - returns a generator producing sequences with elements from
+// a C-style array.
+// ValuesIn(const Container& container)
+// - returns a generator producing sequences with elements from
+// an STL-style container.
+// ValuesIn(Iterator begin, Iterator end)
+// - returns a generator producing sequences with elements from
+// a range [begin, end) defined by a pair of STL-style iterators. These
+// iterators can also be plain C pointers.
+//
+// Please note that ValuesIn copies the values from the containers
+// passed in and keeps them to generate tests in RUN_ALL_TESTS().
+//
+// Examples:
+//
+// This instantiates tests from test case StringTest
+// each with C-string values of "foo", "bar", and "baz":
+//
+// const char* strings[] = {"foo", "bar", "baz"};
+// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings));
+//
+// This instantiates tests from test case StlStringTest
+// each with STL strings with values "a" and "b":
+//
+// ::std::vector< ::std::string> GetParameterStrings() {
+// ::std::vector< ::std::string> v;
+// v.push_back("a");
+// v.push_back("b");
+// return v;
+// }
+//
+// INSTANTIATE_TEST_CASE_P(CharSequence,
+// StlStringTest,
+// ValuesIn(GetParameterStrings()));
+//
+//
+// This will also instantiate tests from CharTest
+// each with parameter values 'a' and 'b':
+//
+// ::std::list<char> GetParameterChars() {
+// ::std::list<char> list;
+// list.push_back('a');
+// list.push_back('b');
+// return list;
+// }
+// ::std::list<char> l = GetParameterChars();
+// INSTANTIATE_TEST_CASE_P(CharSequence2,
+// CharTest,
+// ValuesIn(l.begin(), l.end()));
+//
+template <typename ForwardIterator>
+internal::ParamGenerator<
+ typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type>
+ValuesIn(ForwardIterator begin, ForwardIterator end) {
+ typedef typename ::testing::internal::IteratorTraits<ForwardIterator>
+ ::value_type ParamType;
+ return internal::ParamGenerator<ParamType>(
+ new internal::ValuesInIteratorRangeGenerator<ParamType>(begin, end));
+}
+
+template <typename T, size_t N>
+internal::ParamGenerator<T> ValuesIn(const T (&array)[N]) {
+ return ValuesIn(array, array + N);
+}
+
+template <class Container>
+internal::ParamGenerator<typename Container::value_type> ValuesIn(
+ const Container& container) {
+ return ValuesIn(container.begin(), container.end());
+}
+
+// Values() allows generating tests from explicitly specified list of
+// parameters.
+//
+// Synopsis:
+// Values(T v1, T v2, ..., T vN)
+// - returns a generator producing sequences with elements v1, v2, ..., vN.
+//
+// For example, this instantiates tests from test case BarTest each
+// with values "one", "two", and "three":
+//
+// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three"));
+//
+// This instantiates tests from test case BazTest each with values 1, 2, 3.5.
+// The exact type of values will depend on the type of parameter in BazTest.
+//
+// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5));
+//
+// Currently, Values() supports from 1 to $n parameters.
+//
+$range i 1..n
+$for i [[
+$range j 1..i
+
+template <$for j, [[typename T$j]]>
+internal::ValueArray$i<$for j, [[T$j]]> Values($for j, [[T$j v$j]]) {
+ return internal::ValueArray$i<$for j, [[T$j]]>($for j, [[v$j]]);
+}
+
+]]
+
+// Bool() allows generating tests with parameters in a set of (false, true).
+//
+// Synopsis:
+// Bool()
+// - returns a generator producing sequences with elements {false, true}.
+//
+// It is useful when testing code that depends on Boolean flags. Combinations
+// of multiple flags can be tested when several Bool()'s are combined using
+// Combine() function.
+//
+// In the following example all tests in the test case FlagDependentTest
+// will be instantiated twice with parameters false and true.
+//
+// class FlagDependentTest : public testing::TestWithParam<bool> {
+// virtual void SetUp() {
+// external_flag = GetParam();
+// }
+// }
+// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool());
+//
+inline internal::ParamGenerator<bool> Bool() {
+ return Values(false, true);
+}
+
+# if GTEST_HAS_COMBINE
+// Combine() allows the user to combine two or more sequences to produce
+// values of a Cartesian product of those sequences' elements.
+//
+// Synopsis:
+// Combine(gen1, gen2, ..., genN)
+// - returns a generator producing sequences with elements coming from
+// the Cartesian product of elements from the sequences generated by
+// gen1, gen2, ..., genN. The sequence elements will have a type of
+// tuple<T1, T2, ..., TN> where T1, T2, ..., TN are the types
+// of elements from sequences produces by gen1, gen2, ..., genN.
+//
+// Combine can have up to $maxtuple arguments. This number is currently limited
+// by the maximum number of elements in the tuple implementation used by Google
+// Test.
+//
+// Example:
+//
+// This will instantiate tests in test case AnimalTest each one with
+// the parameter values tuple("cat", BLACK), tuple("cat", WHITE),
+// tuple("dog", BLACK), and tuple("dog", WHITE):
+//
+// enum Color { BLACK, GRAY, WHITE };
+// class AnimalTest
+// : public testing::TestWithParam<tuple<const char*, Color> > {...};
+//
+// TEST_P(AnimalTest, AnimalLooksNice) {...}
+//
+// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest,
+// Combine(Values("cat", "dog"),
+// Values(BLACK, WHITE)));
+//
+// This will instantiate tests in FlagDependentTest with all variations of two
+// Boolean flags:
+//
+// class FlagDependentTest
+// : public testing::TestWithParam<tuple<bool, bool> > {
+// virtual void SetUp() {
+// // Assigns external_flag_1 and external_flag_2 values from the tuple.
+// tie(external_flag_1, external_flag_2) = GetParam();
+// }
+// };
+//
+// TEST_P(FlagDependentTest, TestFeature1) {
+// // Test your code using external_flag_1 and external_flag_2 here.
+// }
+// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest,
+// Combine(Bool(), Bool()));
+//
+$range i 2..maxtuple
+$for i [[
+$range j 1..i
+
+template <$for j, [[typename Generator$j]]>
+internal::CartesianProductHolder$i<$for j, [[Generator$j]]> Combine(
+ $for j, [[const Generator$j& g$j]]) {
+ return internal::CartesianProductHolder$i<$for j, [[Generator$j]]>(
+ $for j, [[g$j]]);
+}
+
+]]
+# endif // GTEST_HAS_COMBINE
+
+
+
+# define TEST_P(test_case_name, test_name) \
+ class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
+ : public test_case_name { \
+ public: \
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \
+ virtual void TestBody(); \
+ private: \
+ static int AddToRegistry() { \
+ ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
+ GetTestCasePatternHolder<test_case_name>(\
+ #test_case_name, __FILE__, __LINE__)->AddTestPattern(\
+ #test_case_name, \
+ #test_name, \
+ new ::testing::internal::TestMetaFactory< \
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \
+ return 0; \
+ } \
+ static int gtest_registering_dummy_; \
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(\
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \
+ }; \
+ int GTEST_TEST_CLASS_NAME_(test_case_name, \
+ test_name)::gtest_registering_dummy_ = \
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \
+ void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
+
+# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \
+ ::testing::internal::ParamGenerator<test_case_name::ParamType> \
+ gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \
+ int gtest_##prefix##test_case_name##_dummy_ = \
+ ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
+ GetTestCasePatternHolder<test_case_name>(\
+ #test_case_name, __FILE__, __LINE__)->AddTestCaseInstantiation(\
+ #prefix, \
+ >est_##prefix##test_case_name##_EvalGenerator_, \
+ __FILE__, __LINE__)
+
+} // namespace testing
+
+#endif // GTEST_HAS_PARAM_TEST
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
diff --git a/test/gtest/include/gtest/gtest-printers.h b/test/gtest/include/gtest/gtest-printers.h
new file mode 100644
index 0000000..bc647b1
--- /dev/null
+++ b/test/gtest/include/gtest/gtest-printers.h
@@ -0,0 +1,858 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+// Google Test - The Google C++ Testing Framework
+//
+// This file implements a universal value printer that can print a
+// value of any type T:
+//
+// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
+//
+// A user can teach this function how to print a class type T by
+// defining either operator<<() or PrintTo() in the namespace that
+// defines T. More specifically, the FIRST defined function in the
+// following list will be used (assuming T is defined in namespace
+// foo):
+//
+// 1. foo::PrintTo(const T&, ostream*)
+// 2. operator<<(ostream&, const T&) defined in either foo or the
+// global namespace.
+//
+// If none of the above is defined, it will print the debug string of
+// the value if it is a protocol buffer, or print the raw bytes in the
+// value otherwise.
+//
+// To aid debugging: when T is a reference type, the address of the
+// value is also printed; when T is a (const) char pointer, both the
+// pointer value and the NUL-terminated string it points to are
+// printed.
+//
+// We also provide some convenient wrappers:
+//
+// // Prints a value to a string. For a (const or not) char
+// // pointer, the NUL-terminated string (but not the pointer) is
+// // printed.
+// std::string ::testing::PrintToString(const T& value);
+//
+// // Prints a value tersely: for a reference type, the referenced
+// // value (but not the address) is printed; for a (const or not) char
+// // pointer, the NUL-terminated string (but not the pointer) is
+// // printed.
+// void ::testing::internal::UniversalTersePrint(const T& value, ostream*);
+//
+// // Prints value using the type inferred by the compiler. The difference
+// // from UniversalTersePrint() is that this function prints both the
+// // pointer and the NUL-terminated string for a (const or not) char pointer.
+// void ::testing::internal::UniversalPrint(const T& value, ostream*);
+//
+// // Prints the fields of a tuple tersely to a string vector, one
+// // element for each field. Tuple support must be enabled in
+// // gtest-port.h.
+// std::vector<string> UniversalTersePrintTupleFieldsToStrings(
+// const Tuple& value);
+//
+// Known limitation:
+//
+// The print primitives print the elements of an STL-style container
+// using the compiler-inferred type of *iter where iter is a
+// const_iterator of the container. When const_iterator is an input
+// iterator but not a forward iterator, this inferred type may not
+// match value_type, and the print output may be incorrect. In
+// practice, this is rarely a problem as for most containers
+// const_iterator is a forward iterator. We'll fix this if there's an
+// actual need for it. Note that this fix cannot rely on value_type
+// being defined as many user-defined container types don't have
+// value_type.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
+#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
+#ifndef _WIN32
+#pragma GCC system_header
+#endif
+
+#include <ostream> // NOLINT
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+#include "gtest/internal/gtest-port.h"
+#include "gtest/internal/gtest-internal.h"
+
+namespace testing {
+
+// Definitions in the 'internal' and 'internal2' name spaces are
+// subject to change without notice. DO NOT USE THEM IN USER CODE!
+namespace internal2 {
+
+// Prints the given number of bytes in the given object to the given
+// ostream.
+GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes,
+ size_t count,
+ ::std::ostream* os);
+
+// For selecting which printer to use when a given type has neither <<
+// nor PrintTo().
+enum TypeKind {
+ kProtobuf, // a protobuf type
+ kConvertibleToInteger, // a type implicitly convertible to BiggestInt
+ // (e.g. a named or unnamed enum type)
+ kOtherType // anything else
+};
+
+// TypeWithoutFormatter<T, kTypeKind>::PrintValue(value, os) is called
+// by the universal printer to print a value of type T when neither
+// operator<< nor PrintTo() is defined for T, where kTypeKind is the
+// "kind" of T as defined by enum TypeKind.
+template <typename T, TypeKind kTypeKind>
+class TypeWithoutFormatter {
+ public:
+ // This default version is called when kTypeKind is kOtherType.
+ static void PrintValue(const T& value, ::std::ostream* os) {
+ PrintBytesInObjectTo(reinterpret_cast<const unsigned char*>(&value),
+ sizeof(value), os);
+ }
+};
+
+// We print a protobuf using its ShortDebugString() when the string
+// doesn't exceed this many characters; otherwise we print it using
+// DebugString() for better readability.
+const size_t kProtobufOneLinerMaxLength = 50;
+
+template <typename T>
+class TypeWithoutFormatter<T, kProtobuf> {
+ public:
+ static void PrintValue(const T& value, ::std::ostream* os) {
+ const ::testing::internal::string short_str = value.ShortDebugString();
+ const ::testing::internal::string pretty_str =
+ short_str.length() <= kProtobufOneLinerMaxLength ?
+ short_str : ("\n" + value.DebugString());
+ *os << ("<" + pretty_str + ">");
+ }
+};
+
+template <typename T>
+class TypeWithoutFormatter<T, kConvertibleToInteger> {
+ public:
+ // Since T has no << operator or PrintTo() but can be implicitly
+ // converted to BiggestInt, we print it as a BiggestInt.
+ //
+ // Most likely T is an enum type (either named or unnamed), in which
+ // case printing it as an integer is the desired behavior. In case
+ // T is not an enum, printing it as an integer is the best we can do
+ // given that it has no user-defined printer.
+ static void PrintValue(const T& value, ::std::ostream* os) {
+ const internal::BiggestInt kBigInt = value;
+ *os << kBigInt;
+ }
+};
+
+// Prints the given value to the given ostream. If the value is a
+// protocol message, its debug string is printed; if it's an enum or
+// of a type implicitly convertible to BiggestInt, it's printed as an
+// integer; otherwise the bytes in the value are printed. This is
+// what UniversalPrinter<T>::Print() does when it knows nothing about
+// type T and T has neither << operator nor PrintTo().
+//
+// A user can override this behavior for a class type Foo by defining
+// a << operator in the namespace where Foo is defined.
+//
+// We put this operator in namespace 'internal2' instead of 'internal'
+// to simplify the implementation, as much code in 'internal' needs to
+// use << in STL, which would conflict with our own << were it defined
+// in 'internal'.
+//
+// Note that this operator<< takes a generic std::basic_ostream<Char,
+// CharTraits> type instead of the more restricted std::ostream. If
+// we define it to take an std::ostream instead, we'll get an
+// "ambiguous overloads" compiler error when trying to print a type
+// Foo that supports streaming to std::basic_ostream<Char,
+// CharTraits>, as the compiler cannot tell whether
+// operator<<(std::ostream&, const T&) or
+// operator<<(std::basic_stream<Char, CharTraits>, const Foo&) is more
+// specific.
+template <typename Char, typename CharTraits, typename T>
+::std::basic_ostream<Char, CharTraits>& operator<<(
+ ::std::basic_ostream<Char, CharTraits>& os, const T& x) {
+ TypeWithoutFormatter<T,
+ (internal::IsAProtocolMessage<T>::value ? kProtobuf :
+ internal::ImplicitlyConvertible<const T&, internal::BiggestInt>::value ?
+ kConvertibleToInteger : kOtherType)>::PrintValue(x, &os);
+ return os;
+}
+
+} // namespace internal2
+} // namespace testing
+
+// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up
+// magic needed for implementing UniversalPrinter won't work.
+namespace testing_internal {
+
+// Used to print a value that is not an STL-style container when the
+// user doesn't define PrintTo() for it.
+template <typename T>
+void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) {
+ // With the following statement, during unqualified name lookup,
+ // testing::internal2::operator<< appears as if it was declared in
+ // the nearest enclosing namespace that contains both
+ // ::testing_internal and ::testing::internal2, i.e. the global
+ // namespace. For more details, refer to the C++ Standard section
+ // 7.3.4-1 [namespace.udir]. This allows us to fall back onto
+ // testing::internal2::operator<< in case T doesn't come with a <<
+ // operator.
+ //
+ // We cannot write 'using ::testing::internal2::operator<<;', which
+ // gcc 3.3 fails to compile due to a compiler bug.
+ using namespace ::testing::internal2; // NOLINT
+
+ // Assuming T is defined in namespace foo, in the next statement,
+ // the compiler will consider all of:
+ //
+ // 1. foo::operator<< (thanks to Koenig look-up),
+ // 2. ::operator<< (as the current namespace is enclosed in ::),
+ // 3. testing::internal2::operator<< (thanks to the using statement above).
+ //
+ // The operator<< whose type matches T best will be picked.
+ //
+ // We deliberately allow #2 to be a candidate, as sometimes it's
+ // impossible to define #1 (e.g. when foo is ::std, defining
+ // anything in it is undefined behavior unless you are a compiler
+ // vendor.).
+ *os << value;
+}
+
+} // namespace testing_internal
+
+namespace testing {
+namespace internal {
+
+// UniversalPrinter<T>::Print(value, ostream_ptr) prints the given
+// value to the given ostream. The caller must ensure that
+// 'ostream_ptr' is not NULL, or the behavior is undefined.
+//
+// We define UniversalPrinter as a class template (as opposed to a
+// function template), as we need to partially specialize it for
+// reference types, which cannot be done with function templates.
+template <typename T>
+class UniversalPrinter;
+
+template <typename T>
+void UniversalPrint(const T& value, ::std::ostream* os);
+
+// Used to print an STL-style container when the user doesn't define
+// a PrintTo() for it.
+template <typename C>
+void DefaultPrintTo(IsContainer /* dummy */,
+ false_type /* is not a pointer */,
+ const C& container, ::std::ostream* os) {
+ const size_t kMaxCount = 32; // The maximum number of elements to print.
+ *os << '{';
+ size_t count = 0;
+ for (typename C::const_iterator it = container.begin();
+ it != container.end(); ++it, ++count) {
+ if (count > 0) {
+ *os << ',';
+ if (count == kMaxCount) { // Enough has been printed.
+ *os << " ...";
+ break;
+ }
+ }
+ *os << ' ';
+ // We cannot call PrintTo(*it, os) here as PrintTo() doesn't
+ // handle *it being a native array.
+ internal::UniversalPrint(*it, os);
+ }
+
+ if (count > 0) {
+ *os << ' ';
+ }
+ *os << '}';
+}
+
+// Used to print a pointer that is neither a char pointer nor a member
+// pointer, when the user doesn't define PrintTo() for it. (A member
+// variable pointer or member function pointer doesn't really point to
+// a location in the address space. Their representation is
+// implementation-defined. Therefore they will be printed as raw
+// bytes.)
+template <typename T>
+void DefaultPrintTo(IsNotContainer /* dummy */,
+ true_type /* is a pointer */,
+ T* p, ::std::ostream* os) {
+ if (p == NULL) {
+ *os << "NULL";
+ } else {
+ // C++ doesn't allow casting from a function pointer to any object
+ // pointer.
+ //
+ // IsTrue() silences warnings: "Condition is always true",
+ // "unreachable code".
+ if (IsTrue(ImplicitlyConvertible<T*, const void*>::value)) {
+ // T is not a function type. We just call << to print p,
+ // relying on ADL to pick up user-defined << for their pointer
+ // types, if any.
+ *os << p;
+ } else {
+ // T is a function type, so '*os << p' doesn't do what we want
+ // (it just prints p as bool). We want to print p as a const
+ // void*. However, we cannot cast it to const void* directly,
+ // even using reinterpret_cast, as earlier versions of gcc
+ // (e.g. 3.4.5) cannot compile the cast when p is a function
+ // pointer. Casting to UInt64 first solves the problem.
+ *os << reinterpret_cast<const void*>(
+ reinterpret_cast<internal::UInt64>(p));
+ }
+ }
+}
+
+// Used to print a non-container, non-pointer value when the user
+// doesn't define PrintTo() for it.
+template <typename T>
+void DefaultPrintTo(IsNotContainer /* dummy */,
+ false_type /* is not a pointer */,
+ const T& value, ::std::ostream* os) {
+ ::testing_internal::DefaultPrintNonContainerTo(value, os);
+}
+
+// Prints the given value using the << operator if it has one;
+// otherwise prints the bytes in it. This is what
+// UniversalPrinter<T>::Print() does when PrintTo() is not specialized
+// or overloaded for type T.
+//
+// A user can override this behavior for a class type Foo by defining
+// an overload of PrintTo() in the namespace where Foo is defined. We
+// give the user this option as sometimes defining a << operator for
+// Foo is not desirable (e.g. the coding style may prevent doing it,
+// or there is already a << operator but it doesn't do what the user
+// wants).
+template <typename T>
+void PrintTo(const T& value, ::std::ostream* os) {
+ // DefaultPrintTo() is overloaded. The type of its first two
+ // arguments determine which version will be picked. If T is an
+ // STL-style container, the version for container will be called; if
+ // T is a pointer, the pointer version will be called; otherwise the
+ // generic version will be called.
+ //
+ // Note that we check for container types here, prior to we check
+ // for protocol message types in our operator<<. The rationale is:
+ //
+ // For protocol messages, we want to give people a chance to
+ // override Google Mock's format by defining a PrintTo() or
+ // operator<<. For STL containers, other formats can be
+ // incompatible with Google Mock's format for the container
+ // elements; therefore we check for container types here to ensure
+ // that our format is used.
+ //
+ // The second argument of DefaultPrintTo() is needed to bypass a bug
+ // in Symbian's C++ compiler that prevents it from picking the right
+ // overload between:
+ //
+ // PrintTo(const T& x, ...);
+ // PrintTo(T* x, ...);
+ DefaultPrintTo(IsContainerTest<T>(0), is_pointer<T>(), value, os);
+}
+
+// The following list of PrintTo() overloads tells
+// UniversalPrinter<T>::Print() how to print standard types (built-in
+// types, strings, plain arrays, and pointers).
+
+// Overloads for various char types.
+GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os);
+GTEST_API_ void PrintTo(signed char c, ::std::ostream* os);
+inline void PrintTo(char c, ::std::ostream* os) {
+ // When printing a plain char, we always treat it as unsigned. This
+ // way, the output won't be affected by whether the compiler thinks
+ // char is signed or not.
+ PrintTo(static_cast<unsigned char>(c), os);
+}
+
+// Overloads for other simple built-in types.
+inline void PrintTo(bool x, ::std::ostream* os) {
+ *os << (x ? "true" : "false");
+}
+
+// Overload for wchar_t type.
+// Prints a wchar_t as a symbol if it is printable or as its internal
+// code otherwise and also as its decimal code (except for L'\0').
+// The L'\0' char is printed as "L'\\0'". The decimal code is printed
+// as signed integer when wchar_t is implemented by the compiler
+// as a signed type and is printed as an unsigned integer when wchar_t
+// is implemented as an unsigned type.
+GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os);
+
+// Overloads for C strings.
+GTEST_API_ void PrintTo(const char* s, ::std::ostream* os);
+inline void PrintTo(char* s, ::std::ostream* os) {
+ PrintTo(ImplicitCast_<const char*>(s), os);
+}
+
+// signed/unsigned char is often used for representing binary data, so
+// we print pointers to it as void* to be safe.
+inline void PrintTo(const signed char* s, ::std::ostream* os) {
+ PrintTo(ImplicitCast_<const void*>(s), os);
+}
+inline void PrintTo(signed char* s, ::std::ostream* os) {
+ PrintTo(ImplicitCast_<const void*>(s), os);
+}
+inline void PrintTo(const unsigned char* s, ::std::ostream* os) {
+ PrintTo(ImplicitCast_<const void*>(s), os);
+}
+inline void PrintTo(unsigned char* s, ::std::ostream* os) {
+ PrintTo(ImplicitCast_<const void*>(s), os);
+}
+
+// MSVC can be configured to define wchar_t as a typedef of unsigned
+// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native
+// type. When wchar_t is a typedef, defining an overload for const
+// wchar_t* would cause unsigned short* be printed as a wide string,
+// possibly causing invalid memory accesses.
+#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
+// Overloads for wide C strings
+GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os);
+inline void PrintTo(wchar_t* s, ::std::ostream* os) {
+ PrintTo(ImplicitCast_<const wchar_t*>(s), os);
+}
+#endif
+
+// Overload for C arrays. Multi-dimensional arrays are printed
+// properly.
+
+// Prints the given number of elements in an array, without printing
+// the curly braces.
+template <typename T>
+void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) {
+ UniversalPrint(a[0], os);
+ for (size_t i = 1; i != count; i++) {
+ *os << ", ";
+ UniversalPrint(a[i], os);
+ }
+}
+
+// Overloads for ::string and ::std::string.
+#if GTEST_HAS_GLOBAL_STRING
+GTEST_API_ void PrintStringTo(const ::string&s, ::std::ostream* os);
+inline void PrintTo(const ::string& s, ::std::ostream* os) {
+ PrintStringTo(s, os);
+}
+#endif // GTEST_HAS_GLOBAL_STRING
+
+GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os);
+inline void PrintTo(const ::std::string& s, ::std::ostream* os) {
+ PrintStringTo(s, os);
+}
+
+// Overloads for ::wstring and ::std::wstring.
+#if GTEST_HAS_GLOBAL_WSTRING
+GTEST_API_ void PrintWideStringTo(const ::wstring&s, ::std::ostream* os);
+inline void PrintTo(const ::wstring& s, ::std::ostream* os) {
+ PrintWideStringTo(s, os);
+}
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+#if GTEST_HAS_STD_WSTRING
+GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os);
+inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) {
+ PrintWideStringTo(s, os);
+}
+#endif // GTEST_HAS_STD_WSTRING
+
+#if GTEST_HAS_TR1_TUPLE
+// Overload for ::std::tr1::tuple. Needed for printing function arguments,
+// which are packed as tuples.
+
+// Helper function for printing a tuple. T must be instantiated with
+// a tuple type.
+template <typename T>
+void PrintTupleTo(const T& t, ::std::ostream* os);
+
+// Overloaded PrintTo() for tuples of various arities. We support
+// tuples of up-to 10 fields. The following implementation works
+// regardless of whether tr1::tuple is implemented using the
+// non-standard variadic template feature or not.
+
+inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1>
+void PrintTo(const ::std::tr1::tuple<T1>& t, ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2>
+void PrintTo(const ::std::tr1::tuple<T1, T2>& t, ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3>& t, ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4>& t, ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5>& t,
+ ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6>& t,
+ ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7>& t,
+ ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8>& t,
+ ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9>& t,
+ ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+void PrintTo(
+ const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>& t,
+ ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+#endif // GTEST_HAS_TR1_TUPLE
+
+// Overload for std::pair.
+template <typename T1, typename T2>
+void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) {
+ *os << '(';
+ // We cannot use UniversalPrint(value.first, os) here, as T1 may be
+ // a reference type. The same for printing value.second.
+ UniversalPrinter<T1>::Print(value.first, os);
+ *os << ", ";
+ UniversalPrinter<T2>::Print(value.second, os);
+ *os << ')';
+}
+
+// Implements printing a non-reference type T by letting the compiler
+// pick the right overload of PrintTo() for T.
+template <typename T>
+class UniversalPrinter {
+ public:
+ // MSVC warns about adding const to a function type, so we want to
+ // disable the warning.
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4180) // Temporarily disables warning 4180.
+#endif // _MSC_VER
+
+ // Note: we deliberately don't call this PrintTo(), as that name
+ // conflicts with ::testing::internal::PrintTo in the body of the
+ // function.
+ static void Print(const T& value, ::std::ostream* os) {
+ // By default, ::testing::internal::PrintTo() is used for printing
+ // the value.
+ //
+ // Thanks to Koenig look-up, if T is a class and has its own
+ // PrintTo() function defined in its namespace, that function will
+ // be visible here. Since it is more specific than the generic ones
+ // in ::testing::internal, it will be picked by the compiler in the
+ // following statement - exactly what we want.
+ PrintTo(value, os);
+ }
+
+#ifdef _MSC_VER
+# pragma warning(pop) // Restores the warning state.
+#endif // _MSC_VER
+};
+
+// UniversalPrintArray(begin, len, os) prints an array of 'len'
+// elements, starting at address 'begin'.
+template <typename T>
+void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) {
+ if (len == 0) {
+ *os << "{}";
+ } else {
+ *os << "{ ";
+ const size_t kThreshold = 18;
+ const size_t kChunkSize = 8;
+ // If the array has more than kThreshold elements, we'll have to
+ // omit some details by printing only the first and the last
+ // kChunkSize elements.
+ // TODO(wan at google.com): let the user control the threshold using a flag.
+ if (len <= kThreshold) {
+ PrintRawArrayTo(begin, len, os);
+ } else {
+ PrintRawArrayTo(begin, kChunkSize, os);
+ *os << ", ..., ";
+ PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os);
+ }
+ *os << " }";
+ }
+}
+// This overload prints a (const) char array compactly.
+GTEST_API_ void UniversalPrintArray(
+ const char* begin, size_t len, ::std::ostream* os);
+
+// This overload prints a (const) wchar_t array compactly.
+GTEST_API_ void UniversalPrintArray(
+ const wchar_t* begin, size_t len, ::std::ostream* os);
+
+// Implements printing an array type T[N].
+template <typename T, size_t N>
+class UniversalPrinter<T[N]> {
+ public:
+ // Prints the given array, omitting some elements when there are too
+ // many.
+ static void Print(const T (&a)[N], ::std::ostream* os) {
+ UniversalPrintArray(a, N, os);
+ }
+};
+
+// Implements printing a reference type T&.
+template <typename T>
+class UniversalPrinter<T&> {
+ public:
+ // MSVC warns about adding const to a function type, so we want to
+ // disable the warning.
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4180) // Temporarily disables warning 4180.
+#endif // _MSC_VER
+
+ static void Print(const T& value, ::std::ostream* os) {
+ // Prints the address of the value. We use reinterpret_cast here
+ // as static_cast doesn't compile when T is a function type.
+ *os << "@" << reinterpret_cast<const void*>(&value) << " ";
+
+ // Then prints the value itself.
+ UniversalPrint(value, os);
+ }
+
+#ifdef _MSC_VER
+# pragma warning(pop) // Restores the warning state.
+#endif // _MSC_VER
+};
+
+// Prints a value tersely: for a reference type, the referenced value
+// (but not the address) is printed; for a (const) char pointer, the
+// NUL-terminated string (but not the pointer) is printed.
+
+template <typename T>
+class UniversalTersePrinter {
+ public:
+ static void Print(const T& value, ::std::ostream* os) {
+ UniversalPrint(value, os);
+ }
+};
+template <typename T>
+class UniversalTersePrinter<T&> {
+ public:
+ static void Print(const T& value, ::std::ostream* os) {
+ UniversalPrint(value, os);
+ }
+};
+template <typename T, size_t N>
+class UniversalTersePrinter<T[N]> {
+ public:
+ static void Print(const T (&value)[N], ::std::ostream* os) {
+ UniversalPrinter<T[N]>::Print(value, os);
+ }
+};
+template <>
+class UniversalTersePrinter<const char*> {
+ public:
+ static void Print(const char* str, ::std::ostream* os) {
+ if (str == NULL) {
+ *os << "NULL";
+ } else {
+ UniversalPrint(string(str), os);
+ }
+ }
+};
+template <>
+class UniversalTersePrinter<char*> {
+ public:
+ static void Print(char* str, ::std::ostream* os) {
+ UniversalTersePrinter<const char*>::Print(str, os);
+ }
+};
+
+#if GTEST_HAS_STD_WSTRING
+template <>
+class UniversalTersePrinter<const wchar_t*> {
+ public:
+ static void Print(const wchar_t* str, ::std::ostream* os) {
+ if (str == NULL) {
+ *os << "NULL";
+ } else {
+ UniversalPrint(::std::wstring(str), os);
+ }
+ }
+};
+#endif
+
+template <>
+class UniversalTersePrinter<wchar_t*> {
+ public:
+ static void Print(wchar_t* str, ::std::ostream* os) {
+ UniversalTersePrinter<const wchar_t*>::Print(str, os);
+ }
+};
+
+template <typename T>
+void UniversalTersePrint(const T& value, ::std::ostream* os) {
+ UniversalTersePrinter<T>::Print(value, os);
+}
+
+// Prints a value using the type inferred by the compiler. The
+// difference between this and UniversalTersePrint() is that for a
+// (const) char pointer, this prints both the pointer and the
+// NUL-terminated string.
+template <typename T>
+void UniversalPrint(const T& value, ::std::ostream* os) {
+ // A workarond for the bug in VC++ 7.1 that prevents us from instantiating
+ // UniversalPrinter with T directly.
+ typedef T T1;
+ UniversalPrinter<T1>::Print(value, os);
+}
+
+#if GTEST_HAS_TR1_TUPLE
+typedef ::std::vector<string> Strings;
+
+// This helper template allows PrintTo() for tuples and
+// UniversalTersePrintTupleFieldsToStrings() to be defined by
+// induction on the number of tuple fields. The idea is that
+// TuplePrefixPrinter<N>::PrintPrefixTo(t, os) prints the first N
+// fields in tuple t, and can be defined in terms of
+// TuplePrefixPrinter<N - 1>.
+
+// The inductive case.
+template <size_t N>
+struct TuplePrefixPrinter {
+ // Prints the first N fields of a tuple.
+ template <typename Tuple>
+ static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
+ TuplePrefixPrinter<N - 1>::PrintPrefixTo(t, os);
+ *os << ", ";
+ UniversalPrinter<typename ::std::tr1::tuple_element<N - 1, Tuple>::type>
+ ::Print(::std::tr1::get<N - 1>(t), os);
+ }
+
+ // Tersely prints the first N fields of a tuple to a string vector,
+ // one element for each field.
+ template <typename Tuple>
+ static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
+ TuplePrefixPrinter<N - 1>::TersePrintPrefixToStrings(t, strings);
+ ::std::stringstream ss;
+ UniversalTersePrint(::std::tr1::get<N - 1>(t), &ss);
+ strings->push_back(ss.str());
+ }
+};
+
+// Base cases.
+template <>
+struct TuplePrefixPrinter<0> {
+ template <typename Tuple>
+ static void PrintPrefixTo(const Tuple&, ::std::ostream*) {}
+
+ template <typename Tuple>
+ static void TersePrintPrefixToStrings(const Tuple&, Strings*) {}
+};
+// We have to specialize the entire TuplePrefixPrinter<> class
+// template here, even though the definition of
+// TersePrintPrefixToStrings() is the same as the generic version, as
+// Embarcadero (formerly CodeGear, formerly Borland) C++ doesn't
+// support specializing a method template of a class template.
+template <>
+struct TuplePrefixPrinter<1> {
+ template <typename Tuple>
+ static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
+ UniversalPrinter<typename ::std::tr1::tuple_element<0, Tuple>::type>::
+ Print(::std::tr1::get<0>(t), os);
+ }
+
+ template <typename Tuple>
+ static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
+ ::std::stringstream ss;
+ UniversalTersePrint(::std::tr1::get<0>(t), &ss);
+ strings->push_back(ss.str());
+ }
+};
+
+// Helper function for printing a tuple. T must be instantiated with
+// a tuple type.
+template <typename T>
+void PrintTupleTo(const T& t, ::std::ostream* os) {
+ *os << "(";
+ TuplePrefixPrinter< ::std::tr1::tuple_size<T>::value>::
+ PrintPrefixTo(t, os);
+ *os << ")";
+}
+
+// Prints the fields of a tuple tersely to a string vector, one
+// element for each field. See the comment before
+// UniversalTersePrint() for how we define "tersely".
+template <typename Tuple>
+Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) {
+ Strings result;
+ TuplePrefixPrinter< ::std::tr1::tuple_size<Tuple>::value>::
+ TersePrintPrefixToStrings(value, &result);
+ return result;
+}
+#endif // GTEST_HAS_TR1_TUPLE
+
+} // namespace internal
+
+template <typename T>
+::std::string PrintToString(const T& value) {
+ ::std::stringstream ss;
+ internal::UniversalTersePrinter<T>::Print(value, &ss);
+ return ss.str();
+}
+
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
diff --git a/test/gtest/include/gtest/gtest-spi.h b/test/gtest/include/gtest/gtest-spi.h
new file mode 100644
index 0000000..f63fa9a
--- /dev/null
+++ b/test/gtest/include/gtest/gtest-spi.h
@@ -0,0 +1,232 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+//
+// Utilities for testing Google Test itself and code that uses Google Test
+// (e.g. frameworks built on top of Google Test).
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_
+#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_
+
+#include "gtest/gtest.h"
+
+namespace testing {
+
+// This helper class can be used to mock out Google Test failure reporting
+// so that we can test Google Test or code that builds on Google Test.
+//
+// An object of this class appends a TestPartResult object to the
+// TestPartResultArray object given in the constructor whenever a Google Test
+// failure is reported. It can either intercept only failures that are
+// generated in the same thread that created this object or it can intercept
+// all generated failures. The scope of this mock object can be controlled with
+// the second argument to the two arguments constructor.
+class GTEST_API_ ScopedFakeTestPartResultReporter
+ : public TestPartResultReporterInterface {
+ public:
+ // The two possible mocking modes of this object.
+ enum InterceptMode {
+ INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures.
+ INTERCEPT_ALL_THREADS // Intercepts all failures.
+ };
+
+ // The c'tor sets this object as the test part result reporter used
+ // by Google Test. The 'result' parameter specifies where to report the
+ // results. This reporter will only catch failures generated in the current
+ // thread. DEPRECATED
+ explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result);
+
+ // Same as above, but you can choose the interception scope of this object.
+ ScopedFakeTestPartResultReporter(InterceptMode intercept_mode,
+ TestPartResultArray* result);
+
+ // The d'tor restores the previous test part result reporter.
+ virtual ~ScopedFakeTestPartResultReporter();
+
+ // Appends the TestPartResult object to the TestPartResultArray
+ // received in the constructor.
+ //
+ // This method is from the TestPartResultReporterInterface
+ // interface.
+ virtual void ReportTestPartResult(const TestPartResult& result);
+ private:
+ void Init();
+
+ const InterceptMode intercept_mode_;
+ TestPartResultReporterInterface* old_reporter_;
+ TestPartResultArray* const result_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter);
+};
+
+namespace internal {
+
+// A helper class for implementing EXPECT_FATAL_FAILURE() and
+// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given
+// TestPartResultArray contains exactly one failure that has the given
+// type and contains the given substring. If that's not the case, a
+// non-fatal failure will be generated.
+class GTEST_API_ SingleFailureChecker {
+ public:
+ // The constructor remembers the arguments.
+ SingleFailureChecker(const TestPartResultArray* results,
+ TestPartResult::Type type,
+ const string& substr);
+ ~SingleFailureChecker();
+ private:
+ const TestPartResultArray* const results_;
+ const TestPartResult::Type type_;
+ const string substr_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker);
+};
+
+} // namespace internal
+
+} // namespace testing
+
+// A set of macros for testing Google Test assertions or code that's expected
+// to generate Google Test fatal failures. It verifies that the given
+// statement will cause exactly one fatal Google Test failure with 'substr'
+// being part of the failure message.
+//
+// There are two different versions of this macro. EXPECT_FATAL_FAILURE only
+// affects and considers failures generated in the current thread and
+// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
+//
+// The verification of the assertion is done correctly even when the statement
+// throws an exception or aborts the current function.
+//
+// Known restrictions:
+// - 'statement' cannot reference local non-static variables or
+// non-static members of the current object.
+// - 'statement' cannot return a value.
+// - You cannot stream a failure message to this macro.
+//
+// Note that even though the implementations of the following two
+// macros are much alike, we cannot refactor them to use a common
+// helper macro, due to some peculiarity in how the preprocessor
+// works. The AcceptsMacroThatExpandsToUnprotectedComma test in
+// gtest_unittest.cc will fail to compile if we do that.
+#define EXPECT_FATAL_FAILURE(statement, substr) \
+ do { \
+ class GTestExpectFatalFailureHelper {\
+ public:\
+ static void Execute() { statement; }\
+ };\
+ ::testing::TestPartResultArray gtest_failures;\
+ ::testing::internal::SingleFailureChecker gtest_checker(\
+ >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter:: \
+ INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\
+ GTestExpectFatalFailureHelper::Execute();\
+ }\
+ } while (::testing::internal::AlwaysFalse())
+
+#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
+ do { \
+ class GTestExpectFatalFailureHelper {\
+ public:\
+ static void Execute() { statement; }\
+ };\
+ ::testing::TestPartResultArray gtest_failures;\
+ ::testing::internal::SingleFailureChecker gtest_checker(\
+ >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter:: \
+ INTERCEPT_ALL_THREADS, >est_failures);\
+ GTestExpectFatalFailureHelper::Execute();\
+ }\
+ } while (::testing::internal::AlwaysFalse())
+
+// A macro for testing Google Test assertions or code that's expected to
+// generate Google Test non-fatal failures. It asserts that the given
+// statement will cause exactly one non-fatal Google Test failure with 'substr'
+// being part of the failure message.
+//
+// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only
+// affects and considers failures generated in the current thread and
+// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
+//
+// 'statement' is allowed to reference local variables and members of
+// the current object.
+//
+// The verification of the assertion is done correctly even when the statement
+// throws an exception or aborts the current function.
+//
+// Known restrictions:
+// - You cannot stream a failure message to this macro.
+//
+// Note that even though the implementations of the following two
+// macros are much alike, we cannot refactor them to use a common
+// helper macro, due to some peculiarity in how the preprocessor
+// works. If we do that, the code won't compile when the user gives
+// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that
+// expands to code containing an unprotected comma. The
+// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc
+// catches that.
+//
+// For the same reason, we have to write
+// if (::testing::internal::AlwaysTrue()) { statement; }
+// instead of
+// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
+// to avoid an MSVC warning on unreachable code.
+#define EXPECT_NONFATAL_FAILURE(statement, substr) \
+ do {\
+ ::testing::TestPartResultArray gtest_failures;\
+ ::testing::internal::SingleFailureChecker gtest_checker(\
+ >est_failures, ::testing::TestPartResult::kNonFatalFailure, \
+ (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter:: \
+ INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\
+ if (::testing::internal::AlwaysTrue()) { statement; }\
+ }\
+ } while (::testing::internal::AlwaysFalse())
+
+#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
+ do {\
+ ::testing::TestPartResultArray gtest_failures;\
+ ::testing::internal::SingleFailureChecker gtest_checker(\
+ >est_failures, ::testing::TestPartResult::kNonFatalFailure, \
+ (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \
+ >est_failures);\
+ if (::testing::internal::AlwaysTrue()) { statement; }\
+ }\
+ } while (::testing::internal::AlwaysFalse())
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_
diff --git a/test/gtest/include/gtest/gtest-test-part.h b/test/gtest/include/gtest/gtest-test-part.h
new file mode 100644
index 0000000..77eb844
--- /dev/null
+++ b/test/gtest/include/gtest/gtest-test-part.h
@@ -0,0 +1,179 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: mheule at google.com (Markus Heule)
+//
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
+#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
+
+#include <iosfwd>
+#include <vector>
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-string.h"
+
+namespace testing {
+
+// A copyable object representing the result of a test part (i.e. an
+// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()).
+//
+// Don't inherit from TestPartResult as its destructor is not virtual.
+class GTEST_API_ TestPartResult {
+ public:
+ // The possible outcomes of a test part (i.e. an assertion or an
+ // explicit SUCCEED(), FAIL(), or ADD_FAILURE()).
+ enum Type {
+ kSuccess, // Succeeded.
+ kNonFatalFailure, // Failed but the test can continue.
+ kFatalFailure // Failed and the test should be terminated.
+ };
+
+ // C'tor. TestPartResult does NOT have a default constructor.
+ // Always use this constructor (with parameters) to create a
+ // TestPartResult object.
+ TestPartResult(Type a_type,
+ const char* a_file_name,
+ int a_line_number,
+ const char* a_message)
+ : type_(a_type),
+ file_name_(a_file_name == NULL ? "" : a_file_name),
+ line_number_(a_line_number),
+ summary_(ExtractSummary(a_message)),
+ message_(a_message) {
+ }
+
+ // Gets the outcome of the test part.
+ Type type() const { return type_; }
+
+ // Gets the name of the source file where the test part took place, or
+ // NULL if it's unknown.
+ const char* file_name() const {
+ return file_name_.empty() ? NULL : file_name_.c_str();
+ }
+
+ // Gets the line in the source file where the test part took place,
+ // or -1 if it's unknown.
+ int line_number() const { return line_number_; }
+
+ // Gets the summary of the failure message.
+ const char* summary() const { return summary_.c_str(); }
+
+ // Gets the message associated with the test part.
+ const char* message() const { return message_.c_str(); }
+
+ // Returns true iff the test part passed.
+ bool passed() const { return type_ == kSuccess; }
+
+ // Returns true iff the test part failed.
+ bool failed() const { return type_ != kSuccess; }
+
+ // Returns true iff the test part non-fatally failed.
+ bool nonfatally_failed() const { return type_ == kNonFatalFailure; }
+
+ // Returns true iff the test part fatally failed.
+ bool fatally_failed() const { return type_ == kFatalFailure; }
+
+ private:
+ Type type_;
+
+ // Gets the summary of the failure message by omitting the stack
+ // trace in it.
+ static std::string ExtractSummary(const char* message);
+
+ // The name of the source file where the test part took place, or
+ // "" if the source file is unknown.
+ std::string file_name_;
+ // The line in the source file where the test part took place, or -1
+ // if the line number is unknown.
+ int line_number_;
+ std::string summary_; // The test failure summary.
+ std::string message_; // The test failure message.
+};
+
+// Prints a TestPartResult object.
+std::ostream& operator<<(std::ostream& os, const TestPartResult& result);
+
+// An array of TestPartResult objects.
+//
+// Don't inherit from TestPartResultArray as its destructor is not
+// virtual.
+class GTEST_API_ TestPartResultArray {
+ public:
+ TestPartResultArray() {}
+
+ // Appends the given TestPartResult to the array.
+ void Append(const TestPartResult& result);
+
+ // Returns the TestPartResult at the given index (0-based).
+ const TestPartResult& GetTestPartResult(int index) const;
+
+ // Returns the number of TestPartResult objects in the array.
+ int size() const;
+
+ private:
+ std::vector<TestPartResult> array_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray);
+};
+
+// This interface knows how to report a test part result.
+class TestPartResultReporterInterface {
+ public:
+ virtual ~TestPartResultReporterInterface() {}
+
+ virtual void ReportTestPartResult(const TestPartResult& result) = 0;
+};
+
+namespace internal {
+
+// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a
+// statement generates new fatal failures. To do so it registers itself as the
+// current test part result reporter. Besides checking if fatal failures were
+// reported, it only delegates the reporting to the former result reporter.
+// The original result reporter is restored in the destructor.
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+class GTEST_API_ HasNewFatalFailureHelper
+ : public TestPartResultReporterInterface {
+ public:
+ HasNewFatalFailureHelper();
+ virtual ~HasNewFatalFailureHelper();
+ virtual void ReportTestPartResult(const TestPartResult& result);
+ bool has_new_fatal_failure() const { return has_new_fatal_failure_; }
+ private:
+ bool has_new_fatal_failure_;
+ TestPartResultReporterInterface* original_reporter_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper);
+};
+
+} // namespace internal
+
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
diff --git a/test/gtest/include/gtest/gtest-typed-test.h b/test/gtest/include/gtest/gtest-typed-test.h
new file mode 100644
index 0000000..9e62fdb
--- /dev/null
+++ b/test/gtest/include/gtest/gtest-typed-test.h
@@ -0,0 +1,263 @@
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
+
+#ifndef _WIN32
+#pragma GCC system_header
+#endif
+
+// This header implements typed tests and type-parameterized tests.
+
+// Typed (aka type-driven) tests repeat the same test for types in a
+// list. You must know which types you want to test with when writing
+// typed tests. Here's how you do it:
+
+#if 0
+
+// First, define a fixture class template. It should be parameterized
+// by a type. Remember to derive it from testing::Test.
+template <typename T>
+class FooTest : public testing::Test {
+ public:
+ ...
+ typedef std::list<T> List;
+ static T shared_;
+ T value_;
+};
+
+// Next, associate a list of types with the test case, which will be
+// repeated for each type in the list. The typedef is necessary for
+// the macro to parse correctly.
+typedef testing::Types<char, int, unsigned int> MyTypes;
+TYPED_TEST_CASE(FooTest, MyTypes);
+
+// If the type list contains only one type, you can write that type
+// directly without Types<...>:
+// TYPED_TEST_CASE(FooTest, int);
+
+// Then, use TYPED_TEST() instead of TEST_F() to define as many typed
+// tests for this test case as you want.
+TYPED_TEST(FooTest, DoesBlah) {
+ // Inside a test, refer to TypeParam to get the type parameter.
+ // Since we are inside a derived class template, C++ requires use to
+ // visit the members of FooTest via 'this'.
+ TypeParam n = this->value_;
+
+ // To visit static members of the fixture, add the TestFixture::
+ // prefix.
+ n += TestFixture::shared_;
+
+ // To refer to typedefs in the fixture, add the "typename
+ // TestFixture::" prefix.
+ typename TestFixture::List values;
+ values.push_back(n);
+ ...
+}
+
+TYPED_TEST(FooTest, HasPropertyA) { ... }
+
+#endif // 0
+
+// Type-parameterized tests are abstract test patterns parameterized
+// by a type. Compared with typed tests, type-parameterized tests
+// allow you to define the test pattern without knowing what the type
+// parameters are. The defined pattern can be instantiated with
+// different types any number of times, in any number of translation
+// units.
+//
+// If you are designing an interface or concept, you can define a
+// suite of type-parameterized tests to verify properties that any
+// valid implementation of the interface/concept should have. Then,
+// each implementation can easily instantiate the test suite to verify
+// that it conforms to the requirements, without having to write
+// similar tests repeatedly. Here's an example:
+
+#if 0
+
+// First, define a fixture class template. It should be parameterized
+// by a type. Remember to derive it from testing::Test.
+template <typename T>
+class FooTest : public testing::Test {
+ ...
+};
+
+// Next, declare that you will define a type-parameterized test case
+// (the _P suffix is for "parameterized" or "pattern", whichever you
+// prefer):
+TYPED_TEST_CASE_P(FooTest);
+
+// Then, use TYPED_TEST_P() to define as many type-parameterized tests
+// for this type-parameterized test case as you want.
+TYPED_TEST_P(FooTest, DoesBlah) {
+ // Inside a test, refer to TypeParam to get the type parameter.
+ TypeParam n = 0;
+ ...
+}
+
+TYPED_TEST_P(FooTest, HasPropertyA) { ... }
+
+// Now the tricky part: you need to register all test patterns before
+// you can instantiate them. The first argument of the macro is the
+// test case name; the rest are the names of the tests in this test
+// case.
+REGISTER_TYPED_TEST_CASE_P(FooTest,
+ DoesBlah, HasPropertyA);
+
+// Finally, you are free to instantiate the pattern with the types you
+// want. If you put the above code in a header file, you can #include
+// it in multiple C++ source files and instantiate it multiple times.
+//
+// To distinguish different instances of the pattern, the first
+// argument to the INSTANTIATE_* macro is a prefix that will be added
+// to the actual test case name. Remember to pick unique prefixes for
+// different instances.
+typedef testing::Types<char, int, unsigned int> MyTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
+
+// If the type list contains only one type, you can write that type
+// directly without Types<...>:
+// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int);
+
+#endif // 0
+
+#include "gtest/internal/gtest-port.h"
+#include "gtest/internal/gtest-type-util.h"
+
+// Implements typed tests.
+
+#if GTEST_HAS_TYPED_TEST
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Expands to the name of the typedef for the type parameters of the
+// given test case.
+# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_
+
+// The 'Types' template argument below must have spaces around it
+// since some compilers may choke on '>>' when passing a template
+// instance (e.g. Types<int>)
+# define TYPED_TEST_CASE(CaseName, Types) \
+ typedef ::testing::internal::TypeList< Types >::type \
+ GTEST_TYPE_PARAMS_(CaseName)
+
+# define TYPED_TEST(CaseName, TestName) \
+ template <typename gtest_TypeParam_> \
+ class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \
+ : public CaseName<gtest_TypeParam_> { \
+ private: \
+ typedef CaseName<gtest_TypeParam_> TestFixture; \
+ typedef gtest_TypeParam_ TypeParam; \
+ virtual void TestBody(); \
+ }; \
+ bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \
+ ::testing::internal::TypeParameterizedTest< \
+ CaseName, \
+ ::testing::internal::TemplateSel< \
+ GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \
+ GTEST_TYPE_PARAMS_(CaseName)>::Register(\
+ "", #CaseName, #TestName, 0); \
+ template <typename gtest_TypeParam_> \
+ void GTEST_TEST_CLASS_NAME_(CaseName, TestName)<gtest_TypeParam_>::TestBody()
+
+#endif // GTEST_HAS_TYPED_TEST
+
+// Implements type-parameterized tests.
+
+#if GTEST_HAS_TYPED_TEST_P
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Expands to the namespace name that the type-parameterized tests for
+// the given type-parameterized test case are defined in. The exact
+// name of the namespace is subject to change without notice.
+# define GTEST_CASE_NAMESPACE_(TestCaseName) \
+ gtest_case_##TestCaseName##_
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Expands to the name of the variable used to remember the names of
+// the defined tests in the given test case.
+# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \
+ gtest_typed_test_case_p_state_##TestCaseName##_
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY.
+//
+// Expands to the name of the variable used to remember the names of
+// the registered tests in the given test case.
+# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \
+ gtest_registered_test_names_##TestCaseName##_
+
+// The variables defined in the type-parameterized test macros are
+// static as typically these macros are used in a .h file that can be
+// #included in multiple translation units linked together.
+# define TYPED_TEST_CASE_P(CaseName) \
+ static ::testing::internal::TypedTestCasePState \
+ GTEST_TYPED_TEST_CASE_P_STATE_(CaseName)
+
+# define TYPED_TEST_P(CaseName, TestName) \
+ namespace GTEST_CASE_NAMESPACE_(CaseName) { \
+ template <typename gtest_TypeParam_> \
+ class TestName : public CaseName<gtest_TypeParam_> { \
+ private: \
+ typedef CaseName<gtest_TypeParam_> TestFixture; \
+ typedef gtest_TypeParam_ TypeParam; \
+ virtual void TestBody(); \
+ }; \
+ static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \
+ GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\
+ __FILE__, __LINE__, #CaseName, #TestName); \
+ } \
+ template <typename gtest_TypeParam_> \
+ void GTEST_CASE_NAMESPACE_(CaseName)::TestName<gtest_TypeParam_>::TestBody()
+
+# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \
+ namespace GTEST_CASE_NAMESPACE_(CaseName) { \
+ typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \
+ } \
+ static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \
+ GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\
+ __FILE__, __LINE__, #__VA_ARGS__)
+
+// The 'Types' template argument below must have spaces around it
+// since some compilers may choke on '>>' when passing a template
+// instance (e.g. Types<int>)
+# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \
+ bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \
+ ::testing::internal::TypeParameterizedTestCase<CaseName, \
+ GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \
+ ::testing::internal::TypeList< Types >::type>::Register(\
+ #Prefix, #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName))
+
+#endif // GTEST_HAS_TYPED_TEST_P
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
diff --git a/test/gtest/include/gtest/gtest.h b/test/gtest/include/gtest/gtest.h
new file mode 100644
index 0000000..6fa0a39
--- /dev/null
+++ b/test/gtest/include/gtest/gtest.h
@@ -0,0 +1,2291 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file defines the public API for Google Test. It should be
+// included by any test program that uses Google Test.
+//
+// IMPORTANT NOTE: Due to limitation of the C++ language, we have to
+// leave some internal implementation details in this header file.
+// They are clearly marked by comments like this:
+//
+// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+//
+// Such code is NOT meant to be used by a user directly, and is subject
+// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user
+// program!
+//
+// Acknowledgment: Google Test borrowed the idea of automatic test
+// registration from Barthelemy Dagenais' (barthelemy at prologique.com)
+// easyUnit framework.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_H_
+
+#include <limits>
+#include <ostream>
+#include <vector>
+
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-string.h"
+#include "gtest/gtest-death-test.h"
+#include "gtest/gtest-message.h"
+#include "gtest/gtest-param-test.h"
+#include "gtest/gtest-printers.h"
+#include "gtest/gtest_prod.h"
+#include "gtest/gtest-test-part.h"
+#include "gtest/gtest-typed-test.h"
+
+// Depending on the platform, different string classes are available.
+// On Linux, in addition to ::std::string, Google also makes use of
+// class ::string, which has the same interface as ::std::string, but
+// has a different implementation.
+//
+// The user can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that
+// ::string is available AND is a distinct type to ::std::string, or
+// define it to 0 to indicate otherwise.
+//
+// If the user's ::std::string and ::string are the same class due to
+// aliasing, he should define GTEST_HAS_GLOBAL_STRING to 0.
+//
+// If the user doesn't define GTEST_HAS_GLOBAL_STRING, it is defined
+// heuristically.
+
+namespace testing {
+
+// Declares the flags.
+
+// This flag temporary enables the disabled tests.
+GTEST_DECLARE_bool_(also_run_disabled_tests);
+
+// This flag brings the debugger on an assertion failure.
+GTEST_DECLARE_bool_(break_on_failure);
+
+// This flag controls whether Google Test catches all test-thrown exceptions
+// and logs them as failures.
+GTEST_DECLARE_bool_(catch_exceptions);
+
+// This flag enables using colors in terminal output. Available values are
+// "yes" to enable colors, "no" (disable colors), or "auto" (the default)
+// to let Google Test decide.
+GTEST_DECLARE_string_(color);
+
+// This flag sets up the filter to select by name using a glob pattern
+// the tests to run. If the filter is not given all tests are executed.
+GTEST_DECLARE_string_(filter);
+
+// This flag causes the Google Test to list tests. None of the tests listed
+// are actually run if the flag is provided.
+GTEST_DECLARE_bool_(list_tests);
+
+// This flag controls whether Google Test emits a detailed XML report to a file
+// in addition to its normal textual output.
+GTEST_DECLARE_string_(output);
+
+// This flags control whether Google Test prints the elapsed time for each
+// test.
+GTEST_DECLARE_bool_(print_time);
+
+// This flag specifies the random number seed.
+GTEST_DECLARE_int32_(random_seed);
+
+// This flag sets how many times the tests are repeated. The default value
+// is 1. If the value is -1 the tests are repeating forever.
+GTEST_DECLARE_int32_(repeat);
+
+// This flag controls whether Google Test includes Google Test internal
+// stack frames in failure stack traces.
+GTEST_DECLARE_bool_(show_internal_stack_frames);
+
+// When this flag is specified, tests' order is randomized on every iteration.
+GTEST_DECLARE_bool_(shuffle);
+
+// This flag specifies the maximum number of stack frames to be
+// printed in a failure message.
+GTEST_DECLARE_int32_(stack_trace_depth);
+
+// When this flag is specified, a failed assertion will throw an
+// exception if exceptions are enabled, or exit the program with a
+// non-zero code otherwise.
+GTEST_DECLARE_bool_(throw_on_failure);
+
+// When this flag is set with a "host:port" string, on supported
+// platforms test results are streamed to the specified port on
+// the specified host machine.
+GTEST_DECLARE_string_(stream_result_to);
+
+// The upper limit for valid stack trace depths.
+const int kMaxStackTraceDepth = 100;
+
+namespace internal {
+
+class AssertHelper;
+class DefaultGlobalTestPartResultReporter;
+class ExecDeathTest;
+class NoExecDeathTest;
+class FinalSuccessChecker;
+class GTestFlagSaver;
+class StreamingListenerTest;
+class TestResultAccessor;
+class TestEventListenersAccessor;
+class TestEventRepeater;
+class UnitTestRecordPropertyTestHelper;
+class WindowsDeathTest;
+class UnitTestImpl* GetUnitTestImpl();
+void ReportFailureInUnknownLocation(TestPartResult::Type result_type,
+ const std::string& message);
+
+} // namespace internal
+
+// The friend relationship of some of these classes is cyclic.
+// If we don't forward declare them the compiler might confuse the classes
+// in friendship clauses with same named classes on the scope.
+class Test;
+class TestCase;
+class TestInfo;
+class UnitTest;
+
+// A class for indicating whether an assertion was successful. When
+// the assertion wasn't successful, the AssertionResult object
+// remembers a non-empty message that describes how it failed.
+//
+// To create an instance of this class, use one of the factory functions
+// (AssertionSuccess() and AssertionFailure()).
+//
+// This class is useful for two purposes:
+// 1. Defining predicate functions to be used with Boolean test assertions
+// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts
+// 2. Defining predicate-format functions to be
+// used with predicate assertions (ASSERT_PRED_FORMAT*, etc).
+//
+// For example, if you define IsEven predicate:
+//
+// testing::AssertionResult IsEven(int n) {
+// if ((n % 2) == 0)
+// return testing::AssertionSuccess();
+// else
+// return testing::AssertionFailure() << n << " is odd";
+// }
+//
+// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5)))
+// will print the message
+//
+// Value of: IsEven(Fib(5))
+// Actual: false (5 is odd)
+// Expected: true
+//
+// instead of a more opaque
+//
+// Value of: IsEven(Fib(5))
+// Actual: false
+// Expected: true
+//
+// in case IsEven is a simple Boolean predicate.
+//
+// If you expect your predicate to be reused and want to support informative
+// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up
+// about half as often as positive ones in our tests), supply messages for
+// both success and failure cases:
+//
+// testing::AssertionResult IsEven(int n) {
+// if ((n % 2) == 0)
+// return testing::AssertionSuccess() << n << " is even";
+// else
+// return testing::AssertionFailure() << n << " is odd";
+// }
+//
+// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print
+//
+// Value of: IsEven(Fib(6))
+// Actual: true (8 is even)
+// Expected: false
+//
+// NB: Predicates that support negative Boolean assertions have reduced
+// performance in positive ones so be careful not to use them in tests
+// that have lots (tens of thousands) of positive Boolean assertions.
+//
+// To use this class with EXPECT_PRED_FORMAT assertions such as:
+//
+// // Verifies that Foo() returns an even number.
+// EXPECT_PRED_FORMAT1(IsEven, Foo());
+//
+// you need to define:
+//
+// testing::AssertionResult IsEven(const char* expr, int n) {
+// if ((n % 2) == 0)
+// return testing::AssertionSuccess();
+// else
+// return testing::AssertionFailure()
+// << "Expected: " << expr << " is even\n Actual: it's " << n;
+// }
+//
+// If Foo() returns 5, you will see the following message:
+//
+// Expected: Foo() is even
+// Actual: it's 5
+//
+class GTEST_API_ AssertionResult {
+ public:
+ // Copy constructor.
+ // Used in EXPECT_TRUE/FALSE(assertion_result).
+ AssertionResult(const AssertionResult& other);
+ // Used in the EXPECT_TRUE/FALSE(bool_expression).
+ explicit AssertionResult(bool success) : success_(success) {}
+
+ // Returns true iff the assertion succeeded.
+ operator bool() const { return success_; } // NOLINT
+
+ // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
+ AssertionResult operator!() const;
+
+ // Returns the text streamed into this AssertionResult. Test assertions
+ // use it when they fail (i.e., the predicate's outcome doesn't match the
+ // assertion's expectation). When nothing has been streamed into the
+ // object, returns an empty string.
+ const char* message() const {
+ return message_.get() != NULL ? message_->c_str() : "";
+ }
+ // TODO(vladl at google.com): Remove this after making sure no clients use it.
+ // Deprecated; please use message() instead.
+ const char* failure_message() const { return message(); }
+
+ // Streams a custom failure message into this object.
+ template <typename T> AssertionResult& operator<<(const T& value) {
+ AppendMessage(Message() << value);
+ return *this;
+ }
+
+ // Allows streaming basic output manipulators such as endl or flush into
+ // this object.
+ AssertionResult& operator<<(
+ ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) {
+ AppendMessage(Message() << basic_manipulator);
+ return *this;
+ }
+
+ private:
+ // Appends the contents of message to message_.
+ void AppendMessage(const Message& a_message) {
+ if (message_.get() == NULL)
+ message_.reset(new ::std::string);
+ message_->append(a_message.GetString().c_str());
+ }
+
+ // Stores result of the assertion predicate.
+ bool success_;
+ // Stores the message describing the condition in case the expectation
+ // construct is not satisfied with the predicate's outcome.
+ // Referenced via a pointer to avoid taking too much stack frame space
+ // with test assertions.
+ internal::scoped_ptr< ::std::string> message_;
+
+ GTEST_DISALLOW_ASSIGN_(AssertionResult);
+};
+
+// Makes a successful assertion result.
+GTEST_API_ AssertionResult AssertionSuccess();
+
+// Makes a failed assertion result.
+GTEST_API_ AssertionResult AssertionFailure();
+
+// Makes a failed assertion result with the given failure message.
+// Deprecated; use AssertionFailure() << msg.
+GTEST_API_ AssertionResult AssertionFailure(const Message& msg);
+
+// The abstract class that all tests inherit from.
+//
+// In Google Test, a unit test program contains one or many TestCases, and
+// each TestCase contains one or many Tests.
+//
+// When you define a test using the TEST macro, you don't need to
+// explicitly derive from Test - the TEST macro automatically does
+// this for you.
+//
+// The only time you derive from Test is when defining a test fixture
+// to be used a TEST_F. For example:
+//
+// class FooTest : public testing::Test {
+// protected:
+// virtual void SetUp() { ... }
+// virtual void TearDown() { ... }
+// ...
+// };
+//
+// TEST_F(FooTest, Bar) { ... }
+// TEST_F(FooTest, Baz) { ... }
+//
+// Test is not copyable.
+class GTEST_API_ Test {
+ public:
+ friend class TestInfo;
+
+ // Defines types for pointers to functions that set up and tear down
+ // a test case.
+ typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc;
+ typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc;
+
+ // The d'tor is virtual as we intend to inherit from Test.
+ virtual ~Test();
+
+ // Sets up the stuff shared by all tests in this test case.
+ //
+ // Google Test will call Foo::SetUpTestCase() before running the first
+ // test in test case Foo. Hence a sub-class can define its own
+ // SetUpTestCase() method to shadow the one defined in the super
+ // class.
+ static void SetUpTestCase() {}
+
+ // Tears down the stuff shared by all tests in this test case.
+ //
+ // Google Test will call Foo::TearDownTestCase() after running the last
+ // test in test case Foo. Hence a sub-class can define its own
+ // TearDownTestCase() method to shadow the one defined in the super
+ // class.
+ static void TearDownTestCase() {}
+
+ // Returns true iff the current test has a fatal failure.
+ static bool HasFatalFailure();
+
+ // Returns true iff the current test has a non-fatal failure.
+ static bool HasNonfatalFailure();
+
+ // Returns true iff the current test has a (either fatal or
+ // non-fatal) failure.
+ static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); }
+
+ // Logs a property for the current test, test case, or for the entire
+ // invocation of the test program when used outside of the context of a
+ // test case. Only the last value for a given key is remembered. These
+ // are public static so they can be called from utility functions that are
+ // not members of the test fixture. Calls to RecordProperty made during
+ // lifespan of the test (from the moment its constructor starts to the
+ // moment its destructor finishes) will be output in XML as attributes of
+ // the <testcase> element. Properties recorded from fixture's
+ // SetUpTestCase or TearDownTestCase are logged as attributes of the
+ // corresponding <testsuite> element. Calls to RecordProperty made in the
+ // global context (before or after invocation of RUN_ALL_TESTS and from
+ // SetUp/TearDown method of Environment objects registered with Google
+ // Test) will be output as attributes of the <testsuites> element.
+ static void RecordProperty(const std::string& key, const std::string& value);
+ static void RecordProperty(const std::string& key, int value);
+
+ protected:
+ // Creates a Test object.
+ Test();
+
+ // Sets up the test fixture.
+ virtual void SetUp();
+
+ // Tears down the test fixture.
+ virtual void TearDown();
+
+ private:
+ // Returns true iff the current test has the same fixture class as
+ // the first test in the current test case.
+ static bool HasSameFixtureClass();
+
+ // Runs the test after the test fixture has been set up.
+ //
+ // A sub-class must implement this to define the test logic.
+ //
+ // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM.
+ // Instead, use the TEST or TEST_F macro.
+ virtual void TestBody() = 0;
+
+ // Sets up, executes, and tears down the test.
+ void Run();
+
+ // Deletes self. We deliberately pick an unusual name for this
+ // internal method to avoid clashing with names used in user TESTs.
+ void DeleteSelf_() { delete this; }
+
+ // Uses a GTestFlagSaver to save and restore all Google Test flags.
+ const internal::GTestFlagSaver* const gtest_flag_saver_;
+
+ // Often a user mis-spells SetUp() as Setup() and spends a long time
+ // wondering why it is never called by Google Test. The declaration of
+ // the following method is solely for catching such an error at
+ // compile time:
+ //
+ // - The return type is deliberately chosen to be not void, so it
+ // will be a conflict if a user declares void Setup() in his test
+ // fixture.
+ //
+ // - This method is private, so it will be another compiler error
+ // if a user calls it from his test fixture.
+ //
+ // DO NOT OVERRIDE THIS FUNCTION.
+ //
+ // If you see an error about overriding the following function or
+ // about it being private, you have mis-spelled SetUp() as Setup().
+ struct Setup_should_be_spelled_SetUp {};
+ virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; }
+
+ // We disallow copying Tests.
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(Test);
+};
+
+typedef internal::TimeInMillis TimeInMillis;
+
+// A copyable object representing a user specified test property which can be
+// output as a key/value string pair.
+//
+// Don't inherit from TestProperty as its destructor is not virtual.
+class TestProperty {
+ public:
+ // C'tor. TestProperty does NOT have a default constructor.
+ // Always use this constructor (with parameters) to create a
+ // TestProperty object.
+ TestProperty(const std::string& a_key, const std::string& a_value) :
+ key_(a_key), value_(a_value) {
+ }
+
+ // Gets the user supplied key.
+ const char* key() const {
+ return key_.c_str();
+ }
+
+ // Gets the user supplied value.
+ const char* value() const {
+ return value_.c_str();
+ }
+
+ // Sets a new value, overriding the one supplied in the constructor.
+ void SetValue(const std::string& new_value) {
+ value_ = new_value;
+ }
+
+ private:
+ // The key supplied by the user.
+ std::string key_;
+ // The value supplied by the user.
+ std::string value_;
+};
+
+// The result of a single Test. This includes a list of
+// TestPartResults, a list of TestProperties, a count of how many
+// death tests there are in the Test, and how much time it took to run
+// the Test.
+//
+// TestResult is not copyable.
+class GTEST_API_ TestResult {
+ public:
+ // Creates an empty TestResult.
+ TestResult();
+
+ // D'tor. Do not inherit from TestResult.
+ ~TestResult();
+
+ // Gets the number of all test parts. This is the sum of the number
+ // of successful test parts and the number of failed test parts.
+ int total_part_count() const;
+
+ // Returns the number of the test properties.
+ int test_property_count() const;
+
+ // Returns true iff the test passed (i.e. no test part failed).
+ bool Passed() const { return !Failed(); }
+
+ // Returns true iff the test failed.
+ bool Failed() const;
+
+ // Returns true iff the test fatally failed.
+ bool HasFatalFailure() const;
+
+ // Returns true iff the test has a non-fatal failure.
+ bool HasNonfatalFailure() const;
+
+ // Returns the elapsed time, in milliseconds.
+ TimeInMillis elapsed_time() const { return elapsed_time_; }
+
+ // Returns the i-th test part result among all the results. i can range
+ // from 0 to test_property_count() - 1. If i is not in that range, aborts
+ // the program.
+ const TestPartResult& GetTestPartResult(int i) const;
+
+ // Returns the i-th test property. i can range from 0 to
+ // test_property_count() - 1. If i is not in that range, aborts the
+ // program.
+ const TestProperty& GetTestProperty(int i) const;
+
+ private:
+ friend class TestInfo;
+ friend class TestCase;
+ friend class UnitTest;
+ friend class internal::DefaultGlobalTestPartResultReporter;
+ friend class internal::ExecDeathTest;
+ friend class internal::TestResultAccessor;
+ friend class internal::UnitTestImpl;
+ friend class internal::WindowsDeathTest;
+
+ // Gets the vector of TestPartResults.
+ const std::vector<TestPartResult>& test_part_results() const {
+ return test_part_results_;
+ }
+
+ // Gets the vector of TestProperties.
+ const std::vector<TestProperty>& test_properties() const {
+ return test_properties_;
+ }
+
+ // Sets the elapsed time.
+ void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; }
+
+ // Adds a test property to the list. The property is validated and may add
+ // a non-fatal failure if invalid (e.g., if it conflicts with reserved
+ // key names). If a property is already recorded for the same key, the
+ // value will be updated, rather than storing multiple values for the same
+ // key. xml_element specifies the element for which the property is being
+ // recorded and is used for validation.
+ void RecordProperty(const std::string& xml_element,
+ const TestProperty& test_property);
+
+ // Adds a failure if the key is a reserved attribute of Google Test
+ // testcase tags. Returns true if the property is valid.
+ // TODO(russr): Validate attribute names are legal and human readable.
+ static bool ValidateTestProperty(const std::string& xml_element,
+ const TestProperty& test_property);
+
+ // Adds a test part result to the list.
+ void AddTestPartResult(const TestPartResult& test_part_result);
+
+ // Returns the death test count.
+ int death_test_count() const { return death_test_count_; }
+
+ // Increments the death test count, returning the new count.
+ int increment_death_test_count() { return ++death_test_count_; }
+
+ // Clears the test part results.
+ void ClearTestPartResults();
+
+ // Clears the object.
+ void Clear();
+
+ // Protects mutable state of the property vector and of owned
+ // properties, whose values may be updated.
+ internal::Mutex test_properites_mutex_;
+
+ // The vector of TestPartResults
+ std::vector<TestPartResult> test_part_results_;
+ // The vector of TestProperties
+ std::vector<TestProperty> test_properties_;
+ // Running count of death tests.
+ int death_test_count_;
+ // The elapsed time, in milliseconds.
+ TimeInMillis elapsed_time_;
+
+ // We disallow copying TestResult.
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult);
+}; // class TestResult
+
+// A TestInfo object stores the following information about a test:
+//
+// Test case name
+// Test name
+// Whether the test should be run
+// A function pointer that creates the test object when invoked
+// Test result
+//
+// The constructor of TestInfo registers itself with the UnitTest
+// singleton such that the RUN_ALL_TESTS() macro knows which tests to
+// run.
+class GTEST_API_ TestInfo {
+ public:
+ // Destructs a TestInfo object. This function is not virtual, so
+ // don't inherit from TestInfo.
+ ~TestInfo();
+
+ // Returns the test case name.
+ const char* test_case_name() const { return test_case_name_.c_str(); }
+
+ // Returns the test name.
+ const char* name() const { return name_.c_str(); }
+
+ // Returns the name of the parameter type, or NULL if this is not a typed
+ // or a type-parameterized test.
+ const char* type_param() const {
+ if (type_param_.get() != NULL)
+ return type_param_->c_str();
+ return NULL;
+ }
+
+ // Returns the text representation of the value parameter, or NULL if this
+ // is not a value-parameterized test.
+ const char* value_param() const {
+ if (value_param_.get() != NULL)
+ return value_param_->c_str();
+ return NULL;
+ }
+
+ // Returns true if this test should run, that is if the test is not
+ // disabled (or it is disabled but the also_run_disabled_tests flag has
+ // been specified) and its full name matches the user-specified filter.
+ //
+ // Google Test allows the user to filter the tests by their full names.
+ // The full name of a test Bar in test case Foo is defined as
+ // "Foo.Bar". Only the tests that match the filter will run.
+ //
+ // A filter is a colon-separated list of glob (not regex) patterns,
+ // optionally followed by a '-' and a colon-separated list of
+ // negative patterns (tests to exclude). A test is run if it
+ // matches one of the positive patterns and does not match any of
+ // the negative patterns.
+ //
+ // For example, *A*:Foo.* is a filter that matches any string that
+ // contains the character 'A' or starts with "Foo.".
+ bool should_run() const { return should_run_; }
+
+ // Returns true iff this test will appear in the XML report.
+ bool is_reportable() const {
+ // For now, the XML report includes all tests matching the filter.
+ // In the future, we may trim tests that are excluded because of
+ // sharding.
+ return matches_filter_;
+ }
+
+ // Returns the result of the test.
+ const TestResult* result() const { return &result_; }
+
+ private:
+#if GTEST_HAS_DEATH_TEST
+ friend class internal::DefaultDeathTestFactory;
+#endif // GTEST_HAS_DEATH_TEST
+ friend class Test;
+ friend class TestCase;
+ friend class internal::UnitTestImpl;
+ friend class internal::StreamingListenerTest;
+ friend TestInfo* internal::MakeAndRegisterTestInfo(
+ const char* test_case_name,
+ const char* name,
+ const char* type_param,
+ const char* value_param,
+ internal::TypeId fixture_class_id,
+ Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc,
+ internal::TestFactoryBase* factory);
+
+ // Constructs a TestInfo object. The newly constructed instance assumes
+ // ownership of the factory object.
+ TestInfo(const std::string& test_case_name,
+ const std::string& name,
+ const char* a_type_param, // NULL if not a type-parameterized test
+ const char* a_value_param, // NULL if not a value-parameterized test
+ internal::TypeId fixture_class_id,
+ internal::TestFactoryBase* factory);
+
+ // Increments the number of death tests encountered in this test so
+ // far.
+ int increment_death_test_count() {
+ return result_.increment_death_test_count();
+ }
+
+ // Creates the test object, runs it, records its result, and then
+ // deletes it.
+ void Run();
+
+ static void ClearTestResult(TestInfo* test_info) {
+ test_info->result_.Clear();
+ }
+
+ // These fields are immutable properties of the test.
+ const std::string test_case_name_; // Test case name
+ const std::string name_; // Test name
+ // Name of the parameter type, or NULL if this is not a typed or a
+ // type-parameterized test.
+ const internal::scoped_ptr<const ::std::string> type_param_;
+ // Text representation of the value parameter, or NULL if this is not a
+ // value-parameterized test.
+ const internal::scoped_ptr<const ::std::string> value_param_;
+ const internal::TypeId fixture_class_id_; // ID of the test fixture class
+ bool should_run_; // True iff this test should run
+ bool is_disabled_; // True iff this test is disabled
+ bool matches_filter_; // True if this test matches the
+ // user-specified filter.
+ internal::TestFactoryBase* const factory_; // The factory that creates
+ // the test object
+
+ // This field is mutable and needs to be reset before running the
+ // test for the second time.
+ TestResult result_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo);
+};
+
+// A test case, which consists of a vector of TestInfos.
+//
+// TestCase is not copyable.
+class GTEST_API_ TestCase {
+ public:
+ // Creates a TestCase with the given name.
+ //
+ // TestCase does NOT have a default constructor. Always use this
+ // constructor to create a TestCase object.
+ //
+ // Arguments:
+ //
+ // name: name of the test case
+ // a_type_param: the name of the test's type parameter, or NULL if
+ // this is not a type-parameterized test.
+ // set_up_tc: pointer to the function that sets up the test case
+ // tear_down_tc: pointer to the function that tears down the test case
+ TestCase(const char* name, const char* a_type_param,
+ Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc);
+
+ // Destructor of TestCase.
+ virtual ~TestCase();
+
+ // Gets the name of the TestCase.
+ const char* name() const { return name_.c_str(); }
+
+ // Returns the name of the parameter type, or NULL if this is not a
+ // type-parameterized test case.
+ const char* type_param() const {
+ if (type_param_.get() != NULL)
+ return type_param_->c_str();
+ return NULL;
+ }
+
+ // Returns true if any test in this test case should run.
+ bool should_run() const { return should_run_; }
+
+ // Gets the number of successful tests in this test case.
+ int successful_test_count() const;
+
+ // Gets the number of failed tests in this test case.
+ int failed_test_count() const;
+
+ // Gets the number of disabled tests that will be reported in the XML report.
+ int reportable_disabled_test_count() const;
+
+ // Gets the number of disabled tests in this test case.
+ int disabled_test_count() const;
+
+ // Gets the number of tests to be printed in the XML report.
+ int reportable_test_count() const;
+
+ // Get the number of tests in this test case that should run.
+ int test_to_run_count() const;
+
+ // Gets the number of all tests in this test case.
+ int total_test_count() const;
+
+ // Returns true iff the test case passed.
+ bool Passed() const { return !Failed(); }
+
+ // Returns true iff the test case failed.
+ bool Failed() const { return failed_test_count() > 0; }
+
+ // Returns the elapsed time, in milliseconds.
+ TimeInMillis elapsed_time() const { return elapsed_time_; }
+
+ // Returns the i-th test among all the tests. i can range from 0 to
+ // total_test_count() - 1. If i is not in that range, returns NULL.
+ const TestInfo* GetTestInfo(int i) const;
+
+ // Returns the TestResult that holds test properties recorded during
+ // execution of SetUpTestCase and TearDownTestCase.
+ const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; }
+
+ private:
+ friend class Test;
+ friend class internal::UnitTestImpl;
+
+ // Gets the (mutable) vector of TestInfos in this TestCase.
+ std::vector<TestInfo*>& test_info_list() { return test_info_list_; }
+
+ // Gets the (immutable) vector of TestInfos in this TestCase.
+ const std::vector<TestInfo*>& test_info_list() const {
+ return test_info_list_;
+ }
+
+ // Returns the i-th test among all the tests. i can range from 0 to
+ // total_test_count() - 1. If i is not in that range, returns NULL.
+ TestInfo* GetMutableTestInfo(int i);
+
+ // Sets the should_run member.
+ void set_should_run(bool should) { should_run_ = should; }
+
+ // Adds a TestInfo to this test case. Will delete the TestInfo upon
+ // destruction of the TestCase object.
+ void AddTestInfo(TestInfo * test_info);
+
+ // Clears the results of all tests in this test case.
+ void ClearResult();
+
+ // Clears the results of all tests in the given test case.
+ static void ClearTestCaseResult(TestCase* test_case) {
+ test_case->ClearResult();
+ }
+
+ // Runs every test in this TestCase.
+ void Run();
+
+ // Runs SetUpTestCase() for this TestCase. This wrapper is needed
+ // for catching exceptions thrown from SetUpTestCase().
+ void RunSetUpTestCase() { (*set_up_tc_)(); }
+
+ // Runs TearDownTestCase() for this TestCase. This wrapper is
+ // needed for catching exceptions thrown from TearDownTestCase().
+ void RunTearDownTestCase() { (*tear_down_tc_)(); }
+
+ // Returns true iff test passed.
+ static bool TestPassed(const TestInfo* test_info) {
+ return test_info->should_run() && test_info->result()->Passed();
+ }
+
+ // Returns true iff test failed.
+ static bool TestFailed(const TestInfo* test_info) {
+ return test_info->should_run() && test_info->result()->Failed();
+ }
+
+ // Returns true iff the test is disabled and will be reported in the XML
+ // report.
+ static bool TestReportableDisabled(const TestInfo* test_info) {
+ return test_info->is_reportable() && test_info->is_disabled_;
+ }
+
+ // Returns true iff test is disabled.
+ static bool TestDisabled(const TestInfo* test_info) {
+ return test_info->is_disabled_;
+ }
+
+ // Returns true iff this test will appear in the XML report.
+ static bool TestReportable(const TestInfo* test_info) {
+ return test_info->is_reportable();
+ }
+
+ // Returns true if the given test should run.
+ static bool ShouldRunTest(const TestInfo* test_info) {
+ return test_info->should_run();
+ }
+
+ // Shuffles the tests in this test case.
+ void ShuffleTests(internal::Random* random);
+
+ // Restores the test order to before the first shuffle.
+ void UnshuffleTests();
+
+ // Name of the test case.
+ std::string name_;
+ // Name of the parameter type, or NULL if this is not a typed or a
+ // type-parameterized test.
+ const internal::scoped_ptr<const ::std::string> type_param_;
+ // The vector of TestInfos in their original order. It owns the
+ // elements in the vector.
+ std::vector<TestInfo*> test_info_list_;
+ // Provides a level of indirection for the test list to allow easy
+ // shuffling and restoring the test order. The i-th element in this
+ // vector is the index of the i-th test in the shuffled test list.
+ std::vector<int> test_indices_;
+ // Pointer to the function that sets up the test case.
+ Test::SetUpTestCaseFunc set_up_tc_;
+ // Pointer to the function that tears down the test case.
+ Test::TearDownTestCaseFunc tear_down_tc_;
+ // True iff any test in this test case should run.
+ bool should_run_;
+ // Elapsed time, in milliseconds.
+ TimeInMillis elapsed_time_;
+ // Holds test properties recorded during execution of SetUpTestCase and
+ // TearDownTestCase.
+ TestResult ad_hoc_test_result_;
+
+ // We disallow copying TestCases.
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase);
+};
+
+// An Environment object is capable of setting up and tearing down an
+// environment. The user should subclass this to define his own
+// environment(s).
+//
+// An Environment object does the set-up and tear-down in virtual
+// methods SetUp() and TearDown() instead of the constructor and the
+// destructor, as:
+//
+// 1. You cannot safely throw from a destructor. This is a problem
+// as in some cases Google Test is used where exceptions are enabled, and
+// we may want to implement ASSERT_* using exceptions where they are
+// available.
+// 2. You cannot use ASSERT_* directly in a constructor or
+// destructor.
+class Environment {
+ public:
+ // The d'tor is virtual as we need to subclass Environment.
+ virtual ~Environment() {}
+
+ // Override this to define how to set up the environment.
+ virtual void SetUp() {}
+
+ // Override this to define how to tear down the environment.
+ virtual void TearDown() {}
+ private:
+ // If you see an error about overriding the following function or
+ // about it being private, you have mis-spelled SetUp() as Setup().
+ struct Setup_should_be_spelled_SetUp {};
+ virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; }
+};
+
+// The interface for tracing execution of tests. The methods are organized in
+// the order the corresponding events are fired.
+class TestEventListener {
+ public:
+ virtual ~TestEventListener() {}
+
+ // Fired before any test activity starts.
+ virtual void OnTestProgramStart(const UnitTest& unit_test) = 0;
+
+ // Fired before each iteration of tests starts. There may be more than
+ // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration
+ // index, starting from 0.
+ virtual void OnTestIterationStart(const UnitTest& unit_test,
+ int iteration) = 0;
+
+ // Fired before environment set-up for each iteration of tests starts.
+ virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0;
+
+ // Fired after environment set-up for each iteration of tests ends.
+ virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0;
+
+ // Fired before the test case starts.
+ virtual void OnTestCaseStart(const TestCase& test_case) = 0;
+
+ // Fired before the test starts.
+ virtual void OnTestStart(const TestInfo& test_info) = 0;
+
+ // Fired after a failed assertion or a SUCCEED() invocation.
+ virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0;
+
+ // Fired after the test ends.
+ virtual void OnTestEnd(const TestInfo& test_info) = 0;
+
+ // Fired after the test case ends.
+ virtual void OnTestCaseEnd(const TestCase& test_case) = 0;
+
+ // Fired before environment tear-down for each iteration of tests starts.
+ virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0;
+
+ // Fired after environment tear-down for each iteration of tests ends.
+ virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0;
+
+ // Fired after each iteration of tests finishes.
+ virtual void OnTestIterationEnd(const UnitTest& unit_test,
+ int iteration) = 0;
+
+ // Fired after all test activities have ended.
+ virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0;
+};
+
+// The convenience class for users who need to override just one or two
+// methods and are not concerned that a possible change to a signature of
+// the methods they override will not be caught during the build. For
+// comments about each method please see the definition of TestEventListener
+// above.
+class EmptyTestEventListener : public TestEventListener {
+ public:
+ virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestIterationStart(const UnitTest& /*unit_test*/,
+ int /*iteration*/) {}
+ virtual void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) {}
+ virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestCaseStart(const TestCase& /*test_case*/) {}
+ virtual void OnTestStart(const TestInfo& /*test_info*/) {}
+ virtual void OnTestPartResult(const TestPartResult& /*test_part_result*/) {}
+ virtual void OnTestEnd(const TestInfo& /*test_info*/) {}
+ virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {}
+ virtual void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) {}
+ virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/,
+ int /*iteration*/) {}
+ virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {}
+};
+
+// TestEventListeners lets users add listeners to track events in Google Test.
+class GTEST_API_ TestEventListeners {
+ public:
+ TestEventListeners();
+ ~TestEventListeners();
+
+ // Appends an event listener to the end of the list. Google Test assumes
+ // the ownership of the listener (i.e. it will delete the listener when
+ // the test program finishes).
+ void Append(TestEventListener* listener);
+
+ // Removes the given event listener from the list and returns it. It then
+ // becomes the caller's responsibility to delete the listener. Returns
+ // NULL if the listener is not found in the list.
+ TestEventListener* Release(TestEventListener* listener);
+
+ // Returns the standard listener responsible for the default console
+ // output. Can be removed from the listeners list to shut down default
+ // console output. Note that removing this object from the listener list
+ // with Release transfers its ownership to the caller and makes this
+ // function return NULL the next time.
+ TestEventListener* default_result_printer() const {
+ return default_result_printer_;
+ }
+
+ // Returns the standard listener responsible for the default XML output
+ // controlled by the --gtest_output=xml flag. Can be removed from the
+ // listeners list by users who want to shut down the default XML output
+ // controlled by this flag and substitute it with custom one. Note that
+ // removing this object from the listener list with Release transfers its
+ // ownership to the caller and makes this function return NULL the next
+ // time.
+ TestEventListener* default_xml_generator() const {
+ return default_xml_generator_;
+ }
+
+ private:
+ friend class TestCase;
+ friend class TestInfo;
+ friend class internal::DefaultGlobalTestPartResultReporter;
+ friend class internal::NoExecDeathTest;
+ friend class internal::TestEventListenersAccessor;
+ friend class internal::UnitTestImpl;
+
+ // Returns repeater that broadcasts the TestEventListener events to all
+ // subscribers.
+ TestEventListener* repeater();
+
+ // Sets the default_result_printer attribute to the provided listener.
+ // The listener is also added to the listener list and previous
+ // default_result_printer is removed from it and deleted. The listener can
+ // also be NULL in which case it will not be added to the list. Does
+ // nothing if the previous and the current listener objects are the same.
+ void SetDefaultResultPrinter(TestEventListener* listener);
+
+ // Sets the default_xml_generator attribute to the provided listener. The
+ // listener is also added to the listener list and previous
+ // default_xml_generator is removed from it and deleted. The listener can
+ // also be NULL in which case it will not be added to the list. Does
+ // nothing if the previous and the current listener objects are the same.
+ void SetDefaultXmlGenerator(TestEventListener* listener);
+
+ // Controls whether events will be forwarded by the repeater to the
+ // listeners in the list.
+ bool EventForwardingEnabled() const;
+ void SuppressEventForwarding();
+
+ // The actual list of listeners.
+ internal::TestEventRepeater* repeater_;
+ // Listener responsible for the standard result output.
+ TestEventListener* default_result_printer_;
+ // Listener responsible for the creation of the XML output file.
+ TestEventListener* default_xml_generator_;
+
+ // We disallow copying TestEventListeners.
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners);
+};
+
+// A UnitTest consists of a vector of TestCases.
+//
+// This is a singleton class. The only instance of UnitTest is
+// created when UnitTest::GetInstance() is first called. This
+// instance is never deleted.
+//
+// UnitTest is not copyable.
+//
+// This class is thread-safe as long as the methods are called
+// according to their specification.
+class GTEST_API_ UnitTest {
+ public:
+ // Gets the singleton UnitTest object. The first time this method
+ // is called, a UnitTest object is constructed and returned.
+ // Consecutive calls will return the same object.
+ static UnitTest* GetInstance();
+
+ // Runs all tests in this UnitTest object and prints the result.
+ // Returns 0 if successful, or 1 otherwise.
+ //
+ // This method can only be called from the main thread.
+ //
+ // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+ int Run() GTEST_MUST_USE_RESULT_;
+
+ // Returns the working directory when the first TEST() or TEST_F()
+ // was executed. The UnitTest object owns the string.
+ const char* original_working_dir() const;
+
+ // Returns the TestCase object for the test that's currently running,
+ // or NULL if no test is running.
+ const TestCase* current_test_case() const
+ GTEST_LOCK_EXCLUDED_(mutex_);
+
+ // Returns the TestInfo object for the test that's currently running,
+ // or NULL if no test is running.
+ const TestInfo* current_test_info() const
+ GTEST_LOCK_EXCLUDED_(mutex_);
+
+ // Returns the random seed used at the start of the current test run.
+ int random_seed() const;
+
+#if GTEST_HAS_PARAM_TEST
+ // Returns the ParameterizedTestCaseRegistry object used to keep track of
+ // value-parameterized tests and instantiate and register them.
+ //
+ // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+ internal::ParameterizedTestCaseRegistry& parameterized_test_registry()
+ GTEST_LOCK_EXCLUDED_(mutex_);
+#endif // GTEST_HAS_PARAM_TEST
+
+ // Gets the number of successful test cases.
+ int successful_test_case_count() const;
+
+ // Gets the number of failed test cases.
+ int failed_test_case_count() const;
+
+ // Gets the number of all test cases.
+ int total_test_case_count() const;
+
+ // Gets the number of all test cases that contain at least one test
+ // that should run.
+ int test_case_to_run_count() const;
+
+ // Gets the number of successful tests.
+ int successful_test_count() const;
+
+ // Gets the number of failed tests.
+ int failed_test_count() const;
+
+ // Gets the number of disabled tests that will be reported in the XML report.
+ int reportable_disabled_test_count() const;
+
+ // Gets the number of disabled tests.
+ int disabled_test_count() const;
+
+ // Gets the number of tests to be printed in the XML report.
+ int reportable_test_count() const;
+
+ // Gets the number of all tests.
+ int total_test_count() const;
+
+ // Gets the number of tests that should run.
+ int test_to_run_count() const;
+
+ // Gets the time of the test program start, in ms from the start of the
+ // UNIX epoch.
+ TimeInMillis start_timestamp() const;
+
+ // Gets the elapsed time, in milliseconds.
+ TimeInMillis elapsed_time() const;
+
+ // Returns true iff the unit test passed (i.e. all test cases passed).
+ bool Passed() const;
+
+ // Returns true iff the unit test failed (i.e. some test case failed
+ // or something outside of all tests failed).
+ bool Failed() const;
+
+ // Gets the i-th test case among all the test cases. i can range from 0 to
+ // total_test_case_count() - 1. If i is not in that range, returns NULL.
+ const TestCase* GetTestCase(int i) const;
+
+ // Returns the TestResult containing information on test failures and
+ // properties logged outside of individual test cases.
+ const TestResult& ad_hoc_test_result() const;
+
+ // Returns the list of event listeners that can be used to track events
+ // inside Google Test.
+ TestEventListeners& listeners();
+
+ private:
+ // Registers and returns a global test environment. When a test
+ // program is run, all global test environments will be set-up in
+ // the order they were registered. After all tests in the program
+ // have finished, all global test environments will be torn-down in
+ // the *reverse* order they were registered.
+ //
+ // The UnitTest object takes ownership of the given environment.
+ //
+ // This method can only be called from the main thread.
+ Environment* AddEnvironment(Environment* env);
+
+ // Adds a TestPartResult to the current TestResult object. All
+ // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc)
+ // eventually call this to report their results. The user code
+ // should use the assertion macros instead of calling this directly.
+ void AddTestPartResult(TestPartResult::Type result_type,
+ const char* file_name,
+ int line_number,
+ const std::string& message,
+ const std::string& os_stack_trace)
+ GTEST_LOCK_EXCLUDED_(mutex_);
+
+ // Adds a TestProperty to the current TestResult object when invoked from
+ // inside a test, to current TestCase's ad_hoc_test_result_ when invoked
+ // from SetUpTestCase or TearDownTestCase, or to the global property set
+ // when invoked elsewhere. If the result already contains a property with
+ // the same key, the value will be updated.
+ void RecordProperty(const std::string& key, const std::string& value);
+
+ // Gets the i-th test case among all the test cases. i can range from 0 to
+ // total_test_case_count() - 1. If i is not in that range, returns NULL.
+ TestCase* GetMutableTestCase(int i);
+
+ // Accessors for the implementation object.
+ internal::UnitTestImpl* impl() { return impl_; }
+ const internal::UnitTestImpl* impl() const { return impl_; }
+
+ // These classes and funcions are friends as they need to access private
+ // members of UnitTest.
+ friend class Test;
+ friend class internal::AssertHelper;
+ friend class internal::ScopedTrace;
+ friend class internal::StreamingListenerTest;
+ friend class internal::UnitTestRecordPropertyTestHelper;
+ friend Environment* AddGlobalTestEnvironment(Environment* env);
+ friend internal::UnitTestImpl* internal::GetUnitTestImpl();
+ friend void internal::ReportFailureInUnknownLocation(
+ TestPartResult::Type result_type,
+ const std::string& message);
+
+ // Creates an empty UnitTest.
+ UnitTest();
+
+ // D'tor
+ virtual ~UnitTest();
+
+ // Pushes a trace defined by SCOPED_TRACE() on to the per-thread
+ // Google Test trace stack.
+ void PushGTestTrace(const internal::TraceInfo& trace)
+ GTEST_LOCK_EXCLUDED_(mutex_);
+
+ // Pops a trace from the per-thread Google Test trace stack.
+ void PopGTestTrace()
+ GTEST_LOCK_EXCLUDED_(mutex_);
+
+ // Protects mutable state in *impl_. This is mutable as some const
+ // methods need to lock it too.
+ mutable internal::Mutex mutex_;
+
+ // Opaque implementation object. This field is never changed once
+ // the object is constructed. We don't mark it as const here, as
+ // doing so will cause a warning in the constructor of UnitTest.
+ // Mutable state in *impl_ is protected by mutex_.
+ internal::UnitTestImpl* impl_;
+
+ // We disallow copying UnitTest.
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest);
+};
+
+// A convenient wrapper for adding an environment for the test
+// program.
+//
+// You should call this before RUN_ALL_TESTS() is called, probably in
+// main(). If you use gtest_main, you need to call this before main()
+// starts for it to take effect. For example, you can define a global
+// variable like this:
+//
+// testing::Environment* const foo_env =
+// testing::AddGlobalTestEnvironment(new FooEnvironment);
+//
+// However, we strongly recommend you to write your own main() and
+// call AddGlobalTestEnvironment() there, as relying on initialization
+// of global variables makes the code harder to read and may cause
+// problems when you register multiple environments from different
+// translation units and the environments have dependencies among them
+// (remember that the compiler doesn't guarantee the order in which
+// global variables from different translation units are initialized).
+inline Environment* AddGlobalTestEnvironment(Environment* env) {
+ return UnitTest::GetInstance()->AddEnvironment(env);
+}
+
+// Initializes Google Test. This must be called before calling
+// RUN_ALL_TESTS(). In particular, it parses a command line for the
+// flags that Google Test recognizes. Whenever a Google Test flag is
+// seen, it is removed from argv, and *argc is decremented.
+//
+// No value is returned. Instead, the Google Test flag variables are
+// updated.
+//
+// Calling the function for the second time has no user-visible effect.
+GTEST_API_ void InitGoogleTest(int* argc, char** argv);
+
+// This overloaded version can be used in Windows programs compiled in
+// UNICODE mode.
+GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv);
+
+namespace internal {
+
+// FormatForComparison<ToPrint, OtherOperand>::Format(value) formats a
+// value of type ToPrint that is an operand of a comparison assertion
+// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in
+// the comparison, and is used to help determine the best way to
+// format the value. In particular, when the value is a C string
+// (char pointer) and the other operand is an STL string object, we
+// want to format the C string as a string, since we know it is
+// compared by value with the string object. If the value is a char
+// pointer but the other operand is not an STL string object, we don't
+// know whether the pointer is supposed to point to a NUL-terminated
+// string, and thus want to print it as a pointer to be safe.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+
+// The default case.
+template <typename ToPrint, typename OtherOperand>
+class FormatForComparison {
+ public:
+ static ::std::string Format(const ToPrint& value) {
+ return ::testing::PrintToString(value);
+ }
+};
+
+// Array.
+template <typename ToPrint, size_t N, typename OtherOperand>
+class FormatForComparison<ToPrint[N], OtherOperand> {
+ public:
+ static ::std::string Format(const ToPrint* value) {
+ return FormatForComparison<const ToPrint*, OtherOperand>::Format(value);
+ }
+};
+
+// By default, print C string as pointers to be safe, as we don't know
+// whether they actually point to a NUL-terminated string.
+
+#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \
+ template <typename OtherOperand> \
+ class FormatForComparison<CharType*, OtherOperand> { \
+ public: \
+ static ::std::string Format(CharType* value) { \
+ return ::testing::PrintToString(static_cast<const void*>(value)); \
+ } \
+ }
+
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t);
+
+#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_
+
+// If a C string is compared with an STL string object, we know it's meant
+// to point to a NUL-terminated string, and thus can print it as a string.
+
+#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \
+ template <> \
+ class FormatForComparison<CharType*, OtherStringType> { \
+ public: \
+ static ::std::string Format(CharType* value) { \
+ return ::testing::PrintToString(value); \
+ } \
+ }
+
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string);
+
+#if GTEST_HAS_GLOBAL_STRING
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::string);
+#endif
+
+#if GTEST_HAS_GLOBAL_WSTRING
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::wstring);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::wstring);
+#endif
+
+#if GTEST_HAS_STD_WSTRING
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring);
+#endif
+
+#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_
+
+// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc)
+// operand to be used in a failure message. The type (but not value)
+// of the other operand may affect the format. This allows us to
+// print a char* as a raw pointer when it is compared against another
+// char* or void*, and print it as a C string when it is compared
+// against an std::string object, for example.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+template <typename T1, typename T2>
+std::string FormatForComparisonFailureMessage(
+ const T1& value, const T2& /* other_operand */) {
+ return FormatForComparison<T1, T2>::Format(value);
+}
+
+// The helper function for {ASSERT|EXPECT}_EQ.
+template <typename T1, typename T2>
+AssertionResult CmpHelperEQ(const char* expected_expression,
+ const char* actual_expression,
+ const T1& expected,
+ const T2& actual) {
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4389) // Temporarily disables warning on
+ // signed/unsigned mismatch.
+#endif
+
+ if (expected == actual) {
+ return AssertionSuccess();
+ }
+
+#ifdef _MSC_VER
+# pragma warning(pop) // Restores the warning state.
+#endif
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ FormatForComparisonFailureMessage(expected, actual),
+ FormatForComparisonFailureMessage(actual, expected),
+ false);
+}
+
+// With this overloaded version, we allow anonymous enums to be used
+// in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous enums
+// can be implicitly cast to BiggestInt.
+GTEST_API_ AssertionResult CmpHelperEQ(const char* expected_expression,
+ const char* actual_expression,
+ BiggestInt expected,
+ BiggestInt actual);
+
+// The helper class for {ASSERT|EXPECT}_EQ. The template argument
+// lhs_is_null_literal is true iff the first argument to ASSERT_EQ()
+// is a null pointer literal. The following default implementation is
+// for lhs_is_null_literal being false.
+template <bool lhs_is_null_literal>
+class EqHelper {
+ public:
+ // This templatized version is for the general case.
+ template <typename T1, typename T2>
+ static AssertionResult Compare(const char* expected_expression,
+ const char* actual_expression,
+ const T1& expected,
+ const T2& actual) {
+ return CmpHelperEQ(expected_expression, actual_expression, expected,
+ actual);
+ }
+
+ // With this overloaded version, we allow anonymous enums to be used
+ // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous
+ // enums can be implicitly cast to BiggestInt.
+ //
+ // Even though its body looks the same as the above version, we
+ // cannot merge the two, as it will make anonymous enums unhappy.
+ static AssertionResult Compare(const char* expected_expression,
+ const char* actual_expression,
+ BiggestInt expected,
+ BiggestInt actual) {
+ return CmpHelperEQ(expected_expression, actual_expression, expected,
+ actual);
+ }
+};
+
+// This specialization is used when the first argument to ASSERT_EQ()
+// is a null pointer literal, like NULL, false, or 0.
+template <>
+class EqHelper<true> {
+ public:
+ // We define two overloaded versions of Compare(). The first
+ // version will be picked when the second argument to ASSERT_EQ() is
+ // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or
+ // EXPECT_EQ(false, a_bool).
+ template <typename T1, typename T2>
+ static AssertionResult Compare(
+ const char* expected_expression,
+ const char* actual_expression,
+ const T1& expected,
+ const T2& actual,
+ // The following line prevents this overload from being considered if T2
+ // is not a pointer type. We need this because ASSERT_EQ(NULL, my_ptr)
+ // expands to Compare("", "", NULL, my_ptr), which requires a conversion
+ // to match the Secret* in the other overload, which would otherwise make
+ // this template match better.
+ typename EnableIf<!is_pointer<T2>::value>::type* = 0) {
+ return CmpHelperEQ(expected_expression, actual_expression, expected,
+ actual);
+ }
+
+ // This version will be picked when the second argument to ASSERT_EQ() is a
+ // pointer, e.g. ASSERT_EQ(NULL, a_pointer).
+ template <typename T>
+ static AssertionResult Compare(
+ const char* expected_expression,
+ const char* actual_expression,
+ // We used to have a second template parameter instead of Secret*. That
+ // template parameter would deduce to 'long', making this a better match
+ // than the first overload even without the first overload's EnableIf.
+ // Unfortunately, gcc with -Wconversion-null warns when "passing NULL to
+ // non-pointer argument" (even a deduced integral argument), so the old
+ // implementation caused warnings in user code.
+ Secret* /* expected (NULL) */,
+ T* actual) {
+ // We already know that 'expected' is a null pointer.
+ return CmpHelperEQ(expected_expression, actual_expression,
+ static_cast<T*>(NULL), actual);
+ }
+};
+
+// A macro for implementing the helper functions needed to implement
+// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste
+// of similar code.
+//
+// For each templatized helper function, we also define an overloaded
+// version for BiggestInt in order to reduce code bloat and allow
+// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled
+// with gcc 4.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+#define GTEST_IMPL_CMP_HELPER_(op_name, op)\
+template <typename T1, typename T2>\
+AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \
+ const T1& val1, const T2& val2) {\
+ if (val1 op val2) {\
+ return AssertionSuccess();\
+ } else {\
+ return AssertionFailure() \
+ << "Expected: (" << expr1 << ") " #op " (" << expr2\
+ << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\
+ << " vs " << FormatForComparisonFailureMessage(val2, val1);\
+ }\
+}\
+GTEST_API_ AssertionResult CmpHelper##op_name(\
+ const char* expr1, const char* expr2, BiggestInt val1, BiggestInt val2)
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+
+// Implements the helper function for {ASSERT|EXPECT}_NE
+GTEST_IMPL_CMP_HELPER_(NE, !=);
+// Implements the helper function for {ASSERT|EXPECT}_LE
+GTEST_IMPL_CMP_HELPER_(LE, <=);
+// Implements the helper function for {ASSERT|EXPECT}_LT
+GTEST_IMPL_CMP_HELPER_(LT, <);
+// Implements the helper function for {ASSERT|EXPECT}_GE
+GTEST_IMPL_CMP_HELPER_(GE, >=);
+// Implements the helper function for {ASSERT|EXPECT}_GT
+GTEST_IMPL_CMP_HELPER_(GT, >);
+
+#undef GTEST_IMPL_CMP_HELPER_
+
+// The helper function for {ASSERT|EXPECT}_STREQ.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression,
+ const char* actual_expression,
+ const char* expected,
+ const char* actual);
+
+// The helper function for {ASSERT|EXPECT}_STRCASEEQ.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression,
+ const char* actual_expression,
+ const char* expected,
+ const char* actual);
+
+// The helper function for {ASSERT|EXPECT}_STRNE.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression,
+ const char* s2_expression,
+ const char* s1,
+ const char* s2);
+
+// The helper function for {ASSERT|EXPECT}_STRCASENE.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression,
+ const char* s2_expression,
+ const char* s1,
+ const char* s2);
+
+
+// Helper function for *_STREQ on wide strings.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression,
+ const char* actual_expression,
+ const wchar_t* expected,
+ const wchar_t* actual);
+
+// Helper function for *_STRNE on wide strings.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression,
+ const char* s2_expression,
+ const wchar_t* s1,
+ const wchar_t* s2);
+
+} // namespace internal
+
+// IsSubstring() and IsNotSubstring() are intended to be used as the
+// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by
+// themselves. They check whether needle is a substring of haystack
+// (NULL is considered a substring of itself only), and return an
+// appropriate error message when they fail.
+//
+// The {needle,haystack}_expr arguments are the stringified
+// expressions that generated the two real arguments.
+GTEST_API_ AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const char* needle, const char* haystack);
+GTEST_API_ AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const wchar_t* needle, const wchar_t* haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const char* needle, const char* haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const wchar_t* needle, const wchar_t* haystack);
+GTEST_API_ AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::string& needle, const ::std::string& haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::string& needle, const ::std::string& haystack);
+
+#if GTEST_HAS_STD_WSTRING
+GTEST_API_ AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::wstring& needle, const ::std::wstring& haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::wstring& needle, const ::std::wstring& haystack);
+#endif // GTEST_HAS_STD_WSTRING
+
+namespace internal {
+
+// Helper template function for comparing floating-points.
+//
+// Template parameter:
+//
+// RawType: the raw floating-point type (either float or double)
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+template <typename RawType>
+AssertionResult CmpHelperFloatingPointEQ(const char* expected_expression,
+ const char* actual_expression,
+ RawType expected,
+ RawType actual) {
+ const FloatingPoint<RawType> lhs(expected), rhs(actual);
+
+ if (lhs.AlmostEquals(rhs)) {
+ return AssertionSuccess();
+ }
+
+ ::std::stringstream expected_ss;
+ expected_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+ << expected;
+
+ ::std::stringstream actual_ss;
+ actual_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+ << actual;
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ StringStreamToString(&expected_ss),
+ StringStreamToString(&actual_ss),
+ false);
+}
+
+// Helper function for implementing ASSERT_NEAR.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1,
+ const char* expr2,
+ const char* abs_error_expr,
+ double val1,
+ double val2,
+ double abs_error);
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+// A class that enables one to stream messages to assertion macros
+class GTEST_API_ AssertHelper {
+ public:
+ // Constructor.
+ AssertHelper(TestPartResult::Type type,
+ const char* file,
+ int line,
+ const char* message);
+ ~AssertHelper();
+
+ // Message assignment is a semantic trick to enable assertion
+ // streaming; see the GTEST_MESSAGE_ macro below.
+ void operator=(const Message& message) const;
+
+ private:
+ // We put our data in a struct so that the size of the AssertHelper class can
+ // be as small as possible. This is important because gcc is incapable of
+ // re-using stack space even for temporary variables, so every EXPECT_EQ
+ // reserves stack space for another AssertHelper.
+ struct AssertHelperData {
+ AssertHelperData(TestPartResult::Type t,
+ const char* srcfile,
+ int line_num,
+ const char* msg)
+ : type(t), file(srcfile), line(line_num), message(msg) { }
+
+ TestPartResult::Type const type;
+ const char* const file;
+ int const line;
+ std::string const message;
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData);
+ };
+
+ AssertHelperData* const data_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper);
+};
+
+} // namespace internal
+
+#if GTEST_HAS_PARAM_TEST
+// The pure interface class that all value-parameterized tests inherit from.
+// A value-parameterized class must inherit from both ::testing::Test and
+// ::testing::WithParamInterface. In most cases that just means inheriting
+// from ::testing::TestWithParam, but more complicated test hierarchies
+// may need to inherit from Test and WithParamInterface at different levels.
+//
+// This interface has support for accessing the test parameter value via
+// the GetParam() method.
+//
+// Use it with one of the parameter generator defining functions, like Range(),
+// Values(), ValuesIn(), Bool(), and Combine().
+//
+// class FooTest : public ::testing::TestWithParam<int> {
+// protected:
+// FooTest() {
+// // Can use GetParam() here.
+// }
+// virtual ~FooTest() {
+// // Can use GetParam() here.
+// }
+// virtual void SetUp() {
+// // Can use GetParam() here.
+// }
+// virtual void TearDown {
+// // Can use GetParam() here.
+// }
+// };
+// TEST_P(FooTest, DoesBar) {
+// // Can use GetParam() method here.
+// Foo foo;
+// ASSERT_TRUE(foo.DoesBar(GetParam()));
+// }
+// INSTANTIATE_TEST_CASE_P(OneToTenRange, FooTest, ::testing::Range(1, 10));
+
+template <typename T>
+class WithParamInterface {
+ public:
+ typedef T ParamType;
+ virtual ~WithParamInterface() {}
+
+ // The current parameter value. Is also available in the test fixture's
+ // constructor. This member function is non-static, even though it only
+ // references static data, to reduce the opportunity for incorrect uses
+ // like writing 'WithParamInterface<bool>::GetParam()' for a test that
+ // uses a fixture whose parameter type is int.
+ const ParamType& GetParam() const {
+ GTEST_CHECK_(parameter_ != NULL)
+ << "GetParam() can only be called inside a value-parameterized test "
+ << "-- did you intend to write TEST_P instead of TEST_F?";
+ return *parameter_;
+ }
+
+ private:
+ // Sets parameter value. The caller is responsible for making sure the value
+ // remains alive and unchanged throughout the current test.
+ static void SetParam(const ParamType* parameter) {
+ parameter_ = parameter;
+ }
+
+ // Static value used for accessing parameter during a test lifetime.
+ static const ParamType* parameter_;
+
+ // TestClass must be a subclass of WithParamInterface<T> and Test.
+ template <class TestClass> friend class internal::ParameterizedTestFactory;
+};
+
+template <typename T>
+const T* WithParamInterface<T>::parameter_ = NULL;
+
+// Most value-parameterized classes can ignore the existence of
+// WithParamInterface, and can just inherit from ::testing::TestWithParam.
+
+template <typename T>
+class TestWithParam : public Test, public WithParamInterface<T> {
+};
+
+#endif // GTEST_HAS_PARAM_TEST
+
+// Macros for indicating success/failure in test code.
+
+// ADD_FAILURE unconditionally adds a failure to the current test.
+// SUCCEED generates a success - it doesn't automatically make the
+// current test successful, as a test is only successful when it has
+// no failure.
+//
+// EXPECT_* verifies that a certain condition is satisfied. If not,
+// it behaves like ADD_FAILURE. In particular:
+//
+// EXPECT_TRUE verifies that a Boolean condition is true.
+// EXPECT_FALSE verifies that a Boolean condition is false.
+//
+// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except
+// that they will also abort the current function on failure. People
+// usually want the fail-fast behavior of FAIL and ASSERT_*, but those
+// writing data-driven tests often find themselves using ADD_FAILURE
+// and EXPECT_* more.
+
+// Generates a nonfatal failure with a generic message.
+#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed")
+
+// Generates a nonfatal failure at the given source file location with
+// a generic message.
+#define ADD_FAILURE_AT(file, line) \
+ GTEST_MESSAGE_AT_(file, line, "Failed", \
+ ::testing::TestPartResult::kNonFatalFailure)
+
+// Generates a fatal failure with a generic message.
+#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed")
+
+// Define this macro to 1 to omit the definition of FAIL(), which is a
+// generic name and clashes with some other libraries.
+#if !GTEST_DONT_DEFINE_FAIL
+# define FAIL() GTEST_FAIL()
+#endif
+
+// Generates a success with a generic message.
+#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded")
+
+// Define this macro to 1 to omit the definition of SUCCEED(), which
+// is a generic name and clashes with some other libraries.
+#if !GTEST_DONT_DEFINE_SUCCEED
+# define SUCCEED() GTEST_SUCCEED()
+#endif
+
+// Macros for testing exceptions.
+//
+// * {ASSERT|EXPECT}_THROW(statement, expected_exception):
+// Tests that the statement throws the expected exception.
+// * {ASSERT|EXPECT}_NO_THROW(statement):
+// Tests that the statement doesn't throw any exception.
+// * {ASSERT|EXPECT}_ANY_THROW(statement):
+// Tests that the statement throws an exception.
+
+#define EXPECT_THROW(statement, expected_exception) \
+ GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_NO_THROW(statement) \
+ GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_ANY_THROW(statement) \
+ GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_THROW(statement, expected_exception) \
+ GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_)
+#define ASSERT_NO_THROW(statement) \
+ GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_)
+#define ASSERT_ANY_THROW(statement) \
+ GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_)
+
+// Boolean assertions. Condition can be either a Boolean expression or an
+// AssertionResult. For more information on how to use AssertionResult with
+// these macros see comments on that class.
+#define EXPECT_TRUE(condition) \
+ GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
+ GTEST_NONFATAL_FAILURE_)
+#define EXPECT_FALSE(condition) \
+ GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
+ GTEST_NONFATAL_FAILURE_)
+#define ASSERT_TRUE(condition) \
+ GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
+ GTEST_FATAL_FAILURE_)
+#define ASSERT_FALSE(condition) \
+ GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
+ GTEST_FATAL_FAILURE_)
+
+// Includes the auto-generated header that implements a family of
+// generic predicate assertion macros.
+#include "gtest/gtest_pred_impl.h"
+
+// Macros for testing equalities and inequalities.
+//
+// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual
+// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2
+// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2
+// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2
+// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2
+// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2
+//
+// When they are not, Google Test prints both the tested expressions and
+// their actual values. The values must be compatible built-in types,
+// or you will get a compiler error. By "compatible" we mean that the
+// values can be compared by the respective operator.
+//
+// Note:
+//
+// 1. It is possible to make a user-defined type work with
+// {ASSERT|EXPECT}_??(), but that requires overloading the
+// comparison operators and is thus discouraged by the Google C++
+// Usage Guide. Therefore, you are advised to use the
+// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are
+// equal.
+//
+// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on
+// pointers (in particular, C strings). Therefore, if you use it
+// with two C strings, you are testing how their locations in memory
+// are related, not how their content is related. To compare two C
+// strings by content, use {ASSERT|EXPECT}_STR*().
+//
+// 3. {ASSERT|EXPECT}_EQ(expected, actual) is preferred to
+// {ASSERT|EXPECT}_TRUE(expected == actual), as the former tells you
+// what the actual value is when it fails, and similarly for the
+// other comparisons.
+//
+// 4. Do not depend on the order in which {ASSERT|EXPECT}_??()
+// evaluate their arguments, which is undefined.
+//
+// 5. These macros evaluate their arguments exactly once.
+//
+// Examples:
+//
+// EXPECT_NE(5, Foo());
+// EXPECT_EQ(NULL, a_pointer);
+// ASSERT_LT(i, array_size);
+// ASSERT_GT(records.size(), 0) << "There is no record left.";
+
+#define EXPECT_EQ(expected, actual) \
+ EXPECT_PRED_FORMAT2(::testing::internal:: \
+ EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \
+ expected, actual)
+#define EXPECT_NE(expected, actual) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual)
+#define EXPECT_LE(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)
+#define EXPECT_LT(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2)
+#define EXPECT_GE(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2)
+#define EXPECT_GT(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2)
+
+#define GTEST_ASSERT_EQ(expected, actual) \
+ ASSERT_PRED_FORMAT2(::testing::internal:: \
+ EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \
+ expected, actual)
+#define GTEST_ASSERT_NE(val1, val2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2)
+#define GTEST_ASSERT_LE(val1, val2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)
+#define GTEST_ASSERT_LT(val1, val2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2)
+#define GTEST_ASSERT_GE(val1, val2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2)
+#define GTEST_ASSERT_GT(val1, val2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2)
+
+// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of
+// ASSERT_XY(), which clashes with some users' own code.
+
+#if !GTEST_DONT_DEFINE_ASSERT_EQ
+# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_NE
+# define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_LE
+# define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_LT
+# define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_GE
+# define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_GT
+# define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2)
+#endif
+
+// C-string Comparisons. All tests treat NULL and any non-NULL string
+// as different. Two NULLs are equal.
+//
+// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2
+// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2
+// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case
+// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case
+//
+// For wide or narrow string objects, you can use the
+// {ASSERT|EXPECT}_??() macros.
+//
+// Don't depend on the order in which the arguments are evaluated,
+// which is undefined.
+//
+// These macros evaluate their arguments exactly once.
+
+#define EXPECT_STREQ(expected, actual) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual)
+#define EXPECT_STRNE(s1, s2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2)
+#define EXPECT_STRCASEEQ(expected, actual) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual)
+#define EXPECT_STRCASENE(s1, s2)\
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2)
+
+#define ASSERT_STREQ(expected, actual) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual)
+#define ASSERT_STRNE(s1, s2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2)
+#define ASSERT_STRCASEEQ(expected, actual) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual)
+#define ASSERT_STRCASENE(s1, s2)\
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2)
+
+// Macros for comparing floating-point numbers.
+//
+// * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual):
+// Tests that two float values are almost equal.
+// * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual):
+// Tests that two double values are almost equal.
+// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error):
+// Tests that v1 and v2 are within the given distance to each other.
+//
+// Google Test uses ULP-based comparison to automatically pick a default
+// error bound that is appropriate for the operands. See the
+// FloatingPoint template class in gtest-internal.h if you are
+// interested in the implementation details.
+
+#define EXPECT_FLOAT_EQ(expected, actual)\
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<float>, \
+ expected, actual)
+
+#define EXPECT_DOUBLE_EQ(expected, actual)\
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<double>, \
+ expected, actual)
+
+#define ASSERT_FLOAT_EQ(expected, actual)\
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<float>, \
+ expected, actual)
+
+#define ASSERT_DOUBLE_EQ(expected, actual)\
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<double>, \
+ expected, actual)
+
+#define EXPECT_NEAR(val1, val2, abs_error)\
+ EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \
+ val1, val2, abs_error)
+
+#define ASSERT_NEAR(val1, val2, abs_error)\
+ ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \
+ val1, val2, abs_error)
+
+// These predicate format functions work on floating-point values, and
+// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g.
+//
+// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0);
+
+// Asserts that val1 is less than, or almost equal to, val2. Fails
+// otherwise. In particular, it fails if either val1 or val2 is NaN.
+GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2,
+ float val1, float val2);
+GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2,
+ double val1, double val2);
+
+
+#if GTEST_OS_WINDOWS
+
+// Macros that test for HRESULT failure and success, these are only useful
+// on Windows, and rely on Windows SDK macros and APIs to compile.
+//
+// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr)
+//
+// When expr unexpectedly fails or succeeds, Google Test prints the
+// expected result and the actual result with both a human-readable
+// string representation of the error, if available, as well as the
+// hex result code.
+# define EXPECT_HRESULT_SUCCEEDED(expr) \
+ EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr))
+
+# define ASSERT_HRESULT_SUCCEEDED(expr) \
+ ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr))
+
+# define EXPECT_HRESULT_FAILED(expr) \
+ EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr))
+
+# define ASSERT_HRESULT_FAILED(expr) \
+ ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr))
+
+#endif // GTEST_OS_WINDOWS
+
+// Macros that execute statement and check that it doesn't generate new fatal
+// failures in the current thread.
+//
+// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement);
+//
+// Examples:
+//
+// EXPECT_NO_FATAL_FAILURE(Process());
+// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed";
+//
+#define ASSERT_NO_FATAL_FAILURE(statement) \
+ GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_)
+#define EXPECT_NO_FATAL_FAILURE(statement) \
+ GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_)
+
+// Causes a trace (including the source file path, the current line
+// number, and the given message) to be included in every test failure
+// message generated by code in the current scope. The effect is
+// undone when the control leaves the current scope.
+//
+// The message argument can be anything streamable to std::ostream.
+//
+// In the implementation, we include the current line number as part
+// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s
+// to appear in the same block - as long as they are on different
+// lines.
+#define SCOPED_TRACE(message) \
+ ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\
+ __FILE__, __LINE__, ::testing::Message() << (message))
+
+// Compile-time assertion for type equality.
+// StaticAssertTypeEq<type1, type2>() compiles iff type1 and type2 are
+// the same type. The value it returns is not interesting.
+//
+// Instead of making StaticAssertTypeEq a class template, we make it a
+// function template that invokes a helper class template. This
+// prevents a user from misusing StaticAssertTypeEq<T1, T2> by
+// defining objects of that type.
+//
+// CAVEAT:
+//
+// When used inside a method of a class template,
+// StaticAssertTypeEq<T1, T2>() is effective ONLY IF the method is
+// instantiated. For example, given:
+//
+// template <typename T> class Foo {
+// public:
+// void Bar() { testing::StaticAssertTypeEq<int, T>(); }
+// };
+//
+// the code:
+//
+// void Test1() { Foo<bool> foo; }
+//
+// will NOT generate a compiler error, as Foo<bool>::Bar() is never
+// actually instantiated. Instead, you need:
+//
+// void Test2() { Foo<bool> foo; foo.Bar(); }
+//
+// to cause a compiler error.
+template <typename T1, typename T2>
+bool StaticAssertTypeEq() {
+ (void)internal::StaticAssertTypeEqHelper<T1, T2>();
+ return true;
+}
+
+// Defines a test.
+//
+// The first parameter is the name of the test case, and the second
+// parameter is the name of the test within the test case.
+//
+// The convention is to end the test case name with "Test". For
+// example, a test case for the Foo class can be named FooTest.
+//
+// The user should put his test code between braces after using this
+// macro. Example:
+//
+// TEST(FooTest, InitializesCorrectly) {
+// Foo foo;
+// EXPECT_TRUE(foo.StatusIsOK());
+// }
+
+// Note that we call GetTestTypeId() instead of GetTypeId<
+// ::testing::Test>() here to get the type ID of testing::Test. This
+// is to work around a suspected linker bug when using Google Test as
+// a framework on Mac OS X. The bug causes GetTypeId<
+// ::testing::Test>() to return different values depending on whether
+// the call is from the Google Test framework itself or from user test
+// code. GetTestTypeId() is guaranteed to always return the same
+// value, as it always calls GetTypeId<>() from the Google Test
+// framework.
+#define GTEST_TEST(test_case_name, test_name)\
+ GTEST_TEST_(test_case_name, test_name, \
+ ::testing::Test, ::testing::internal::GetTestTypeId())
+
+// Define this macro to 1 to omit the definition of TEST(), which
+// is a generic name and clashes with some other libraries.
+#if !GTEST_DONT_DEFINE_TEST
+# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name)
+#endif
+
+// Defines a test that uses a test fixture.
+//
+// The first parameter is the name of the test fixture class, which
+// also doubles as the test case name. The second parameter is the
+// name of the test within the test case.
+//
+// A test fixture class must be declared earlier. The user should put
+// his test code between braces after using this macro. Example:
+//
+// class FooTest : public testing::Test {
+// protected:
+// virtual void SetUp() { b_.AddElement(3); }
+//
+// Foo a_;
+// Foo b_;
+// };
+//
+// TEST_F(FooTest, InitializesCorrectly) {
+// EXPECT_TRUE(a_.StatusIsOK());
+// }
+//
+// TEST_F(FooTest, ReturnsElementCountCorrectly) {
+// EXPECT_EQ(0, a_.size());
+// EXPECT_EQ(1, b_.size());
+// }
+
+#define TEST_F(test_fixture, test_name)\
+ GTEST_TEST_(test_fixture, test_name, test_fixture, \
+ ::testing::internal::GetTypeId<test_fixture>())
+
+} // namespace testing
+
+// Use this function in main() to run all tests. It returns 0 if all
+// tests are successful, or 1 otherwise.
+//
+// RUN_ALL_TESTS() should be invoked after the command line has been
+// parsed by InitGoogleTest().
+//
+// This function was formerly a macro; thus, it is in the global
+// namespace and has an all-caps name.
+int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_;
+
+inline int RUN_ALL_TESTS() {
+ return ::testing::UnitTest::GetInstance()->Run();
+}
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_H_
diff --git a/test/gtest/include/gtest/gtest_pred_impl.h b/test/gtest/include/gtest/gtest_pred_impl.h
new file mode 100644
index 0000000..30ae712
--- /dev/null
+++ b/test/gtest/include/gtest/gtest_pred_impl.h
@@ -0,0 +1,358 @@
+// Copyright 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command
+// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND!
+//
+// Implements a family of generic predicate assertion macros.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
+#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
+
+// Makes sure this header is not included before gtest.h.
+#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
+# error Do not include gtest_pred_impl.h directly. Include gtest.h instead.
+#endif // GTEST_INCLUDE_GTEST_GTEST_H_
+
+// This header implements a family of generic predicate assertion
+// macros:
+//
+// ASSERT_PRED_FORMAT1(pred_format, v1)
+// ASSERT_PRED_FORMAT2(pred_format, v1, v2)
+// ...
+//
+// where pred_format is a function or functor that takes n (in the
+// case of ASSERT_PRED_FORMATn) values and their source expression
+// text, and returns a testing::AssertionResult. See the definition
+// of ASSERT_EQ in gtest.h for an example.
+//
+// If you don't care about formatting, you can use the more
+// restrictive version:
+//
+// ASSERT_PRED1(pred, v1)
+// ASSERT_PRED2(pred, v1, v2)
+// ...
+//
+// where pred is an n-ary function or functor that returns bool,
+// and the values v1, v2, ..., must support the << operator for
+// streaming to std::ostream.
+//
+// We also define the EXPECT_* variations.
+//
+// For now we only support predicates whose arity is at most 5.
+// Please email googletestframework at googlegroups.com if you need
+// support for higher arities.
+
+// GTEST_ASSERT_ is the basic statement to which all of the assertions
+// in this file reduce. Don't use this in your code.
+
+#define GTEST_ASSERT_(expression, on_failure) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (const ::testing::AssertionResult gtest_ar = (expression)) \
+ ; \
+ else \
+ on_failure(gtest_ar.failure_message())
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use
+// this in your code.
+template <typename Pred,
+ typename T1>
+AssertionResult AssertPred1Helper(const char* pred_text,
+ const char* e1,
+ Pred pred,
+ const T1& v1) {
+ if (pred(v1)) return AssertionSuccess();
+
+ return AssertionFailure() << pred_text << "("
+ << e1 << ") evaluates to false, where"
+ << "\n" << e1 << " evaluates to " << v1;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\
+ GTEST_ASSERT_(pred_format(#v1, v1), \
+ on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use
+// this in your code.
+#define GTEST_PRED1_(pred, v1, on_failure)\
+ GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \
+ #v1, \
+ pred, \
+ v1), on_failure)
+
+// Unary predicate assertion macros.
+#define EXPECT_PRED_FORMAT1(pred_format, v1) \
+ GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED1(pred, v1) \
+ GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT1(pred_format, v1) \
+ GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED1(pred, v1) \
+ GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use
+// this in your code.
+template <typename Pred,
+ typename T1,
+ typename T2>
+AssertionResult AssertPred2Helper(const char* pred_text,
+ const char* e1,
+ const char* e2,
+ Pred pred,
+ const T1& v1,
+ const T2& v2) {
+ if (pred(v1, v2)) return AssertionSuccess();
+
+ return AssertionFailure() << pred_text << "("
+ << e1 << ", "
+ << e2 << ") evaluates to false, where"
+ << "\n" << e1 << " evaluates to " << v1
+ << "\n" << e2 << " evaluates to " << v2;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\
+ GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \
+ on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use
+// this in your code.
+#define GTEST_PRED2_(pred, v1, v2, on_failure)\
+ GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \
+ #v1, \
+ #v2, \
+ pred, \
+ v1, \
+ v2), on_failure)
+
+// Binary predicate assertion macros.
+#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \
+ GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED2(pred, v1, v2) \
+ GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \
+ GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED2(pred, v1, v2) \
+ GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use
+// this in your code.
+template <typename Pred,
+ typename T1,
+ typename T2,
+ typename T3>
+AssertionResult AssertPred3Helper(const char* pred_text,
+ const char* e1,
+ const char* e2,
+ const char* e3,
+ Pred pred,
+ const T1& v1,
+ const T2& v2,
+ const T3& v3) {
+ if (pred(v1, v2, v3)) return AssertionSuccess();
+
+ return AssertionFailure() << pred_text << "("
+ << e1 << ", "
+ << e2 << ", "
+ << e3 << ") evaluates to false, where"
+ << "\n" << e1 << " evaluates to " << v1
+ << "\n" << e2 << " evaluates to " << v2
+ << "\n" << e3 << " evaluates to " << v3;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\
+ GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \
+ on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use
+// this in your code.
+#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\
+ GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \
+ #v1, \
+ #v2, \
+ #v3, \
+ pred, \
+ v1, \
+ v2, \
+ v3), on_failure)
+
+// Ternary predicate assertion macros.
+#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \
+ GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED3(pred, v1, v2, v3) \
+ GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \
+ GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED3(pred, v1, v2, v3) \
+ GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use
+// this in your code.
+template <typename Pred,
+ typename T1,
+ typename T2,
+ typename T3,
+ typename T4>
+AssertionResult AssertPred4Helper(const char* pred_text,
+ const char* e1,
+ const char* e2,
+ const char* e3,
+ const char* e4,
+ Pred pred,
+ const T1& v1,
+ const T2& v2,
+ const T3& v3,
+ const T4& v4) {
+ if (pred(v1, v2, v3, v4)) return AssertionSuccess();
+
+ return AssertionFailure() << pred_text << "("
+ << e1 << ", "
+ << e2 << ", "
+ << e3 << ", "
+ << e4 << ") evaluates to false, where"
+ << "\n" << e1 << " evaluates to " << v1
+ << "\n" << e2 << " evaluates to " << v2
+ << "\n" << e3 << " evaluates to " << v3
+ << "\n" << e4 << " evaluates to " << v4;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\
+ GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \
+ on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use
+// this in your code.
+#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\
+ GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \
+ #v1, \
+ #v2, \
+ #v3, \
+ #v4, \
+ pred, \
+ v1, \
+ v2, \
+ v3, \
+ v4), on_failure)
+
+// 4-ary predicate assertion macros.
+#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
+ GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED4(pred, v1, v2, v3, v4) \
+ GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
+ GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED4(pred, v1, v2, v3, v4) \
+ GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use
+// this in your code.
+template <typename Pred,
+ typename T1,
+ typename T2,
+ typename T3,
+ typename T4,
+ typename T5>
+AssertionResult AssertPred5Helper(const char* pred_text,
+ const char* e1,
+ const char* e2,
+ const char* e3,
+ const char* e4,
+ const char* e5,
+ Pred pred,
+ const T1& v1,
+ const T2& v2,
+ const T3& v3,
+ const T4& v4,
+ const T5& v5) {
+ if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess();
+
+ return AssertionFailure() << pred_text << "("
+ << e1 << ", "
+ << e2 << ", "
+ << e3 << ", "
+ << e4 << ", "
+ << e5 << ") evaluates to false, where"
+ << "\n" << e1 << " evaluates to " << v1
+ << "\n" << e2 << " evaluates to " << v2
+ << "\n" << e3 << " evaluates to " << v3
+ << "\n" << e4 << " evaluates to " << v4
+ << "\n" << e5 << " evaluates to " << v5;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\
+ GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \
+ on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use
+// this in your code.
+#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\
+ GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \
+ #v1, \
+ #v2, \
+ #v3, \
+ #v4, \
+ #v5, \
+ pred, \
+ v1, \
+ v2, \
+ v3, \
+ v4, \
+ v5), on_failure)
+
+// 5-ary predicate assertion macros.
+#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
+ GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \
+ GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
+ GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \
+ GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
+
+
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
diff --git a/test/gtest/include/gtest/gtest_prod.h b/test/gtest/include/gtest/gtest_prod.h
new file mode 100644
index 0000000..da80ddc
--- /dev/null
+++ b/test/gtest/include/gtest/gtest_prod.h
@@ -0,0 +1,58 @@
+// Copyright 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+//
+// Google C++ Testing Framework definitions useful in production code.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_
+#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_
+
+// When you need to test the private or protected members of a class,
+// use the FRIEND_TEST macro to declare your tests as friends of the
+// class. For example:
+//
+// class MyClass {
+// private:
+// void MyMethod();
+// FRIEND_TEST(MyClassTest, MyMethod);
+// };
+//
+// class MyClassTest : public testing::Test {
+// // ...
+// };
+//
+// TEST_F(MyClassTest, MyMethod) {
+// // Can call MyClass::MyMethod() here.
+// }
+
+#define FRIEND_TEST(test_case_name, test_name)\
+friend class test_case_name##_##test_name##_Test
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_
diff --git a/test/gtest/include/gtest/internal/gtest-death-test-internal.h b/test/gtest/include/gtest/internal/gtest-death-test-internal.h
new file mode 100644
index 0000000..2b3a78f
--- /dev/null
+++ b/test/gtest/include/gtest/internal/gtest-death-test-internal.h
@@ -0,0 +1,319 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: wan at google.com (Zhanyong Wan), eefacm at gmail.com (Sean Mcafee)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file defines internal utilities needed for implementing
+// death tests. They are subject to change without notice.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
+
+#include "gtest/internal/gtest-internal.h"
+
+#include <stdio.h>
+
+namespace testing {
+namespace internal {
+
+GTEST_DECLARE_string_(internal_run_death_test);
+
+// Names of the flags (needed for parsing Google Test flags).
+const char kDeathTestStyleFlag[] = "death_test_style";
+const char kDeathTestUseFork[] = "death_test_use_fork";
+const char kInternalRunDeathTestFlag[] = "internal_run_death_test";
+
+#if GTEST_HAS_DEATH_TEST
+
+// DeathTest is a class that hides much of the complexity of the
+// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method
+// returns a concrete class that depends on the prevailing death test
+// style, as defined by the --gtest_death_test_style and/or
+// --gtest_internal_run_death_test flags.
+
+// In describing the results of death tests, these terms are used with
+// the corresponding definitions:
+//
+// exit status: The integer exit information in the format specified
+// by wait(2)
+// exit code: The integer code passed to exit(3), _exit(2), or
+// returned from main()
+class GTEST_API_ DeathTest {
+ public:
+ // Create returns false if there was an error determining the
+ // appropriate action to take for the current death test; for example,
+ // if the gtest_death_test_style flag is set to an invalid value.
+ // The LastMessage method will return a more detailed message in that
+ // case. Otherwise, the DeathTest pointer pointed to by the "test"
+ // argument is set. If the death test should be skipped, the pointer
+ // is set to NULL; otherwise, it is set to the address of a new concrete
+ // DeathTest object that controls the execution of the current test.
+ static bool Create(const char* statement, const RE* regex,
+ const char* file, int line, DeathTest** test);
+ DeathTest();
+ virtual ~DeathTest() { }
+
+ // A helper class that aborts a death test when it's deleted.
+ class ReturnSentinel {
+ public:
+ explicit ReturnSentinel(DeathTest* test) : test_(test) { }
+ ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); }
+ private:
+ DeathTest* const test_;
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel);
+ } GTEST_ATTRIBUTE_UNUSED_;
+
+ // An enumeration of possible roles that may be taken when a death
+ // test is encountered. EXECUTE means that the death test logic should
+ // be executed immediately. OVERSEE means that the program should prepare
+ // the appropriate environment for a child process to execute the death
+ // test, then wait for it to complete.
+ enum TestRole { OVERSEE_TEST, EXECUTE_TEST };
+
+ // An enumeration of the three reasons that a test might be aborted.
+ enum AbortReason {
+ TEST_ENCOUNTERED_RETURN_STATEMENT,
+ TEST_THREW_EXCEPTION,
+ TEST_DID_NOT_DIE
+ };
+
+ // Assumes one of the above roles.
+ virtual TestRole AssumeRole() = 0;
+
+ // Waits for the death test to finish and returns its status.
+ virtual int Wait() = 0;
+
+ // Returns true if the death test passed; that is, the test process
+ // exited during the test, its exit status matches a user-supplied
+ // predicate, and its stderr output matches a user-supplied regular
+ // expression.
+ // The user-supplied predicate may be a macro expression rather
+ // than a function pointer or functor, or else Wait and Passed could
+ // be combined.
+ virtual bool Passed(bool exit_status_ok) = 0;
+
+ // Signals that the death test did not die as expected.
+ virtual void Abort(AbortReason reason) = 0;
+
+ // Returns a human-readable outcome message regarding the outcome of
+ // the last death test.
+ static const char* LastMessage();
+
+ static void set_last_death_test_message(const std::string& message);
+
+ private:
+ // A string containing a description of the outcome of the last death test.
+ static std::string last_death_test_message_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest);
+};
+
+// Factory interface for death tests. May be mocked out for testing.
+class DeathTestFactory {
+ public:
+ virtual ~DeathTestFactory() { }
+ virtual bool Create(const char* statement, const RE* regex,
+ const char* file, int line, DeathTest** test) = 0;
+};
+
+// A concrete DeathTestFactory implementation for normal use.
+class DefaultDeathTestFactory : public DeathTestFactory {
+ public:
+ virtual bool Create(const char* statement, const RE* regex,
+ const char* file, int line, DeathTest** test);
+};
+
+// Returns true if exit_status describes a process that was terminated
+// by a signal, or exited normally with a nonzero exit code.
+GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
+
+// Traps C++ exceptions escaping statement and reports them as test
+// failures. Note that trapping SEH exceptions is not implemented here.
+# if GTEST_HAS_EXCEPTIONS
+# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
+ try { \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ } catch (const ::std::exception& gtest_exception) { \
+ fprintf(\
+ stderr, \
+ "\n%s: Caught std::exception-derived exception escaping the " \
+ "death test statement. Exception message: %s\n", \
+ ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \
+ gtest_exception.what()); \
+ fflush(stderr); \
+ death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
+ } catch (...) { \
+ death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
+ }
+
+# else
+# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
+
+# endif
+
+// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*,
+// ASSERT_EXIT*, and EXPECT_EXIT*.
+# define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ const ::testing::internal::RE& gtest_regex = (regex); \
+ ::testing::internal::DeathTest* gtest_dt; \
+ if (!::testing::internal::DeathTest::Create(#statement, >est_regex, \
+ __FILE__, __LINE__, >est_dt)) { \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
+ } \
+ if (gtest_dt != NULL) { \
+ ::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \
+ gtest_dt_ptr(gtest_dt); \
+ switch (gtest_dt->AssumeRole()) { \
+ case ::testing::internal::DeathTest::OVERSEE_TEST: \
+ if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
+ } \
+ break; \
+ case ::testing::internal::DeathTest::EXECUTE_TEST: { \
+ ::testing::internal::DeathTest::ReturnSentinel \
+ gtest_sentinel(gtest_dt); \
+ GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \
+ gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \
+ break; \
+ } \
+ default: \
+ break; \
+ } \
+ } \
+ } else \
+ GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__): \
+ fail(::testing::internal::DeathTest::LastMessage())
+// The symbol "fail" here expands to something into which a message
+// can be streamed.
+
+// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in
+// NDEBUG mode. In this case we need the statements to be executed, the regex is
+// ignored, and the macro must accept a streamed message even though the message
+// is never printed.
+# define GTEST_EXECUTE_STATEMENT_(statement, regex) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ } else \
+ ::testing::Message()
+
+// A class representing the parsed contents of the
+// --gtest_internal_run_death_test flag, as it existed when
+// RUN_ALL_TESTS was called.
+class InternalRunDeathTestFlag {
+ public:
+ InternalRunDeathTestFlag(const std::string& a_file,
+ int a_line,
+ int an_index,
+ int a_write_fd)
+ : file_(a_file), line_(a_line), index_(an_index),
+ write_fd_(a_write_fd) {}
+
+ ~InternalRunDeathTestFlag() {
+ if (write_fd_ >= 0)
+ posix::Close(write_fd_);
+ }
+
+ const std::string& file() const { return file_; }
+ int line() const { return line_; }
+ int index() const { return index_; }
+ int write_fd() const { return write_fd_; }
+
+ private:
+ std::string file_;
+ int line_;
+ int index_;
+ int write_fd_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag);
+};
+
+// Returns a newly created InternalRunDeathTestFlag object with fields
+// initialized from the GTEST_FLAG(internal_run_death_test) flag if
+// the flag is specified; otherwise returns NULL.
+InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag();
+
+#else // GTEST_HAS_DEATH_TEST
+
+// This macro is used for implementing macros such as
+// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where
+// death tests are not supported. Those macros must compile on such systems
+// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on
+// systems that support death tests. This allows one to write such a macro
+// on a system that does not support death tests and be sure that it will
+// compile on a death-test supporting system.
+//
+// Parameters:
+// statement - A statement that a macro such as EXPECT_DEATH would test
+// for program termination. This macro has to make sure this
+// statement is compiled but not executed, to ensure that
+// EXPECT_DEATH_IF_SUPPORTED compiles with a certain
+// parameter iff EXPECT_DEATH compiles with it.
+// regex - A regex that a macro such as EXPECT_DEATH would use to test
+// the output of statement. This parameter has to be
+// compiled but not evaluated by this macro, to ensure that
+// this macro only accepts expressions that a macro such as
+// EXPECT_DEATH would accept.
+// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED
+// and a return statement for ASSERT_DEATH_IF_SUPPORTED.
+// This ensures that ASSERT_DEATH_IF_SUPPORTED will not
+// compile inside functions where ASSERT_DEATH doesn't
+// compile.
+//
+// The branch that has an always false condition is used to ensure that
+// statement and regex are compiled (and thus syntactically correct) but
+// never executed. The unreachable code macro protects the terminator
+// statement from generating an 'unreachable code' warning in case
+// statement unconditionally returns or throws. The Message constructor at
+// the end allows the syntax of streaming additional messages into the
+// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH.
+# define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ GTEST_LOG_(WARNING) \
+ << "Death tests are not supported on this platform.\n" \
+ << "Statement '" #statement "' cannot be verified."; \
+ } else if (::testing::internal::AlwaysFalse()) { \
+ ::testing::internal::RE::PartialMatch(".*", (regex)); \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ terminator; \
+ } else \
+ ::testing::Message()
+
+#endif // GTEST_HAS_DEATH_TEST
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
diff --git a/test/gtest/include/gtest/internal/gtest-filepath.h b/test/gtest/include/gtest/internal/gtest-filepath.h
new file mode 100644
index 0000000..7a13b4b
--- /dev/null
+++ b/test/gtest/include/gtest/internal/gtest-filepath.h
@@ -0,0 +1,206 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: keith.ray at gmail.com (Keith Ray)
+//
+// Google Test filepath utilities
+//
+// This header file declares classes and functions used internally by
+// Google Test. They are subject to change without notice.
+//
+// This file is #included in <gtest/internal/gtest-internal.h>.
+// Do not include this header file separately!
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
+
+#include "gtest/internal/gtest-string.h"
+
+namespace testing {
+namespace internal {
+
+// FilePath - a class for file and directory pathname manipulation which
+// handles platform-specific conventions (like the pathname separator).
+// Used for helper functions for naming files in a directory for xml output.
+// Except for Set methods, all methods are const or static, which provides an
+// "immutable value object" -- useful for peace of mind.
+// A FilePath with a value ending in a path separator ("like/this/") represents
+// a directory, otherwise it is assumed to represent a file. In either case,
+// it may or may not represent an actual file or directory in the file system.
+// Names are NOT checked for syntax correctness -- no checking for illegal
+// characters, malformed paths, etc.
+
+class GTEST_API_ FilePath {
+ public:
+ FilePath() : pathname_("") { }
+ FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { }
+
+ explicit FilePath(const std::string& pathname) : pathname_(pathname) {
+ Normalize();
+ }
+
+ FilePath& operator=(const FilePath& rhs) {
+ Set(rhs);
+ return *this;
+ }
+
+ void Set(const FilePath& rhs) {
+ pathname_ = rhs.pathname_;
+ }
+
+ const std::string& string() const { return pathname_; }
+ const char* c_str() const { return pathname_.c_str(); }
+
+ // Returns the current working directory, or "" if unsuccessful.
+ static FilePath GetCurrentDir();
+
+ // Given directory = "dir", base_name = "test", number = 0,
+ // extension = "xml", returns "dir/test.xml". If number is greater
+ // than zero (e.g., 12), returns "dir/test_12.xml".
+ // On Windows platform, uses \ as the separator rather than /.
+ static FilePath MakeFileName(const FilePath& directory,
+ const FilePath& base_name,
+ int number,
+ const char* extension);
+
+ // Given directory = "dir", relative_path = "test.xml",
+ // returns "dir/test.xml".
+ // On Windows, uses \ as the separator rather than /.
+ static FilePath ConcatPaths(const FilePath& directory,
+ const FilePath& relative_path);
+
+ // Returns a pathname for a file that does not currently exist. The pathname
+ // will be directory/base_name.extension or
+ // directory/base_name_<number>.extension if directory/base_name.extension
+ // already exists. The number will be incremented until a pathname is found
+ // that does not already exist.
+ // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
+ // There could be a race condition if two or more processes are calling this
+ // function at the same time -- they could both pick the same filename.
+ static FilePath GenerateUniqueFileName(const FilePath& directory,
+ const FilePath& base_name,
+ const char* extension);
+
+ // Returns true iff the path is "".
+ bool IsEmpty() const { return pathname_.empty(); }
+
+ // If input name has a trailing separator character, removes it and returns
+ // the name, otherwise return the name string unmodified.
+ // On Windows platform, uses \ as the separator, other platforms use /.
+ FilePath RemoveTrailingPathSeparator() const;
+
+ // Returns a copy of the FilePath with the directory part removed.
+ // Example: FilePath("path/to/file").RemoveDirectoryName() returns
+ // FilePath("file"). If there is no directory part ("just_a_file"), it returns
+ // the FilePath unmodified. If there is no file part ("just_a_dir/") it
+ // returns an empty FilePath ("").
+ // On Windows platform, '\' is the path separator, otherwise it is '/'.
+ FilePath RemoveDirectoryName() const;
+
+ // RemoveFileName returns the directory path with the filename removed.
+ // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
+ // If the FilePath is "a_file" or "/a_file", RemoveFileName returns
+ // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
+ // not have a file, like "just/a/dir/", it returns the FilePath unmodified.
+ // On Windows platform, '\' is the path separator, otherwise it is '/'.
+ FilePath RemoveFileName() const;
+
+ // Returns a copy of the FilePath with the case-insensitive extension removed.
+ // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
+ // FilePath("dir/file"). If a case-insensitive extension is not
+ // found, returns a copy of the original FilePath.
+ FilePath RemoveExtension(const char* extension) const;
+
+ // Creates directories so that path exists. Returns true if successful or if
+ // the directories already exist; returns false if unable to create
+ // directories for any reason. Will also return false if the FilePath does
+ // not represent a directory (that is, it doesn't end with a path separator).
+ bool CreateDirectoriesRecursively() const;
+
+ // Create the directory so that path exists. Returns true if successful or
+ // if the directory already exists; returns false if unable to create the
+ // directory for any reason, including if the parent directory does not
+ // exist. Not named "CreateDirectory" because that's a macro on Windows.
+ bool CreateFolder() const;
+
+ // Returns true if FilePath describes something in the file-system,
+ // either a file, directory, or whatever, and that something exists.
+ bool FileOrDirectoryExists() const;
+
+ // Returns true if pathname describes a directory in the file-system
+ // that exists.
+ bool DirectoryExists() const;
+
+ // Returns true if FilePath ends with a path separator, which indicates that
+ // it is intended to represent a directory. Returns false otherwise.
+ // This does NOT check that a directory (or file) actually exists.
+ bool IsDirectory() const;
+
+ // Returns true if pathname describes a root directory. (Windows has one
+ // root directory per disk drive.)
+ bool IsRootDirectory() const;
+
+ // Returns true if pathname describes an absolute path.
+ bool IsAbsolutePath() const;
+
+ private:
+ // Replaces multiple consecutive separators with a single separator.
+ // For example, "bar///foo" becomes "bar/foo". Does not eliminate other
+ // redundancies that might be in a pathname involving "." or "..".
+ //
+ // A pathname with multiple consecutive separators may occur either through
+ // user error or as a result of some scripts or APIs that generate a pathname
+ // with a trailing separator. On other platforms the same API or script
+ // may NOT generate a pathname with a trailing "/". Then elsewhere that
+ // pathname may have another "/" and pathname components added to it,
+ // without checking for the separator already being there.
+ // The script language and operating system may allow paths like "foo//bar"
+ // but some of the functions in FilePath will not handle that correctly. In
+ // particular, RemoveTrailingPathSeparator() only removes one separator, and
+ // it is called in CreateDirectoriesRecursively() assuming that it will change
+ // a pathname from directory syntax (trailing separator) to filename syntax.
+ //
+ // On Windows this method also replaces the alternate path separator '/' with
+ // the primary path separator '\\', so that for example "bar\\/\\foo" becomes
+ // "bar\\foo".
+
+ void Normalize();
+
+ // Returns a pointer to the last occurence of a valid path separator in
+ // the FilePath. On Windows, for example, both '/' and '\' are valid path
+ // separators. Returns NULL if no path separator was found.
+ const char* FindLastPathSeparator() const;
+
+ std::string pathname_;
+}; // class FilePath
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
diff --git a/test/gtest/include/gtest/internal/gtest-internal.h b/test/gtest/include/gtest/internal/gtest-internal.h
new file mode 100644
index 0000000..0dcc3a3
--- /dev/null
+++ b/test/gtest/include/gtest/internal/gtest-internal.h
@@ -0,0 +1,1158 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: wan at google.com (Zhanyong Wan), eefacm at gmail.com (Sean Mcafee)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file declares functions and macros used internally by
+// Google Test. They are subject to change without notice.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
+
+#include "gtest/internal/gtest-port.h"
+
+#if GTEST_OS_LINUX
+# include <stdlib.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <unistd.h>
+#endif // GTEST_OS_LINUX
+
+#if GTEST_HAS_EXCEPTIONS
+# include <stdexcept>
+#endif
+
+#include <ctype.h>
+#include <float.h>
+#include <string.h>
+#include <iomanip>
+#include <limits>
+#include <set>
+
+#include "gtest/gtest-message.h"
+#include "gtest/internal/gtest-string.h"
+#include "gtest/internal/gtest-filepath.h"
+#include "gtest/internal/gtest-type-util.h"
+
+// Due to C++ preprocessor weirdness, we need double indirection to
+// concatenate two tokens when one of them is __LINE__. Writing
+//
+// foo ## __LINE__
+//
+// will result in the token foo__LINE__, instead of foo followed by
+// the current line number. For more details, see
+// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6
+#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar)
+#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar
+
+class ProtocolMessage;
+namespace proto2 { class Message; }
+
+namespace testing {
+
+// Forward declarations.
+
+class AssertionResult; // Result of an assertion.
+class Message; // Represents a failure message.
+class Test; // Represents a test.
+class TestInfo; // Information about a test.
+class TestPartResult; // Result of a test part.
+class UnitTest; // A collection of test cases.
+
+template <typename T>
+::std::string PrintToString(const T& value);
+
+namespace internal {
+
+struct TraceInfo; // Information about a trace point.
+class ScopedTrace; // Implements scoped trace.
+class TestInfoImpl; // Opaque implementation of TestInfo
+class UnitTestImpl; // Opaque implementation of UnitTest
+
+// How many times InitGoogleTest() has been called.
+GTEST_API_ extern int g_init_gtest_count;
+
+// The text used in failure messages to indicate the start of the
+// stack trace.
+GTEST_API_ extern const char kStackTraceMarker[];
+
+// Two overloaded helpers for checking at compile time whether an
+// expression is a null pointer literal (i.e. NULL or any 0-valued
+// compile-time integral constant). Their return values have
+// different sizes, so we can use sizeof() to test which version is
+// picked by the compiler. These helpers have no implementations, as
+// we only need their signatures.
+//
+// Given IsNullLiteralHelper(x), the compiler will pick the first
+// version if x can be implicitly converted to Secret*, and pick the
+// second version otherwise. Since Secret is a secret and incomplete
+// type, the only expression a user can write that has type Secret* is
+// a null pointer literal. Therefore, we know that x is a null
+// pointer literal if and only if the first version is picked by the
+// compiler.
+char IsNullLiteralHelper(Secret* p);
+char (&IsNullLiteralHelper(...))[2]; // NOLINT
+
+// A compile-time bool constant that is true if and only if x is a
+// null pointer literal (i.e. NULL or any 0-valued compile-time
+// integral constant).
+#ifdef GTEST_ELLIPSIS_NEEDS_POD_
+// We lose support for NULL detection where the compiler doesn't like
+// passing non-POD classes through ellipsis (...).
+# define GTEST_IS_NULL_LITERAL_(x) false
+#else
+# define GTEST_IS_NULL_LITERAL_(x) \
+ (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1)
+#endif // GTEST_ELLIPSIS_NEEDS_POD_
+
+// Appends the user-supplied message to the Google-Test-generated message.
+GTEST_API_ std::string AppendUserMessage(
+ const std::string& gtest_msg, const Message& user_msg);
+
+#if GTEST_HAS_EXCEPTIONS
+
+// This exception is thrown by (and only by) a failed Google Test
+// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions
+// are enabled). We derive it from std::runtime_error, which is for
+// errors presumably detectable only at run time. Since
+// std::runtime_error inherits from std::exception, many testing
+// frameworks know how to extract and print the message inside it.
+class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error {
+ public:
+ explicit GoogleTestFailureException(const TestPartResult& failure);
+};
+
+#endif // GTEST_HAS_EXCEPTIONS
+
+// A helper class for creating scoped traces in user programs.
+class GTEST_API_ ScopedTrace {
+ public:
+ // The c'tor pushes the given source file location and message onto
+ // a trace stack maintained by Google Test.
+ ScopedTrace(const char* file, int line, const Message& message);
+
+ // The d'tor pops the info pushed by the c'tor.
+ //
+ // Note that the d'tor is not virtual in order to be efficient.
+ // Don't inherit from ScopedTrace!
+ ~ScopedTrace();
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace);
+} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its
+ // c'tor and d'tor. Therefore it doesn't
+ // need to be used otherwise.
+
+// Constructs and returns the message for an equality assertion
+// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure.
+//
+// The first four parameters are the expressions used in the assertion
+// and their values, as strings. For example, for ASSERT_EQ(foo, bar)
+// where foo is 5 and bar is 6, we have:
+//
+// expected_expression: "foo"
+// actual_expression: "bar"
+// expected_value: "5"
+// actual_value: "6"
+//
+// The ignoring_case parameter is true iff the assertion is a
+// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will
+// be inserted into the message.
+GTEST_API_ AssertionResult EqFailure(const char* expected_expression,
+ const char* actual_expression,
+ const std::string& expected_value,
+ const std::string& actual_value,
+ bool ignoring_case);
+
+// Constructs a failure message for Boolean assertions such as EXPECT_TRUE.
+GTEST_API_ std::string GetBoolAssertionFailureMessage(
+ const AssertionResult& assertion_result,
+ const char* expression_text,
+ const char* actual_predicate_value,
+ const char* expected_predicate_value);
+
+// This template class represents an IEEE floating-point number
+// (either single-precision or double-precision, depending on the
+// template parameters).
+//
+// The purpose of this class is to do more sophisticated number
+// comparison. (Due to round-off error, etc, it's very unlikely that
+// two floating-points will be equal exactly. Hence a naive
+// comparison by the == operation often doesn't work.)
+//
+// Format of IEEE floating-point:
+//
+// The most-significant bit being the leftmost, an IEEE
+// floating-point looks like
+//
+// sign_bit exponent_bits fraction_bits
+//
+// Here, sign_bit is a single bit that designates the sign of the
+// number.
+//
+// For float, there are 8 exponent bits and 23 fraction bits.
+//
+// For double, there are 11 exponent bits and 52 fraction bits.
+//
+// More details can be found at
+// http://en.wikipedia.org/wiki/IEEE_floating-point_standard.
+//
+// Template parameter:
+//
+// RawType: the raw floating-point type (either float or double)
+template <typename RawType>
+class FloatingPoint {
+ public:
+ // Defines the unsigned integer type that has the same size as the
+ // floating point number.
+ typedef typename TypeWithSize<sizeof(RawType)>::UInt Bits;
+
+ // Constants.
+
+ // # of bits in a number.
+ static const size_t kBitCount = 8*sizeof(RawType);
+
+ // # of fraction bits in a number.
+ static const size_t kFractionBitCount =
+ std::numeric_limits<RawType>::digits - 1;
+
+ // # of exponent bits in a number.
+ static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount;
+
+ // The mask for the sign bit.
+ static const Bits kSignBitMask = static_cast<Bits>(1) << (kBitCount - 1);
+
+ // The mask for the fraction bits.
+ static const Bits kFractionBitMask =
+ ~static_cast<Bits>(0) >> (kExponentBitCount + 1);
+
+ // The mask for the exponent bits.
+ static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask);
+
+ // How many ULP's (Units in the Last Place) we want to tolerate when
+ // comparing two numbers. The larger the value, the more error we
+ // allow. A 0 value means that two numbers must be exactly the same
+ // to be considered equal.
+ //
+ // The maximum error of a single floating-point operation is 0.5
+ // units in the last place. On Intel CPU's, all floating-point
+ // calculations are done with 80-bit precision, while double has 64
+ // bits. Therefore, 4 should be enough for ordinary use.
+ //
+ // See the following article for more details on ULP:
+ // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
+ static const size_t kMaxUlps = 4;
+
+ // Constructs a FloatingPoint from a raw floating-point number.
+ //
+ // On an Intel CPU, passing a non-normalized NAN (Not a Number)
+ // around may change its bits, although the new value is guaranteed
+ // to be also a NAN. Therefore, don't expect this constructor to
+ // preserve the bits in x when x is a NAN.
+ explicit FloatingPoint(const RawType& x) { u_.value_ = x; }
+
+ // Static methods
+
+ // Reinterprets a bit pattern as a floating-point number.
+ //
+ // This function is needed to test the AlmostEquals() method.
+ static RawType ReinterpretBits(const Bits bits) {
+ FloatingPoint fp(0);
+ fp.u_.bits_ = bits;
+ return fp.u_.value_;
+ }
+
+ // Returns the floating-point number that represent positive infinity.
+ static RawType Infinity() {
+ return ReinterpretBits(kExponentBitMask);
+ }
+
+ // Returns the maximum representable finite floating-point number.
+ static RawType Max();
+
+ // Non-static methods
+
+ // Returns the bits that represents this number.
+ const Bits &bits() const { return u_.bits_; }
+
+ // Returns the exponent bits of this number.
+ Bits exponent_bits() const { return kExponentBitMask & u_.bits_; }
+
+ // Returns the fraction bits of this number.
+ Bits fraction_bits() const { return kFractionBitMask & u_.bits_; }
+
+ // Returns the sign bit of this number.
+ Bits sign_bit() const { return kSignBitMask & u_.bits_; }
+
+ // Returns true iff this is NAN (not a number).
+ bool is_nan() const {
+ // It's a NAN if the exponent bits are all ones and the fraction
+ // bits are not entirely zeros.
+ return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0);
+ }
+
+ // Returns true iff this number is at most kMaxUlps ULP's away from
+ // rhs. In particular, this function:
+ //
+ // - returns false if either number is (or both are) NAN.
+ // - treats really large numbers as almost equal to infinity.
+ // - thinks +0.0 and -0.0 are 0 DLP's apart.
+ bool AlmostEquals(const FloatingPoint& rhs) const {
+ // The IEEE standard says that any comparison operation involving
+ // a NAN must return false.
+ if (is_nan() || rhs.is_nan()) return false;
+
+ return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_)
+ <= kMaxUlps;
+ }
+
+ private:
+ // The data type used to store the actual floating-point number.
+ union FloatingPointUnion {
+ RawType value_; // The raw floating-point number.
+ Bits bits_; // The bits that represent the number.
+ };
+
+ // Converts an integer from the sign-and-magnitude representation to
+ // the biased representation. More precisely, let N be 2 to the
+ // power of (kBitCount - 1), an integer x is represented by the
+ // unsigned number x + N.
+ //
+ // For instance,
+ //
+ // -N + 1 (the most negative number representable using
+ // sign-and-magnitude) is represented by 1;
+ // 0 is represented by N; and
+ // N - 1 (the biggest number representable using
+ // sign-and-magnitude) is represented by 2N - 1.
+ //
+ // Read http://en.wikipedia.org/wiki/Signed_number_representations
+ // for more details on signed number representations.
+ static Bits SignAndMagnitudeToBiased(const Bits &sam) {
+ if (kSignBitMask & sam) {
+ // sam represents a negative number.
+ return ~sam + 1;
+ } else {
+ // sam represents a positive number.
+ return kSignBitMask | sam;
+ }
+ }
+
+ // Given two numbers in the sign-and-magnitude representation,
+ // returns the distance between them as an unsigned number.
+ static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1,
+ const Bits &sam2) {
+ const Bits biased1 = SignAndMagnitudeToBiased(sam1);
+ const Bits biased2 = SignAndMagnitudeToBiased(sam2);
+ return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1);
+ }
+
+ FloatingPointUnion u_;
+};
+
+// We cannot use std::numeric_limits<T>::max() as it clashes with the max()
+// macro defined by <windows.h>.
+template <>
+inline float FloatingPoint<float>::Max() { return FLT_MAX; }
+template <>
+inline double FloatingPoint<double>::Max() { return DBL_MAX; }
+
+// Typedefs the instances of the FloatingPoint template class that we
+// care to use.
+typedef FloatingPoint<float> Float;
+typedef FloatingPoint<double> Double;
+
+// In order to catch the mistake of putting tests that use different
+// test fixture classes in the same test case, we need to assign
+// unique IDs to fixture classes and compare them. The TypeId type is
+// used to hold such IDs. The user should treat TypeId as an opaque
+// type: the only operation allowed on TypeId values is to compare
+// them for equality using the == operator.
+typedef const void* TypeId;
+
+template <typename T>
+class TypeIdHelper {
+ public:
+ // dummy_ must not have a const type. Otherwise an overly eager
+ // compiler (e.g. MSVC 7.1 & 8.0) may try to merge
+ // TypeIdHelper<T>::dummy_ for different Ts as an "optimization".
+ static bool dummy_;
+};
+
+template <typename T>
+bool TypeIdHelper<T>::dummy_ = false;
+
+// GetTypeId<T>() returns the ID of type T. Different values will be
+// returned for different types. Calling the function twice with the
+// same type argument is guaranteed to return the same ID.
+template <typename T>
+TypeId GetTypeId() {
+ // The compiler is required to allocate a different
+ // TypeIdHelper<T>::dummy_ variable for each T used to instantiate
+ // the template. Therefore, the address of dummy_ is guaranteed to
+ // be unique.
+ return &(TypeIdHelper<T>::dummy_);
+}
+
+// Returns the type ID of ::testing::Test. Always call this instead
+// of GetTypeId< ::testing::Test>() to get the type ID of
+// ::testing::Test, as the latter may give the wrong result due to a
+// suspected linker bug when compiling Google Test as a Mac OS X
+// framework.
+GTEST_API_ TypeId GetTestTypeId();
+
+// Defines the abstract factory interface that creates instances
+// of a Test object.
+class TestFactoryBase {
+ public:
+ virtual ~TestFactoryBase() {}
+
+ // Creates a test instance to run. The instance is both created and destroyed
+ // within TestInfoImpl::Run()
+ virtual Test* CreateTest() = 0;
+
+ protected:
+ TestFactoryBase() {}
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase);
+};
+
+// This class provides implementation of TeastFactoryBase interface.
+// It is used in TEST and TEST_F macros.
+template <class TestClass>
+class TestFactoryImpl : public TestFactoryBase {
+ public:
+ virtual Test* CreateTest() { return new TestClass; }
+};
+
+#if GTEST_OS_WINDOWS
+
+// Predicate-formatters for implementing the HRESULT checking macros
+// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}
+// We pass a long instead of HRESULT to avoid causing an
+// include dependency for the HRESULT type.
+GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr,
+ long hr); // NOLINT
+GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr,
+ long hr); // NOLINT
+
+#endif // GTEST_OS_WINDOWS
+
+// Types of SetUpTestCase() and TearDownTestCase() functions.
+typedef void (*SetUpTestCaseFunc)();
+typedef void (*TearDownTestCaseFunc)();
+
+// Creates a new TestInfo object and registers it with Google Test;
+// returns the created object.
+//
+// Arguments:
+//
+// test_case_name: name of the test case
+// name: name of the test
+// type_param the name of the test's type parameter, or NULL if
+// this is not a typed or a type-parameterized test.
+// value_param text representation of the test's value parameter,
+// or NULL if this is not a type-parameterized test.
+// fixture_class_id: ID of the test fixture class
+// set_up_tc: pointer to the function that sets up the test case
+// tear_down_tc: pointer to the function that tears down the test case
+// factory: pointer to the factory that creates a test object.
+// The newly created TestInfo instance will assume
+// ownership of the factory object.
+GTEST_API_ TestInfo* MakeAndRegisterTestInfo(
+ const char* test_case_name,
+ const char* name,
+ const char* type_param,
+ const char* value_param,
+ TypeId fixture_class_id,
+ SetUpTestCaseFunc set_up_tc,
+ TearDownTestCaseFunc tear_down_tc,
+ TestFactoryBase* factory);
+
+// If *pstr starts with the given prefix, modifies *pstr to be right
+// past the prefix and returns true; otherwise leaves *pstr unchanged
+// and returns false. None of pstr, *pstr, and prefix can be NULL.
+GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr);
+
+#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+// State of the definition of a type-parameterized test case.
+class GTEST_API_ TypedTestCasePState {
+ public:
+ TypedTestCasePState() : registered_(false) {}
+
+ // Adds the given test name to defined_test_names_ and return true
+ // if the test case hasn't been registered; otherwise aborts the
+ // program.
+ bool AddTestName(const char* file, int line, const char* case_name,
+ const char* test_name) {
+ if (registered_) {
+ fprintf(stderr, "%s Test %s must be defined before "
+ "REGISTER_TYPED_TEST_CASE_P(%s, ...).\n",
+ FormatFileLocation(file, line).c_str(), test_name, case_name);
+ fflush(stderr);
+ posix::Abort();
+ }
+ defined_test_names_.insert(test_name);
+ return true;
+ }
+
+ // Verifies that registered_tests match the test names in
+ // defined_test_names_; returns registered_tests if successful, or
+ // aborts the program otherwise.
+ const char* VerifyRegisteredTestNames(
+ const char* file, int line, const char* registered_tests);
+
+ private:
+ bool registered_;
+ ::std::set<const char*> defined_test_names_;
+};
+
+// Skips to the first non-space char after the first comma in 'str';
+// returns NULL if no comma is found in 'str'.
+inline const char* SkipComma(const char* str) {
+ const char* comma = strchr(str, ',');
+ if (comma == NULL) {
+ return NULL;
+ }
+ while (IsSpace(*(++comma))) {}
+ return comma;
+}
+
+// Returns the prefix of 'str' before the first comma in it; returns
+// the entire string if it contains no comma.
+inline std::string GetPrefixUntilComma(const char* str) {
+ const char* comma = strchr(str, ',');
+ return comma == NULL ? str : std::string(str, comma);
+}
+
+// TypeParameterizedTest<Fixture, TestSel, Types>::Register()
+// registers a list of type-parameterized tests with Google Test. The
+// return value is insignificant - we just need to return something
+// such that we can call this function in a namespace scope.
+//
+// Implementation note: The GTEST_TEMPLATE_ macro declares a template
+// template parameter. It's defined in gtest-type-util.h.
+template <GTEST_TEMPLATE_ Fixture, class TestSel, typename Types>
+class TypeParameterizedTest {
+ public:
+ // 'index' is the index of the test in the type list 'Types'
+ // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase,
+ // Types). Valid values for 'index' are [0, N - 1] where N is the
+ // length of Types.
+ static bool Register(const char* prefix, const char* case_name,
+ const char* test_names, int index) {
+ typedef typename Types::Head Type;
+ typedef Fixture<Type> FixtureClass;
+ typedef typename GTEST_BIND_(TestSel, Type) TestClass;
+
+ // First, registers the first type-parameterized test in the type
+ // list.
+ MakeAndRegisterTestInfo(
+ (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/"
+ + StreamableToString(index)).c_str(),
+ GetPrefixUntilComma(test_names).c_str(),
+ GetTypeName<Type>().c_str(),
+ NULL, // No value parameter.
+ GetTypeId<FixtureClass>(),
+ TestClass::SetUpTestCase,
+ TestClass::TearDownTestCase,
+ new TestFactoryImpl<TestClass>);
+
+ // Next, recurses (at compile time) with the tail of the type list.
+ return TypeParameterizedTest<Fixture, TestSel, typename Types::Tail>
+ ::Register(prefix, case_name, test_names, index + 1);
+ }
+};
+
+// The base case for the compile time recursion.
+template <GTEST_TEMPLATE_ Fixture, class TestSel>
+class TypeParameterizedTest<Fixture, TestSel, Types0> {
+ public:
+ static bool Register(const char* /*prefix*/, const char* /*case_name*/,
+ const char* /*test_names*/, int /*index*/) {
+ return true;
+ }
+};
+
+// TypeParameterizedTestCase<Fixture, Tests, Types>::Register()
+// registers *all combinations* of 'Tests' and 'Types' with Google
+// Test. The return value is insignificant - we just need to return
+// something such that we can call this function in a namespace scope.
+template <GTEST_TEMPLATE_ Fixture, typename Tests, typename Types>
+class TypeParameterizedTestCase {
+ public:
+ static bool Register(const char* prefix, const char* case_name,
+ const char* test_names) {
+ typedef typename Tests::Head Head;
+
+ // First, register the first test in 'Test' for each type in 'Types'.
+ TypeParameterizedTest<Fixture, Head, Types>::Register(
+ prefix, case_name, test_names, 0);
+
+ // Next, recurses (at compile time) with the tail of the test list.
+ return TypeParameterizedTestCase<Fixture, typename Tests::Tail, Types>
+ ::Register(prefix, case_name, SkipComma(test_names));
+ }
+};
+
+// The base case for the compile time recursion.
+template <GTEST_TEMPLATE_ Fixture, typename Types>
+class TypeParameterizedTestCase<Fixture, Templates0, Types> {
+ public:
+ static bool Register(const char* /*prefix*/, const char* /*case_name*/,
+ const char* /*test_names*/) {
+ return true;
+ }
+};
+
+#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+// Returns the current OS stack trace as an std::string.
+//
+// The maximum number of stack frames to be included is specified by
+// the gtest_stack_trace_depth flag. The skip_count parameter
+// specifies the number of top frames to be skipped, which doesn't
+// count against the number of frames to be included.
+//
+// For example, if Foo() calls Bar(), which in turn calls
+// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in
+// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't.
+GTEST_API_ std::string GetCurrentOsStackTraceExceptTop(
+ UnitTest* unit_test, int skip_count);
+
+// Helpers for suppressing warnings on unreachable code or constant
+// condition.
+
+// Always returns true.
+GTEST_API_ bool AlwaysTrue();
+
+// Always returns false.
+inline bool AlwaysFalse() { return !AlwaysTrue(); }
+
+// Helper for suppressing false warning from Clang on a const char*
+// variable declared in a conditional expression always being NULL in
+// the else branch.
+struct GTEST_API_ ConstCharPtr {
+ ConstCharPtr(const char* str) : value(str) {}
+ operator bool() const { return true; }
+ const char* value;
+};
+
+// A simple Linear Congruential Generator for generating random
+// numbers with a uniform distribution. Unlike rand() and srand(), it
+// doesn't use global state (and therefore can't interfere with user
+// code). Unlike rand_r(), it's portable. An LCG isn't very random,
+// but it's good enough for our purposes.
+class GTEST_API_ Random {
+ public:
+ static const UInt32 kMaxRange = 1u << 31;
+
+ explicit Random(UInt32 seed) : state_(seed) {}
+
+ void Reseed(UInt32 seed) { state_ = seed; }
+
+ // Generates a random number from [0, range). Crashes if 'range' is
+ // 0 or greater than kMaxRange.
+ UInt32 Generate(UInt32 range);
+
+ private:
+ UInt32 state_;
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(Random);
+};
+
+// Defining a variable of type CompileAssertTypesEqual<T1, T2> will cause a
+// compiler error iff T1 and T2 are different types.
+template <typename T1, typename T2>
+struct CompileAssertTypesEqual;
+
+template <typename T>
+struct CompileAssertTypesEqual<T, T> {
+};
+
+// Removes the reference from a type if it is a reference type,
+// otherwise leaves it unchanged. This is the same as
+// tr1::remove_reference, which is not widely available yet.
+template <typename T>
+struct RemoveReference { typedef T type; }; // NOLINT
+template <typename T>
+struct RemoveReference<T&> { typedef T type; }; // NOLINT
+
+// A handy wrapper around RemoveReference that works when the argument
+// T depends on template parameters.
+#define GTEST_REMOVE_REFERENCE_(T) \
+ typename ::testing::internal::RemoveReference<T>::type
+
+// Removes const from a type if it is a const type, otherwise leaves
+// it unchanged. This is the same as tr1::remove_const, which is not
+// widely available yet.
+template <typename T>
+struct RemoveConst { typedef T type; }; // NOLINT
+template <typename T>
+struct RemoveConst<const T> { typedef T type; }; // NOLINT
+
+// MSVC 8.0, Sun C++, and IBM XL C++ have a bug which causes the above
+// definition to fail to remove the const in 'const int[3]' and 'const
+// char[3][4]'. The following specialization works around the bug.
+template <typename T, size_t N>
+struct RemoveConst<const T[N]> {
+ typedef typename RemoveConst<T>::type type[N];
+};
+
+#if defined(_MSC_VER) && _MSC_VER < 1400
+// This is the only specialization that allows VC++ 7.1 to remove const in
+// 'const int[3] and 'const int[3][4]'. However, it causes trouble with GCC
+// and thus needs to be conditionally compiled.
+template <typename T, size_t N>
+struct RemoveConst<T[N]> {
+ typedef typename RemoveConst<T>::type type[N];
+};
+#endif
+
+// A handy wrapper around RemoveConst that works when the argument
+// T depends on template parameters.
+#define GTEST_REMOVE_CONST_(T) \
+ typename ::testing::internal::RemoveConst<T>::type
+
+// Turns const U&, U&, const U, and U all into U.
+#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \
+ GTEST_REMOVE_CONST_(GTEST_REMOVE_REFERENCE_(T))
+
+// Adds reference to a type if it is not a reference type,
+// otherwise leaves it unchanged. This is the same as
+// tr1::add_reference, which is not widely available yet.
+template <typename T>
+struct AddReference { typedef T& type; }; // NOLINT
+template <typename T>
+struct AddReference<T&> { typedef T& type; }; // NOLINT
+
+// A handy wrapper around AddReference that works when the argument T
+// depends on template parameters.
+#define GTEST_ADD_REFERENCE_(T) \
+ typename ::testing::internal::AddReference<T>::type
+
+// Adds a reference to const on top of T as necessary. For example,
+// it transforms
+//
+// char ==> const char&
+// const char ==> const char&
+// char& ==> const char&
+// const char& ==> const char&
+//
+// The argument T must depend on some template parameters.
+#define GTEST_REFERENCE_TO_CONST_(T) \
+ GTEST_ADD_REFERENCE_(const GTEST_REMOVE_REFERENCE_(T))
+
+// ImplicitlyConvertible<From, To>::value is a compile-time bool
+// constant that's true iff type From can be implicitly converted to
+// type To.
+template <typename From, typename To>
+class ImplicitlyConvertible {
+ private:
+ // We need the following helper functions only for their types.
+ // They have no implementations.
+
+ // MakeFrom() is an expression whose type is From. We cannot simply
+ // use From(), as the type From may not have a public default
+ // constructor.
+ static From MakeFrom();
+
+ // These two functions are overloaded. Given an expression
+ // Helper(x), the compiler will pick the first version if x can be
+ // implicitly converted to type To; otherwise it will pick the
+ // second version.
+ //
+ // The first version returns a value of size 1, and the second
+ // version returns a value of size 2. Therefore, by checking the
+ // size of Helper(x), which can be done at compile time, we can tell
+ // which version of Helper() is used, and hence whether x can be
+ // implicitly converted to type To.
+ static char Helper(To);
+ static char (&Helper(...))[2]; // NOLINT
+
+ // We have to put the 'public' section after the 'private' section,
+ // or MSVC refuses to compile the code.
+ public:
+ // MSVC warns about implicitly converting from double to int for
+ // possible loss of data, so we need to temporarily disable the
+ // warning.
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4244) // Temporarily disables warning 4244.
+
+ static const bool value =
+ sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
+# pragma warning(pop) // Restores the warning state.
+#elif defined(__BORLANDC__)
+ // C++Builder cannot use member overload resolution during template
+ // instantiation. The simplest workaround is to use its C++0x type traits
+ // functions (C++Builder 2009 and above only).
+ static const bool value = __is_convertible(From, To);
+#else
+ static const bool value =
+ sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
+#endif // _MSV_VER
+};
+template <typename From, typename To>
+const bool ImplicitlyConvertible<From, To>::value;
+
+// IsAProtocolMessage<T>::value is a compile-time bool constant that's
+// true iff T is type ProtocolMessage, proto2::Message, or a subclass
+// of those.
+template <typename T>
+struct IsAProtocolMessage
+ : public bool_constant<
+ ImplicitlyConvertible<const T*, const ::ProtocolMessage*>::value ||
+ ImplicitlyConvertible<const T*, const ::proto2::Message*>::value> {
+};
+
+// When the compiler sees expression IsContainerTest<C>(0), if C is an
+// STL-style container class, the first overload of IsContainerTest
+// will be viable (since both C::iterator* and C::const_iterator* are
+// valid types and NULL can be implicitly converted to them). It will
+// be picked over the second overload as 'int' is a perfect match for
+// the type of argument 0. If C::iterator or C::const_iterator is not
+// a valid type, the first overload is not viable, and the second
+// overload will be picked. Therefore, we can determine whether C is
+// a container class by checking the type of IsContainerTest<C>(0).
+// The value of the expression is insignificant.
+//
+// Note that we look for both C::iterator and C::const_iterator. The
+// reason is that C++ injects the name of a class as a member of the
+// class itself (e.g. you can refer to class iterator as either
+// 'iterator' or 'iterator::iterator'). If we look for C::iterator
+// only, for example, we would mistakenly think that a class named
+// iterator is an STL container.
+//
+// Also note that the simpler approach of overloading
+// IsContainerTest(typename C::const_iterator*) and
+// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++.
+typedef int IsContainer;
+template <class C>
+IsContainer IsContainerTest(int /* dummy */,
+ typename C::iterator* /* it */ = NULL,
+ typename C::const_iterator* /* const_it */ = NULL) {
+ return 0;
+}
+
+typedef char IsNotContainer;
+template <class C>
+IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; }
+
+// EnableIf<condition>::type is void when 'Cond' is true, and
+// undefined when 'Cond' is false. To use SFINAE to make a function
+// overload only apply when a particular expression is true, add
+// "typename EnableIf<expression>::type* = 0" as the last parameter.
+template<bool> struct EnableIf;
+template<> struct EnableIf<true> { typedef void type; }; // NOLINT
+
+// Utilities for native arrays.
+
+// ArrayEq() compares two k-dimensional native arrays using the
+// elements' operator==, where k can be any integer >= 0. When k is
+// 0, ArrayEq() degenerates into comparing a single pair of values.
+
+template <typename T, typename U>
+bool ArrayEq(const T* lhs, size_t size, const U* rhs);
+
+// This generic version is used when k is 0.
+template <typename T, typename U>
+inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; }
+
+// This overload is used when k >= 1.
+template <typename T, typename U, size_t N>
+inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) {
+ return internal::ArrayEq(lhs, N, rhs);
+}
+
+// This helper reduces code bloat. If we instead put its logic inside
+// the previous ArrayEq() function, arrays with different sizes would
+// lead to different copies of the template code.
+template <typename T, typename U>
+bool ArrayEq(const T* lhs, size_t size, const U* rhs) {
+ for (size_t i = 0; i != size; i++) {
+ if (!internal::ArrayEq(lhs[i], rhs[i]))
+ return false;
+ }
+ return true;
+}
+
+// Finds the first element in the iterator range [begin, end) that
+// equals elem. Element may be a native array type itself.
+template <typename Iter, typename Element>
+Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) {
+ for (Iter it = begin; it != end; ++it) {
+ if (internal::ArrayEq(*it, elem))
+ return it;
+ }
+ return end;
+}
+
+// CopyArray() copies a k-dimensional native array using the elements'
+// operator=, where k can be any integer >= 0. When k is 0,
+// CopyArray() degenerates into copying a single value.
+
+template <typename T, typename U>
+void CopyArray(const T* from, size_t size, U* to);
+
+// This generic version is used when k is 0.
+template <typename T, typename U>
+inline void CopyArray(const T& from, U* to) { *to = from; }
+
+// This overload is used when k >= 1.
+template <typename T, typename U, size_t N>
+inline void CopyArray(const T(&from)[N], U(*to)[N]) {
+ internal::CopyArray(from, N, *to);
+}
+
+// This helper reduces code bloat. If we instead put its logic inside
+// the previous CopyArray() function, arrays with different sizes
+// would lead to different copies of the template code.
+template <typename T, typename U>
+void CopyArray(const T* from, size_t size, U* to) {
+ for (size_t i = 0; i != size; i++) {
+ internal::CopyArray(from[i], to + i);
+ }
+}
+
+// The relation between an NativeArray object (see below) and the
+// native array it represents.
+enum RelationToSource {
+ kReference, // The NativeArray references the native array.
+ kCopy // The NativeArray makes a copy of the native array and
+ // owns the copy.
+};
+
+// Adapts a native array to a read-only STL-style container. Instead
+// of the complete STL container concept, this adaptor only implements
+// members useful for Google Mock's container matchers. New members
+// should be added as needed. To simplify the implementation, we only
+// support Element being a raw type (i.e. having no top-level const or
+// reference modifier). It's the client's responsibility to satisfy
+// this requirement. Element can be an array type itself (hence
+// multi-dimensional arrays are supported).
+template <typename Element>
+class NativeArray {
+ public:
+ // STL-style container typedefs.
+ typedef Element value_type;
+ typedef Element* iterator;
+ typedef const Element* const_iterator;
+
+ // Constructs from a native array.
+ NativeArray(const Element* array, size_t count, RelationToSource relation) {
+ Init(array, count, relation);
+ }
+
+ // Copy constructor.
+ NativeArray(const NativeArray& rhs) {
+ Init(rhs.array_, rhs.size_, rhs.relation_to_source_);
+ }
+
+ ~NativeArray() {
+ // Ensures that the user doesn't instantiate NativeArray with a
+ // const or reference type.
+ static_cast<void>(StaticAssertTypeEqHelper<Element,
+ GTEST_REMOVE_REFERENCE_AND_CONST_(Element)>());
+ if (relation_to_source_ == kCopy)
+ delete[] array_;
+ }
+
+ // STL-style container methods.
+ size_t size() const { return size_; }
+ const_iterator begin() const { return array_; }
+ const_iterator end() const { return array_ + size_; }
+ bool operator==(const NativeArray& rhs) const {
+ return size() == rhs.size() &&
+ ArrayEq(begin(), size(), rhs.begin());
+ }
+
+ private:
+ // Initializes this object; makes a copy of the input array if
+ // 'relation' is kCopy.
+ void Init(const Element* array, size_t a_size, RelationToSource relation) {
+ if (relation == kReference) {
+ array_ = array;
+ } else {
+ Element* const copy = new Element[a_size];
+ CopyArray(array, a_size, copy);
+ array_ = copy;
+ }
+ size_ = a_size;
+ relation_to_source_ = relation;
+ }
+
+ const Element* array_;
+ size_t size_;
+ RelationToSource relation_to_source_;
+
+ GTEST_DISALLOW_ASSIGN_(NativeArray);
+};
+
+} // namespace internal
+} // namespace testing
+
+#define GTEST_MESSAGE_AT_(file, line, message, result_type) \
+ ::testing::internal::AssertHelper(result_type, file, line, message) \
+ = ::testing::Message()
+
+#define GTEST_MESSAGE_(message, result_type) \
+ GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type)
+
+#define GTEST_FATAL_FAILURE_(message) \
+ return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure)
+
+#define GTEST_NONFATAL_FAILURE_(message) \
+ GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure)
+
+#define GTEST_SUCCESS_(message) \
+ GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess)
+
+// Suppresses MSVC warnings 4072 (unreachable code) for the code following
+// statement if it returns or throws (or doesn't return or throw in some
+// situations).
+#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \
+ if (::testing::internal::AlwaysTrue()) { statement; }
+
+#define GTEST_TEST_THROW_(statement, expected_exception, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::ConstCharPtr gtest_msg = "") { \
+ bool gtest_caught_expected = false; \
+ try { \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ } \
+ catch (expected_exception const&) { \
+ gtest_caught_expected = true; \
+ } \
+ catch (...) { \
+ gtest_msg.value = \
+ "Expected: " #statement " throws an exception of type " \
+ #expected_exception ".\n Actual: it throws a different type."; \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
+ } \
+ if (!gtest_caught_expected) { \
+ gtest_msg.value = \
+ "Expected: " #statement " throws an exception of type " \
+ #expected_exception ".\n Actual: it throws nothing."; \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
+ } \
+ } else \
+ GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \
+ fail(gtest_msg.value)
+
+#define GTEST_TEST_NO_THROW_(statement, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ try { \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ } \
+ catch (...) { \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \
+ } \
+ } else \
+ GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \
+ fail("Expected: " #statement " doesn't throw an exception.\n" \
+ " Actual: it throws.")
+
+#define GTEST_TEST_ANY_THROW_(statement, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ bool gtest_caught_any = false; \
+ try { \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ } \
+ catch (...) { \
+ gtest_caught_any = true; \
+ } \
+ if (!gtest_caught_any) { \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \
+ } \
+ } else \
+ GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \
+ fail("Expected: " #statement " throws an exception.\n" \
+ " Actual: it doesn't.")
+
+
+// Implements Boolean test assertions such as EXPECT_TRUE. expression can be
+// either a boolean expression or an AssertionResult. text is a textual
+// represenation of expression as it was passed into the EXPECT_TRUE.
+#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (const ::testing::AssertionResult gtest_ar_ = \
+ ::testing::AssertionResult(expression)) \
+ ; \
+ else \
+ fail(::testing::internal::GetBoolAssertionFailureMessage(\
+ gtest_ar_, text, #actual, #expected).c_str())
+
+#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \
+ } \
+ } else \
+ GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \
+ fail("Expected: " #statement " doesn't generate new fatal " \
+ "failures in the current thread.\n" \
+ " Actual: it does.")
+
+// Expands to the name of the class that implements the given test.
+#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
+ test_case_name##_##test_name##_Test
+
+// Helper macro for defining tests.
+#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\
+class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\
+ public:\
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\
+ private:\
+ virtual void TestBody();\
+ static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(\
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\
+};\
+\
+::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\
+ ::test_info_ =\
+ ::testing::internal::MakeAndRegisterTestInfo(\
+ #test_case_name, #test_name, NULL, NULL, \
+ (parent_id), \
+ parent_class::SetUpTestCase, \
+ parent_class::TearDownTestCase, \
+ new ::testing::internal::TestFactoryImpl<\
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\
+void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
diff --git a/test/gtest/include/gtest/internal/gtest-linked_ptr.h b/test/gtest/include/gtest/internal/gtest-linked_ptr.h
new file mode 100644
index 0000000..b1362cd
--- /dev/null
+++ b/test/gtest/include/gtest/internal/gtest-linked_ptr.h
@@ -0,0 +1,233 @@
+// Copyright 2003 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: Dan Egnor (egnor at google.com)
+//
+// A "smart" pointer type with reference tracking. Every pointer to a
+// particular object is kept on a circular linked list. When the last pointer
+// to an object is destroyed or reassigned, the object is deleted.
+//
+// Used properly, this deletes the object when the last reference goes away.
+// There are several caveats:
+// - Like all reference counting schemes, cycles lead to leaks.
+// - Each smart pointer is actually two pointers (8 bytes instead of 4).
+// - Every time a pointer is assigned, the entire list of pointers to that
+// object is traversed. This class is therefore NOT SUITABLE when there
+// will often be more than two or three pointers to a particular object.
+// - References are only tracked as long as linked_ptr<> objects are copied.
+// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS
+// will happen (double deletion).
+//
+// A good use of this class is storing object references in STL containers.
+// You can safely put linked_ptr<> in a vector<>.
+// Other uses may not be as good.
+//
+// Note: If you use an incomplete type with linked_ptr<>, the class
+// *containing* linked_ptr<> must have a constructor and destructor (even
+// if they do nothing!).
+//
+// Bill Gibbons suggested we use something like this.
+//
+// Thread Safety:
+// Unlike other linked_ptr implementations, in this implementation
+// a linked_ptr object is thread-safe in the sense that:
+// - it's safe to copy linked_ptr objects concurrently,
+// - it's safe to copy *from* a linked_ptr and read its underlying
+// raw pointer (e.g. via get()) concurrently, and
+// - it's safe to write to two linked_ptrs that point to the same
+// shared object concurrently.
+// TODO(wan at google.com): rename this to safe_linked_ptr to avoid
+// confusion with normal linked_ptr.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "gtest/internal/gtest-port.h"
+
+namespace testing {
+namespace internal {
+
+// Protects copying of all linked_ptr objects.
+GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex);
+
+// This is used internally by all instances of linked_ptr<>. It needs to be
+// a non-template class because different types of linked_ptr<> can refer to
+// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)).
+// So, it needs to be possible for different types of linked_ptr to participate
+// in the same circular linked list, so we need a single class type here.
+//
+// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr<T>.
+class linked_ptr_internal {
+ public:
+ // Create a new circle that includes only this instance.
+ void join_new() {
+ next_ = this;
+ }
+
+ // Many linked_ptr operations may change p.link_ for some linked_ptr
+ // variable p in the same circle as this object. Therefore we need
+ // to prevent two such operations from occurring concurrently.
+ //
+ // Note that different types of linked_ptr objects can coexist in a
+ // circle (e.g. linked_ptr<Base>, linked_ptr<Derived1>, and
+ // linked_ptr<Derived2>). Therefore we must use a single mutex to
+ // protect all linked_ptr objects. This can create serious
+ // contention in production code, but is acceptable in a testing
+ // framework.
+
+ // Join an existing circle.
+ void join(linked_ptr_internal const* ptr)
+ GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) {
+ MutexLock lock(&g_linked_ptr_mutex);
+
+ linked_ptr_internal const* p = ptr;
+ while (p->next_ != ptr) p = p->next_;
+ p->next_ = this;
+ next_ = ptr;
+ }
+
+ // Leave whatever circle we're part of. Returns true if we were the
+ // last member of the circle. Once this is done, you can join() another.
+ bool depart()
+ GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) {
+ MutexLock lock(&g_linked_ptr_mutex);
+
+ if (next_ == this) return true;
+ linked_ptr_internal const* p = next_;
+ while (p->next_ != this) p = p->next_;
+ p->next_ = next_;
+ return false;
+ }
+
+ private:
+ mutable linked_ptr_internal const* next_;
+};
+
+template <typename T>
+class linked_ptr {
+ public:
+ typedef T element_type;
+
+ // Take over ownership of a raw pointer. This should happen as soon as
+ // possible after the object is created.
+ explicit linked_ptr(T* ptr = NULL) { capture(ptr); }
+ ~linked_ptr() { depart(); }
+
+ // Copy an existing linked_ptr<>, adding ourselves to the list of references.
+ template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); }
+ linked_ptr(linked_ptr const& ptr) { // NOLINT
+ assert(&ptr != this);
+ copy(&ptr);
+ }
+
+ // Assignment releases the old value and acquires the new.
+ template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) {
+ depart();
+ copy(&ptr);
+ return *this;
+ }
+
+ linked_ptr& operator=(linked_ptr const& ptr) {
+ if (&ptr != this) {
+ depart();
+ copy(&ptr);
+ }
+ return *this;
+ }
+
+ // Smart pointer members.
+ void reset(T* ptr = NULL) {
+ depart();
+ capture(ptr);
+ }
+ T* get() const { return value_; }
+ T* operator->() const { return value_; }
+ T& operator*() const { return *value_; }
+
+ bool operator==(T* p) const { return value_ == p; }
+ bool operator!=(T* p) const { return value_ != p; }
+ template <typename U>
+ bool operator==(linked_ptr<U> const& ptr) const {
+ return value_ == ptr.get();
+ }
+ template <typename U>
+ bool operator!=(linked_ptr<U> const& ptr) const {
+ return value_ != ptr.get();
+ }
+
+ private:
+ template <typename U>
+ friend class linked_ptr;
+
+ T* value_;
+ linked_ptr_internal link_;
+
+ void depart() {
+ if (link_.depart()) delete value_;
+ }
+
+ void capture(T* ptr) {
+ value_ = ptr;
+ link_.join_new();
+ }
+
+ template <typename U> void copy(linked_ptr<U> const* ptr) {
+ value_ = ptr->get();
+ if (value_)
+ link_.join(&ptr->link_);
+ else
+ link_.join_new();
+ }
+};
+
+template<typename T> inline
+bool operator==(T* ptr, const linked_ptr<T>& x) {
+ return ptr == x.get();
+}
+
+template<typename T> inline
+bool operator!=(T* ptr, const linked_ptr<T>& x) {
+ return ptr != x.get();
+}
+
+// A function to convert T* into linked_ptr<T>
+// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation
+// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
+template <typename T>
+linked_ptr<T> make_linked_ptr(T* ptr) {
+ return linked_ptr<T>(ptr);
+}
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
diff --git a/test/gtest/include/gtest/internal/gtest-param-util-generated.h b/test/gtest/include/gtest/internal/gtest-param-util-generated.h
new file mode 100644
index 0000000..e805485
--- /dev/null
+++ b/test/gtest/include/gtest/internal/gtest-param-util-generated.h
@@ -0,0 +1,5143 @@
+// This file was GENERATED by command:
+// pump.py gtest-param-util-generated.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: vladl at google.com (Vlad Losev)
+
+// Type and function utilities for implementing parameterized tests.
+// This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
+//
+// Currently Google Test supports at most 50 arguments in Values,
+// and at most 10 arguments in Combine. Please contact
+// googletestframework at googlegroups.com if you need more.
+// Please note that the number of arguments to Combine is limited
+// by the maximum arity of the implementation of tr1::tuple which is
+// currently set at 10.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
+
+// scripts/fuse_gtest.py depends on gtest's own header being #included
+// *unconditionally*. Therefore these #includes cannot be moved
+// inside #if GTEST_HAS_PARAM_TEST.
+#include "gtest/internal/gtest-param-util.h"
+#include "gtest/internal/gtest-port.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace testing {
+
+// Forward declarations of ValuesIn(), which is implemented in
+// include/gtest/gtest-param-test.h.
+template <typename ForwardIterator>
+internal::ParamGenerator<
+ typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type>
+ValuesIn(ForwardIterator begin, ForwardIterator end);
+
+template <typename T, size_t N>
+internal::ParamGenerator<T> ValuesIn(const T (&array)[N]);
+
+template <class Container>
+internal::ParamGenerator<typename Container::value_type> ValuesIn(
+ const Container& container);
+
+namespace internal {
+
+// Used in the Values() function to provide polymorphic capabilities.
+template <typename T1>
+class ValueArray1 {
+ public:
+ explicit ValueArray1(T1 v1) : v1_(v1) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const { return ValuesIn(&v1_, &v1_ + 1); }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray1& other);
+
+ const T1 v1_;
+};
+
+template <typename T1, typename T2>
+class ValueArray2 {
+ public:
+ ValueArray2(T1 v1, T2 v2) : v1_(v1), v2_(v2) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray2& other);
+
+ const T1 v1_;
+ const T2 v2_;
+};
+
+template <typename T1, typename T2, typename T3>
+class ValueArray3 {
+ public:
+ ValueArray3(T1 v1, T2 v2, T3 v3) : v1_(v1), v2_(v2), v3_(v3) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray3& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4>
+class ValueArray4 {
+ public:
+ ValueArray4(T1 v1, T2 v2, T3 v3, T4 v4) : v1_(v1), v2_(v2), v3_(v3),
+ v4_(v4) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray4& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+class ValueArray5 {
+ public:
+ ValueArray5(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5) : v1_(v1), v2_(v2), v3_(v3),
+ v4_(v4), v5_(v5) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray5& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+class ValueArray6 {
+ public:
+ ValueArray6(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6) : v1_(v1), v2_(v2),
+ v3_(v3), v4_(v4), v5_(v5), v6_(v6) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray6& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+class ValueArray7 {
+ public:
+ ValueArray7(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7) : v1_(v1),
+ v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray7& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+class ValueArray8 {
+ public:
+ ValueArray8(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+ T8 v8) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray8& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+class ValueArray9 {
+ public:
+ ValueArray9(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
+ T9 v9) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray9& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+class ValueArray10 {
+ public:
+ ValueArray10(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray10& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11>
+class ValueArray11 {
+ public:
+ ValueArray11(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+ v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray11& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12>
+class ValueArray12 {
+ public:
+ ValueArray12(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+ v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray12& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13>
+class ValueArray13 {
+ public:
+ ValueArray13(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+ v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+ v12_(v12), v13_(v13) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray13& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14>
+class ValueArray14 {
+ public:
+ ValueArray14(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) : v1_(v1), v2_(v2), v3_(v3),
+ v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray14& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15>
+class ValueArray15 {
+ public:
+ ValueArray15(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) : v1_(v1), v2_(v2),
+ v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray15& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16>
+class ValueArray16 {
+ public:
+ ValueArray16(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16) : v1_(v1),
+ v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+ v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+ v16_(v16) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray16& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17>
+class ValueArray17 {
+ public:
+ ValueArray17(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16,
+ T17 v17) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray17& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18>
+class ValueArray18 {
+ public:
+ ValueArray18(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray18& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19>
+class ValueArray19 {
+ public:
+ ValueArray19(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+ v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
+ v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray19& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20>
+class ValueArray20 {
+ public:
+ ValueArray20(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+ v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
+ v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
+ v19_(v19), v20_(v20) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray20& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21>
+class ValueArray21 {
+ public:
+ ValueArray21(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+ v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+ v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
+ v18_(v18), v19_(v19), v20_(v20), v21_(v21) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray21& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22>
+class ValueArray22 {
+ public:
+ ValueArray22(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22) : v1_(v1), v2_(v2), v3_(v3),
+ v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray22& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23>
+class ValueArray23 {
+ public:
+ ValueArray23(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23) : v1_(v1), v2_(v2),
+ v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+ v23_(v23) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray23& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24>
+class ValueArray24 {
+ public:
+ ValueArray24(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24) : v1_(v1),
+ v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+ v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+ v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
+ v22_(v22), v23_(v23), v24_(v24) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray24& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25>
+class ValueArray25 {
+ public:
+ ValueArray25(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24,
+ T25 v25) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray25& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26>
+class ValueArray26 {
+ public:
+ ValueArray26(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray26& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27>
+class ValueArray27 {
+ public:
+ ValueArray27(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+ v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
+ v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19),
+ v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25),
+ v26_(v26), v27_(v27) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray27& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28>
+class ValueArray28 {
+ public:
+ ValueArray28(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+ v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
+ v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
+ v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24),
+ v25_(v25), v26_(v26), v27_(v27), v28_(v28) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray28& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29>
+class ValueArray29 {
+ public:
+ ValueArray29(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+ v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+ v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
+ v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23),
+ v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray29& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30>
+class ValueArray30 {
+ public:
+ ValueArray30(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) : v1_(v1), v2_(v2), v3_(v3),
+ v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+ v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+ v29_(v29), v30_(v30) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray30& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31>
+class ValueArray31 {
+ public:
+ ValueArray31(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) : v1_(v1), v2_(v2),
+ v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+ v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+ v29_(v29), v30_(v30), v31_(v31) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray31& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32>
+class ValueArray32 {
+ public:
+ ValueArray32(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32) : v1_(v1),
+ v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+ v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+ v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
+ v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27),
+ v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray32& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33>
+class ValueArray33 {
+ public:
+ ValueArray33(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32,
+ T33 v33) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+ v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+ v33_(v33) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray33& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34>
+class ValueArray34 {
+ public:
+ ValueArray34(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+ v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+ v33_(v33), v34_(v34) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray34& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35>
+class ValueArray35 {
+ public:
+ ValueArray35(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+ v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
+ v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19),
+ v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25),
+ v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31),
+ v32_(v32), v33_(v33), v34_(v34), v35_(v35) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray35& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36>
+class ValueArray36 {
+ public:
+ ValueArray36(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+ v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
+ v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
+ v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24),
+ v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30),
+ v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray36& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37>
+class ValueArray37 {
+ public:
+ ValueArray37(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+ v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+ v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
+ v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23),
+ v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29),
+ v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35),
+ v36_(v36), v37_(v37) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray37& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38>
+class ValueArray38 {
+ public:
+ ValueArray38(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38) : v1_(v1), v2_(v2), v3_(v3),
+ v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+ v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+ v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
+ v35_(v35), v36_(v36), v37_(v37), v38_(v38) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray38& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39>
+class ValueArray39 {
+ public:
+ ValueArray39(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39) : v1_(v1), v2_(v2),
+ v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+ v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+ v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
+ v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray39& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40>
+class ValueArray40 {
+ public:
+ ValueArray40(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) : v1_(v1),
+ v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+ v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+ v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
+ v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27),
+ v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33),
+ v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39),
+ v40_(v40) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray40& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41>
+class ValueArray41 {
+ public:
+ ValueArray41(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40,
+ T41 v41) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+ v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+ v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
+ v39_(v39), v40_(v40), v41_(v41) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray41& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42>
+class ValueArray42 {
+ public:
+ ValueArray42(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+ v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+ v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
+ v39_(v39), v40_(v40), v41_(v41), v42_(v42) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray42& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43>
+class ValueArray43 {
+ public:
+ ValueArray43(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+ v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
+ v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19),
+ v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25),
+ v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31),
+ v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37),
+ v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray43& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44>
+class ValueArray44 {
+ public:
+ ValueArray44(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+ v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
+ v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
+ v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24),
+ v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30),
+ v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36),
+ v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42),
+ v43_(v43), v44_(v44) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray44& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+ const T44 v44_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45>
+class ValueArray45 {
+ public:
+ ValueArray45(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44, T45 v45) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+ v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+ v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
+ v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23),
+ v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29),
+ v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35),
+ v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41),
+ v42_(v42), v43_(v43), v44_(v44), v45_(v45) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+ static_cast<T>(v45_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray45& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+ const T44 v44_;
+ const T45 v45_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46>
+class ValueArray46 {
+ public:
+ ValueArray46(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) : v1_(v1), v2_(v2), v3_(v3),
+ v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+ v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+ v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
+ v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40),
+ v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+ static_cast<T>(v45_), static_cast<T>(v46_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray46& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+ const T44 v44_;
+ const T45 v45_;
+ const T46 v46_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47>
+class ValueArray47 {
+ public:
+ ValueArray47(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) : v1_(v1), v2_(v2),
+ v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+ v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+ v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
+ v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40),
+ v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46),
+ v47_(v47) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+ static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray47& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+ const T44 v44_;
+ const T45 v45_;
+ const T46 v46_;
+ const T47 v47_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48>
+class ValueArray48 {
+ public:
+ ValueArray48(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48) : v1_(v1),
+ v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+ v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+ v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
+ v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27),
+ v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33),
+ v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39),
+ v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45),
+ v46_(v46), v47_(v47), v48_(v48) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+ static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_),
+ static_cast<T>(v48_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray48& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+ const T44 v44_;
+ const T45 v45_;
+ const T46 v46_;
+ const T47 v47_;
+ const T48 v48_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49>
+class ValueArray49 {
+ public:
+ ValueArray49(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48,
+ T49 v49) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+ v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+ v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
+ v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44),
+ v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+ static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_),
+ static_cast<T>(v48_), static_cast<T>(v49_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray49& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+ const T44 v44_;
+ const T45 v45_;
+ const T46 v46_;
+ const T47 v47_;
+ const T48 v48_;
+ const T49 v49_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49, typename T50>
+class ValueArray50 {
+ public:
+ ValueArray50(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, T49 v49,
+ T50 v50) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+ v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+ v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
+ v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44),
+ v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49), v50_(v50) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+ static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_),
+ static_cast<T>(v48_), static_cast<T>(v49_), static_cast<T>(v50_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray50& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+ const T44 v44_;
+ const T45 v45_;
+ const T46 v46_;
+ const T47 v47_;
+ const T48 v48_;
+ const T49 v49_;
+ const T50 v50_;
+};
+
+# if GTEST_HAS_COMBINE
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Generates values from the Cartesian product of values produced
+// by the argument generators.
+//
+template <typename T1, typename T2>
+class CartesianProductGenerator2
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2> ParamType;
+
+ CartesianProductGenerator2(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2)
+ : g1_(g1), g2_(g2) {}
+ virtual ~CartesianProductGenerator2() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current2_;
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return ¤t_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator2::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator2& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+}; // class CartesianProductGenerator2
+
+
+template <typename T1, typename T2, typename T3>
+class CartesianProductGenerator3
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3> ParamType;
+
+ CartesianProductGenerator3(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3)
+ : g1_(g1), g2_(g2), g3_(g3) {}
+ virtual ~CartesianProductGenerator3() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current3_;
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return ¤t_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator3::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator3& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+}; // class CartesianProductGenerator3
+
+
+template <typename T1, typename T2, typename T3, typename T4>
+class CartesianProductGenerator4
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3, T4> ParamType;
+
+ CartesianProductGenerator4(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+ const ParamGenerator<T4>& g4)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {}
+ virtual ~CartesianProductGenerator4() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin(), g4_, g4_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+ g4_, g4_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3,
+ const ParamGenerator<T4>& g4,
+ const typename ParamGenerator<T4>::iterator& current4)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+ begin4_(g4.begin()), end4_(g4.end()), current4_(current4) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current4_;
+ if (current4_ == end4_) {
+ current4_ = begin4_;
+ ++current3_;
+ }
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return ¤t_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_ &&
+ current4_ == typed_other->current4_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_),
+ begin4_(other.begin4_),
+ end4_(other.end4_),
+ current4_(other.current4_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_,
+ *current4_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_ ||
+ current4_ == end4_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ const typename ParamGenerator<T4>::iterator begin4_;
+ const typename ParamGenerator<T4>::iterator end4_;
+ typename ParamGenerator<T4>::iterator current4_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator4::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator4& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+ const ParamGenerator<T4> g4_;
+}; // class CartesianProductGenerator4
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+class CartesianProductGenerator5
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3, T4, T5> ParamType;
+
+ CartesianProductGenerator5(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+ const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {}
+ virtual ~CartesianProductGenerator5() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+ g4_, g4_.end(), g5_, g5_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3,
+ const ParamGenerator<T4>& g4,
+ const typename ParamGenerator<T4>::iterator& current4,
+ const ParamGenerator<T5>& g5,
+ const typename ParamGenerator<T5>::iterator& current5)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+ begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+ begin5_(g5.begin()), end5_(g5.end()), current5_(current5) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current5_;
+ if (current5_ == end5_) {
+ current5_ = begin5_;
+ ++current4_;
+ }
+ if (current4_ == end4_) {
+ current4_ = begin4_;
+ ++current3_;
+ }
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return ¤t_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_ &&
+ current4_ == typed_other->current4_ &&
+ current5_ == typed_other->current5_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_),
+ begin4_(other.begin4_),
+ end4_(other.end4_),
+ current4_(other.current4_),
+ begin5_(other.begin5_),
+ end5_(other.end5_),
+ current5_(other.current5_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_,
+ *current4_, *current5_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_ ||
+ current4_ == end4_ ||
+ current5_ == end5_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ const typename ParamGenerator<T4>::iterator begin4_;
+ const typename ParamGenerator<T4>::iterator end4_;
+ typename ParamGenerator<T4>::iterator current4_;
+ const typename ParamGenerator<T5>::iterator begin5_;
+ const typename ParamGenerator<T5>::iterator end5_;
+ typename ParamGenerator<T5>::iterator current5_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator5::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator5& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+ const ParamGenerator<T4> g4_;
+ const ParamGenerator<T5> g5_;
+}; // class CartesianProductGenerator5
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+class CartesianProductGenerator6
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5,
+ T6> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> ParamType;
+
+ CartesianProductGenerator6(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+ const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+ const ParamGenerator<T6>& g6)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {}
+ virtual ~CartesianProductGenerator6() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+ g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3,
+ const ParamGenerator<T4>& g4,
+ const typename ParamGenerator<T4>::iterator& current4,
+ const ParamGenerator<T5>& g5,
+ const typename ParamGenerator<T5>::iterator& current5,
+ const ParamGenerator<T6>& g6,
+ const typename ParamGenerator<T6>::iterator& current6)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+ begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+ begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+ begin6_(g6.begin()), end6_(g6.end()), current6_(current6) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current6_;
+ if (current6_ == end6_) {
+ current6_ = begin6_;
+ ++current5_;
+ }
+ if (current5_ == end5_) {
+ current5_ = begin5_;
+ ++current4_;
+ }
+ if (current4_ == end4_) {
+ current4_ = begin4_;
+ ++current3_;
+ }
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return ¤t_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_ &&
+ current4_ == typed_other->current4_ &&
+ current5_ == typed_other->current5_ &&
+ current6_ == typed_other->current6_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_),
+ begin4_(other.begin4_),
+ end4_(other.end4_),
+ current4_(other.current4_),
+ begin5_(other.begin5_),
+ end5_(other.end5_),
+ current5_(other.current5_),
+ begin6_(other.begin6_),
+ end6_(other.end6_),
+ current6_(other.current6_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_,
+ *current4_, *current5_, *current6_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_ ||
+ current4_ == end4_ ||
+ current5_ == end5_ ||
+ current6_ == end6_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ const typename ParamGenerator<T4>::iterator begin4_;
+ const typename ParamGenerator<T4>::iterator end4_;
+ typename ParamGenerator<T4>::iterator current4_;
+ const typename ParamGenerator<T5>::iterator begin5_;
+ const typename ParamGenerator<T5>::iterator end5_;
+ typename ParamGenerator<T5>::iterator current5_;
+ const typename ParamGenerator<T6>::iterator begin6_;
+ const typename ParamGenerator<T6>::iterator end6_;
+ typename ParamGenerator<T6>::iterator current6_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator6::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator6& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+ const ParamGenerator<T4> g4_;
+ const ParamGenerator<T5> g5_;
+ const ParamGenerator<T6> g6_;
+}; // class CartesianProductGenerator6
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+class CartesianProductGenerator7
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ T7> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7> ParamType;
+
+ CartesianProductGenerator7(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+ const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+ const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {}
+ virtual ~CartesianProductGenerator7() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
+ g7_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+ g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3,
+ const ParamGenerator<T4>& g4,
+ const typename ParamGenerator<T4>::iterator& current4,
+ const ParamGenerator<T5>& g5,
+ const typename ParamGenerator<T5>::iterator& current5,
+ const ParamGenerator<T6>& g6,
+ const typename ParamGenerator<T6>::iterator& current6,
+ const ParamGenerator<T7>& g7,
+ const typename ParamGenerator<T7>::iterator& current7)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+ begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+ begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+ begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
+ begin7_(g7.begin()), end7_(g7.end()), current7_(current7) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current7_;
+ if (current7_ == end7_) {
+ current7_ = begin7_;
+ ++current6_;
+ }
+ if (current6_ == end6_) {
+ current6_ = begin6_;
+ ++current5_;
+ }
+ if (current5_ == end5_) {
+ current5_ = begin5_;
+ ++current4_;
+ }
+ if (current4_ == end4_) {
+ current4_ = begin4_;
+ ++current3_;
+ }
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return ¤t_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_ &&
+ current4_ == typed_other->current4_ &&
+ current5_ == typed_other->current5_ &&
+ current6_ == typed_other->current6_ &&
+ current7_ == typed_other->current7_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_),
+ begin4_(other.begin4_),
+ end4_(other.end4_),
+ current4_(other.current4_),
+ begin5_(other.begin5_),
+ end5_(other.end5_),
+ current5_(other.current5_),
+ begin6_(other.begin6_),
+ end6_(other.end6_),
+ current6_(other.current6_),
+ begin7_(other.begin7_),
+ end7_(other.end7_),
+ current7_(other.current7_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_,
+ *current4_, *current5_, *current6_, *current7_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_ ||
+ current4_ == end4_ ||
+ current5_ == end5_ ||
+ current6_ == end6_ ||
+ current7_ == end7_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ const typename ParamGenerator<T4>::iterator begin4_;
+ const typename ParamGenerator<T4>::iterator end4_;
+ typename ParamGenerator<T4>::iterator current4_;
+ const typename ParamGenerator<T5>::iterator begin5_;
+ const typename ParamGenerator<T5>::iterator end5_;
+ typename ParamGenerator<T5>::iterator current5_;
+ const typename ParamGenerator<T6>::iterator begin6_;
+ const typename ParamGenerator<T6>::iterator end6_;
+ typename ParamGenerator<T6>::iterator current6_;
+ const typename ParamGenerator<T7>::iterator begin7_;
+ const typename ParamGenerator<T7>::iterator end7_;
+ typename ParamGenerator<T7>::iterator current7_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator7::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator7& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+ const ParamGenerator<T4> g4_;
+ const ParamGenerator<T5> g5_;
+ const ParamGenerator<T6> g6_;
+ const ParamGenerator<T7> g7_;
+}; // class CartesianProductGenerator7
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+class CartesianProductGenerator8
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ T7, T8> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8> ParamType;
+
+ CartesianProductGenerator8(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+ const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+ const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7,
+ const ParamGenerator<T8>& g8)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7),
+ g8_(g8) {}
+ virtual ~CartesianProductGenerator8() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
+ g7_.begin(), g8_, g8_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+ g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_,
+ g8_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3,
+ const ParamGenerator<T4>& g4,
+ const typename ParamGenerator<T4>::iterator& current4,
+ const ParamGenerator<T5>& g5,
+ const typename ParamGenerator<T5>::iterator& current5,
+ const ParamGenerator<T6>& g6,
+ const typename ParamGenerator<T6>::iterator& current6,
+ const ParamGenerator<T7>& g7,
+ const typename ParamGenerator<T7>::iterator& current7,
+ const ParamGenerator<T8>& g8,
+ const typename ParamGenerator<T8>::iterator& current8)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+ begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+ begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+ begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
+ begin7_(g7.begin()), end7_(g7.end()), current7_(current7),
+ begin8_(g8.begin()), end8_(g8.end()), current8_(current8) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current8_;
+ if (current8_ == end8_) {
+ current8_ = begin8_;
+ ++current7_;
+ }
+ if (current7_ == end7_) {
+ current7_ = begin7_;
+ ++current6_;
+ }
+ if (current6_ == end6_) {
+ current6_ = begin6_;
+ ++current5_;
+ }
+ if (current5_ == end5_) {
+ current5_ = begin5_;
+ ++current4_;
+ }
+ if (current4_ == end4_) {
+ current4_ = begin4_;
+ ++current3_;
+ }
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return ¤t_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_ &&
+ current4_ == typed_other->current4_ &&
+ current5_ == typed_other->current5_ &&
+ current6_ == typed_other->current6_ &&
+ current7_ == typed_other->current7_ &&
+ current8_ == typed_other->current8_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_),
+ begin4_(other.begin4_),
+ end4_(other.end4_),
+ current4_(other.current4_),
+ begin5_(other.begin5_),
+ end5_(other.end5_),
+ current5_(other.current5_),
+ begin6_(other.begin6_),
+ end6_(other.end6_),
+ current6_(other.current6_),
+ begin7_(other.begin7_),
+ end7_(other.end7_),
+ current7_(other.current7_),
+ begin8_(other.begin8_),
+ end8_(other.end8_),
+ current8_(other.current8_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_,
+ *current4_, *current5_, *current6_, *current7_, *current8_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_ ||
+ current4_ == end4_ ||
+ current5_ == end5_ ||
+ current6_ == end6_ ||
+ current7_ == end7_ ||
+ current8_ == end8_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ const typename ParamGenerator<T4>::iterator begin4_;
+ const typename ParamGenerator<T4>::iterator end4_;
+ typename ParamGenerator<T4>::iterator current4_;
+ const typename ParamGenerator<T5>::iterator begin5_;
+ const typename ParamGenerator<T5>::iterator end5_;
+ typename ParamGenerator<T5>::iterator current5_;
+ const typename ParamGenerator<T6>::iterator begin6_;
+ const typename ParamGenerator<T6>::iterator end6_;
+ typename ParamGenerator<T6>::iterator current6_;
+ const typename ParamGenerator<T7>::iterator begin7_;
+ const typename ParamGenerator<T7>::iterator end7_;
+ typename ParamGenerator<T7>::iterator current7_;
+ const typename ParamGenerator<T8>::iterator begin8_;
+ const typename ParamGenerator<T8>::iterator end8_;
+ typename ParamGenerator<T8>::iterator current8_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator8::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator8& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+ const ParamGenerator<T4> g4_;
+ const ParamGenerator<T5> g5_;
+ const ParamGenerator<T6> g6_;
+ const ParamGenerator<T7> g7_;
+ const ParamGenerator<T8> g8_;
+}; // class CartesianProductGenerator8
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+class CartesianProductGenerator9
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ T7, T8, T9> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9> ParamType;
+
+ CartesianProductGenerator9(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+ const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+ const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7,
+ const ParamGenerator<T8>& g8, const ParamGenerator<T9>& g9)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
+ g9_(g9) {}
+ virtual ~CartesianProductGenerator9() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
+ g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+ g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_,
+ g8_.end(), g9_, g9_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3,
+ const ParamGenerator<T4>& g4,
+ const typename ParamGenerator<T4>::iterator& current4,
+ const ParamGenerator<T5>& g5,
+ const typename ParamGenerator<T5>::iterator& current5,
+ const ParamGenerator<T6>& g6,
+ const typename ParamGenerator<T6>::iterator& current6,
+ const ParamGenerator<T7>& g7,
+ const typename ParamGenerator<T7>::iterator& current7,
+ const ParamGenerator<T8>& g8,
+ const typename ParamGenerator<T8>::iterator& current8,
+ const ParamGenerator<T9>& g9,
+ const typename ParamGenerator<T9>::iterator& current9)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+ begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+ begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+ begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
+ begin7_(g7.begin()), end7_(g7.end()), current7_(current7),
+ begin8_(g8.begin()), end8_(g8.end()), current8_(current8),
+ begin9_(g9.begin()), end9_(g9.end()), current9_(current9) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current9_;
+ if (current9_ == end9_) {
+ current9_ = begin9_;
+ ++current8_;
+ }
+ if (current8_ == end8_) {
+ current8_ = begin8_;
+ ++current7_;
+ }
+ if (current7_ == end7_) {
+ current7_ = begin7_;
+ ++current6_;
+ }
+ if (current6_ == end6_) {
+ current6_ = begin6_;
+ ++current5_;
+ }
+ if (current5_ == end5_) {
+ current5_ = begin5_;
+ ++current4_;
+ }
+ if (current4_ == end4_) {
+ current4_ = begin4_;
+ ++current3_;
+ }
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return ¤t_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_ &&
+ current4_ == typed_other->current4_ &&
+ current5_ == typed_other->current5_ &&
+ current6_ == typed_other->current6_ &&
+ current7_ == typed_other->current7_ &&
+ current8_ == typed_other->current8_ &&
+ current9_ == typed_other->current9_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_),
+ begin4_(other.begin4_),
+ end4_(other.end4_),
+ current4_(other.current4_),
+ begin5_(other.begin5_),
+ end5_(other.end5_),
+ current5_(other.current5_),
+ begin6_(other.begin6_),
+ end6_(other.end6_),
+ current6_(other.current6_),
+ begin7_(other.begin7_),
+ end7_(other.end7_),
+ current7_(other.current7_),
+ begin8_(other.begin8_),
+ end8_(other.end8_),
+ current8_(other.current8_),
+ begin9_(other.begin9_),
+ end9_(other.end9_),
+ current9_(other.current9_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_,
+ *current4_, *current5_, *current6_, *current7_, *current8_,
+ *current9_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_ ||
+ current4_ == end4_ ||
+ current5_ == end5_ ||
+ current6_ == end6_ ||
+ current7_ == end7_ ||
+ current8_ == end8_ ||
+ current9_ == end9_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ const typename ParamGenerator<T4>::iterator begin4_;
+ const typename ParamGenerator<T4>::iterator end4_;
+ typename ParamGenerator<T4>::iterator current4_;
+ const typename ParamGenerator<T5>::iterator begin5_;
+ const typename ParamGenerator<T5>::iterator end5_;
+ typename ParamGenerator<T5>::iterator current5_;
+ const typename ParamGenerator<T6>::iterator begin6_;
+ const typename ParamGenerator<T6>::iterator end6_;
+ typename ParamGenerator<T6>::iterator current6_;
+ const typename ParamGenerator<T7>::iterator begin7_;
+ const typename ParamGenerator<T7>::iterator end7_;
+ typename ParamGenerator<T7>::iterator current7_;
+ const typename ParamGenerator<T8>::iterator begin8_;
+ const typename ParamGenerator<T8>::iterator end8_;
+ typename ParamGenerator<T8>::iterator current8_;
+ const typename ParamGenerator<T9>::iterator begin9_;
+ const typename ParamGenerator<T9>::iterator end9_;
+ typename ParamGenerator<T9>::iterator current9_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator9::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator9& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+ const ParamGenerator<T4> g4_;
+ const ParamGenerator<T5> g5_;
+ const ParamGenerator<T6> g6_;
+ const ParamGenerator<T7> g7_;
+ const ParamGenerator<T8> g8_;
+ const ParamGenerator<T9> g9_;
+}; // class CartesianProductGenerator9
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+class CartesianProductGenerator10
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ T7, T8, T9, T10> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ParamType;
+
+ CartesianProductGenerator10(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+ const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+ const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7,
+ const ParamGenerator<T8>& g8, const ParamGenerator<T9>& g9,
+ const ParamGenerator<T10>& g10)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
+ g9_(g9), g10_(g10) {}
+ virtual ~CartesianProductGenerator10() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
+ g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin(), g10_, g10_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+ g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_,
+ g8_.end(), g9_, g9_.end(), g10_, g10_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3,
+ const ParamGenerator<T4>& g4,
+ const typename ParamGenerator<T4>::iterator& current4,
+ const ParamGenerator<T5>& g5,
+ const typename ParamGenerator<T5>::iterator& current5,
+ const ParamGenerator<T6>& g6,
+ const typename ParamGenerator<T6>::iterator& current6,
+ const ParamGenerator<T7>& g7,
+ const typename ParamGenerator<T7>::iterator& current7,
+ const ParamGenerator<T8>& g8,
+ const typename ParamGenerator<T8>::iterator& current8,
+ const ParamGenerator<T9>& g9,
+ const typename ParamGenerator<T9>::iterator& current9,
+ const ParamGenerator<T10>& g10,
+ const typename ParamGenerator<T10>::iterator& current10)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+ begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+ begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+ begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
+ begin7_(g7.begin()), end7_(g7.end()), current7_(current7),
+ begin8_(g8.begin()), end8_(g8.end()), current8_(current8),
+ begin9_(g9.begin()), end9_(g9.end()), current9_(current9),
+ begin10_(g10.begin()), end10_(g10.end()), current10_(current10) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current10_;
+ if (current10_ == end10_) {
+ current10_ = begin10_;
+ ++current9_;
+ }
+ if (current9_ == end9_) {
+ current9_ = begin9_;
+ ++current8_;
+ }
+ if (current8_ == end8_) {
+ current8_ = begin8_;
+ ++current7_;
+ }
+ if (current7_ == end7_) {
+ current7_ = begin7_;
+ ++current6_;
+ }
+ if (current6_ == end6_) {
+ current6_ = begin6_;
+ ++current5_;
+ }
+ if (current5_ == end5_) {
+ current5_ = begin5_;
+ ++current4_;
+ }
+ if (current4_ == end4_) {
+ current4_ = begin4_;
+ ++current3_;
+ }
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return ¤t_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_ &&
+ current4_ == typed_other->current4_ &&
+ current5_ == typed_other->current5_ &&
+ current6_ == typed_other->current6_ &&
+ current7_ == typed_other->current7_ &&
+ current8_ == typed_other->current8_ &&
+ current9_ == typed_other->current9_ &&
+ current10_ == typed_other->current10_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_),
+ begin4_(other.begin4_),
+ end4_(other.end4_),
+ current4_(other.current4_),
+ begin5_(other.begin5_),
+ end5_(other.end5_),
+ current5_(other.current5_),
+ begin6_(other.begin6_),
+ end6_(other.end6_),
+ current6_(other.current6_),
+ begin7_(other.begin7_),
+ end7_(other.end7_),
+ current7_(other.current7_),
+ begin8_(other.begin8_),
+ end8_(other.end8_),
+ current8_(other.current8_),
+ begin9_(other.begin9_),
+ end9_(other.end9_),
+ current9_(other.current9_),
+ begin10_(other.begin10_),
+ end10_(other.end10_),
+ current10_(other.current10_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_,
+ *current4_, *current5_, *current6_, *current7_, *current8_,
+ *current9_, *current10_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_ ||
+ current4_ == end4_ ||
+ current5_ == end5_ ||
+ current6_ == end6_ ||
+ current7_ == end7_ ||
+ current8_ == end8_ ||
+ current9_ == end9_ ||
+ current10_ == end10_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ const typename ParamGenerator<T4>::iterator begin4_;
+ const typename ParamGenerator<T4>::iterator end4_;
+ typename ParamGenerator<T4>::iterator current4_;
+ const typename ParamGenerator<T5>::iterator begin5_;
+ const typename ParamGenerator<T5>::iterator end5_;
+ typename ParamGenerator<T5>::iterator current5_;
+ const typename ParamGenerator<T6>::iterator begin6_;
+ const typename ParamGenerator<T6>::iterator end6_;
+ typename ParamGenerator<T6>::iterator current6_;
+ const typename ParamGenerator<T7>::iterator begin7_;
+ const typename ParamGenerator<T7>::iterator end7_;
+ typename ParamGenerator<T7>::iterator current7_;
+ const typename ParamGenerator<T8>::iterator begin8_;
+ const typename ParamGenerator<T8>::iterator end8_;
+ typename ParamGenerator<T8>::iterator current8_;
+ const typename ParamGenerator<T9>::iterator begin9_;
+ const typename ParamGenerator<T9>::iterator end9_;
+ typename ParamGenerator<T9>::iterator current9_;
+ const typename ParamGenerator<T10>::iterator begin10_;
+ const typename ParamGenerator<T10>::iterator end10_;
+ typename ParamGenerator<T10>::iterator current10_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator10::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator10& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+ const ParamGenerator<T4> g4_;
+ const ParamGenerator<T5> g5_;
+ const ParamGenerator<T6> g6_;
+ const ParamGenerator<T7> g7_;
+ const ParamGenerator<T8> g8_;
+ const ParamGenerator<T9> g9_;
+ const ParamGenerator<T10> g10_;
+}; // class CartesianProductGenerator10
+
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Helper classes providing Combine() with polymorphic features. They allow
+// casting CartesianProductGeneratorN<T> to ParamGenerator<U> if T is
+// convertible to U.
+//
+template <class Generator1, class Generator2>
+class CartesianProductHolder2 {
+ public:
+CartesianProductHolder2(const Generator1& g1, const Generator2& g2)
+ : g1_(g1), g2_(g2) {}
+ template <typename T1, typename T2>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2> >(
+ new CartesianProductGenerator2<T1, T2>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder2& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+}; // class CartesianProductHolder2
+
+template <class Generator1, class Generator2, class Generator3>
+class CartesianProductHolder3 {
+ public:
+CartesianProductHolder3(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3)
+ : g1_(g1), g2_(g2), g3_(g3) {}
+ template <typename T1, typename T2, typename T3>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3> >(
+ new CartesianProductGenerator3<T1, T2, T3>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder3& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+}; // class CartesianProductHolder3
+
+template <class Generator1, class Generator2, class Generator3,
+ class Generator4>
+class CartesianProductHolder4 {
+ public:
+CartesianProductHolder4(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3, const Generator4& g4)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {}
+ template <typename T1, typename T2, typename T3, typename T4>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4> >(
+ new CartesianProductGenerator4<T1, T2, T3, T4>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_),
+ static_cast<ParamGenerator<T4> >(g4_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder4& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+ const Generator4 g4_;
+}; // class CartesianProductHolder4
+
+template <class Generator1, class Generator2, class Generator3,
+ class Generator4, class Generator5>
+class CartesianProductHolder5 {
+ public:
+CartesianProductHolder5(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3, const Generator4& g4, const Generator5& g5)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {}
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5> >(
+ new CartesianProductGenerator5<T1, T2, T3, T4, T5>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_),
+ static_cast<ParamGenerator<T4> >(g4_),
+ static_cast<ParamGenerator<T5> >(g5_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder5& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+ const Generator4 g4_;
+ const Generator5 g5_;
+}; // class CartesianProductHolder5
+
+template <class Generator1, class Generator2, class Generator3,
+ class Generator4, class Generator5, class Generator6>
+class CartesianProductHolder6 {
+ public:
+CartesianProductHolder6(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3, const Generator4& g4, const Generator5& g5,
+ const Generator6& g6)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {}
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> >(
+ new CartesianProductGenerator6<T1, T2, T3, T4, T5, T6>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_),
+ static_cast<ParamGenerator<T4> >(g4_),
+ static_cast<ParamGenerator<T5> >(g5_),
+ static_cast<ParamGenerator<T6> >(g6_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder6& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+ const Generator4 g4_;
+ const Generator5 g5_;
+ const Generator6 g6_;
+}; // class CartesianProductHolder6
+
+template <class Generator1, class Generator2, class Generator3,
+ class Generator4, class Generator5, class Generator6, class Generator7>
+class CartesianProductHolder7 {
+ public:
+CartesianProductHolder7(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3, const Generator4& g4, const Generator5& g5,
+ const Generator6& g6, const Generator7& g7)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {}
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ T7> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7> >(
+ new CartesianProductGenerator7<T1, T2, T3, T4, T5, T6, T7>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_),
+ static_cast<ParamGenerator<T4> >(g4_),
+ static_cast<ParamGenerator<T5> >(g5_),
+ static_cast<ParamGenerator<T6> >(g6_),
+ static_cast<ParamGenerator<T7> >(g7_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder7& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+ const Generator4 g4_;
+ const Generator5 g5_;
+ const Generator6 g6_;
+ const Generator7 g7_;
+}; // class CartesianProductHolder7
+
+template <class Generator1, class Generator2, class Generator3,
+ class Generator4, class Generator5, class Generator6, class Generator7,
+ class Generator8>
+class CartesianProductHolder8 {
+ public:
+CartesianProductHolder8(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3, const Generator4& g4, const Generator5& g5,
+ const Generator6& g6, const Generator7& g7, const Generator8& g8)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7),
+ g8_(g8) {}
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7,
+ T8> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8> >(
+ new CartesianProductGenerator8<T1, T2, T3, T4, T5, T6, T7, T8>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_),
+ static_cast<ParamGenerator<T4> >(g4_),
+ static_cast<ParamGenerator<T5> >(g5_),
+ static_cast<ParamGenerator<T6> >(g6_),
+ static_cast<ParamGenerator<T7> >(g7_),
+ static_cast<ParamGenerator<T8> >(g8_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder8& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+ const Generator4 g4_;
+ const Generator5 g5_;
+ const Generator6 g6_;
+ const Generator7 g7_;
+ const Generator8 g8_;
+}; // class CartesianProductHolder8
+
+template <class Generator1, class Generator2, class Generator3,
+ class Generator4, class Generator5, class Generator6, class Generator7,
+ class Generator8, class Generator9>
+class CartesianProductHolder9 {
+ public:
+CartesianProductHolder9(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3, const Generator4& g4, const Generator5& g5,
+ const Generator6& g6, const Generator7& g7, const Generator8& g8,
+ const Generator9& g9)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
+ g9_(g9) {}
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
+ T9> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
+ T9> >(
+ new CartesianProductGenerator9<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_),
+ static_cast<ParamGenerator<T4> >(g4_),
+ static_cast<ParamGenerator<T5> >(g5_),
+ static_cast<ParamGenerator<T6> >(g6_),
+ static_cast<ParamGenerator<T7> >(g7_),
+ static_cast<ParamGenerator<T8> >(g8_),
+ static_cast<ParamGenerator<T9> >(g9_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder9& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+ const Generator4 g4_;
+ const Generator5 g5_;
+ const Generator6 g6_;
+ const Generator7 g7_;
+ const Generator8 g8_;
+ const Generator9 g9_;
+}; // class CartesianProductHolder9
+
+template <class Generator1, class Generator2, class Generator3,
+ class Generator4, class Generator5, class Generator6, class Generator7,
+ class Generator8, class Generator9, class Generator10>
+class CartesianProductHolder10 {
+ public:
+CartesianProductHolder10(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3, const Generator4& g4, const Generator5& g5,
+ const Generator6& g6, const Generator7& g7, const Generator8& g8,
+ const Generator9& g9, const Generator10& g10)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
+ g9_(g9), g10_(g10) {}
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
+ T9, T10> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
+ T9, T10> >(
+ new CartesianProductGenerator10<T1, T2, T3, T4, T5, T6, T7, T8, T9,
+ T10>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_),
+ static_cast<ParamGenerator<T4> >(g4_),
+ static_cast<ParamGenerator<T5> >(g5_),
+ static_cast<ParamGenerator<T6> >(g6_),
+ static_cast<ParamGenerator<T7> >(g7_),
+ static_cast<ParamGenerator<T8> >(g8_),
+ static_cast<ParamGenerator<T9> >(g9_),
+ static_cast<ParamGenerator<T10> >(g10_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder10& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+ const Generator4 g4_;
+ const Generator5 g5_;
+ const Generator6 g6_;
+ const Generator7 g7_;
+ const Generator8 g8_;
+ const Generator9 g9_;
+ const Generator10 g10_;
+}; // class CartesianProductHolder10
+
+# endif // GTEST_HAS_COMBINE
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_HAS_PARAM_TEST
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
diff --git a/test/gtest/include/gtest/internal/gtest-param-util-generated.h.pump b/test/gtest/include/gtest/internal/gtest-param-util-generated.h.pump
new file mode 100644
index 0000000..009206f
--- /dev/null
+++ b/test/gtest/include/gtest/internal/gtest-param-util-generated.h.pump
@@ -0,0 +1,301 @@
+$$ -*- mode: c++; -*-
+$var n = 50 $$ Maximum length of Values arguments we want to support.
+$var maxtuple = 10 $$ Maximum number of Combine arguments we want to support.
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: vladl at google.com (Vlad Losev)
+
+// Type and function utilities for implementing parameterized tests.
+// This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
+//
+// Currently Google Test supports at most $n arguments in Values,
+// and at most $maxtuple arguments in Combine. Please contact
+// googletestframework at googlegroups.com if you need more.
+// Please note that the number of arguments to Combine is limited
+// by the maximum arity of the implementation of tr1::tuple which is
+// currently set at $maxtuple.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
+
+// scripts/fuse_gtest.py depends on gtest's own header being #included
+// *unconditionally*. Therefore these #includes cannot be moved
+// inside #if GTEST_HAS_PARAM_TEST.
+#include "gtest/internal/gtest-param-util.h"
+#include "gtest/internal/gtest-port.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace testing {
+
+// Forward declarations of ValuesIn(), which is implemented in
+// include/gtest/gtest-param-test.h.
+template <typename ForwardIterator>
+internal::ParamGenerator<
+ typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type>
+ValuesIn(ForwardIterator begin, ForwardIterator end);
+
+template <typename T, size_t N>
+internal::ParamGenerator<T> ValuesIn(const T (&array)[N]);
+
+template <class Container>
+internal::ParamGenerator<typename Container::value_type> ValuesIn(
+ const Container& container);
+
+namespace internal {
+
+// Used in the Values() function to provide polymorphic capabilities.
+template <typename T1>
+class ValueArray1 {
+ public:
+ explicit ValueArray1(T1 v1) : v1_(v1) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const { return ValuesIn(&v1_, &v1_ + 1); }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray1& other);
+
+ const T1 v1_;
+};
+
+$range i 2..n
+$for i [[
+$range j 1..i
+
+template <$for j, [[typename T$j]]>
+class ValueArray$i {
+ public:
+ ValueArray$i($for j, [[T$j v$j]]) : $for j, [[v$(j)_(v$j)]] {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {$for j, [[static_cast<T>(v$(j)_)]]};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray$i& other);
+
+$for j [[
+
+ const T$j v$(j)_;
+]]
+
+};
+
+]]
+
+# if GTEST_HAS_COMBINE
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Generates values from the Cartesian product of values produced
+// by the argument generators.
+//
+$range i 2..maxtuple
+$for i [[
+$range j 1..i
+$range k 2..i
+
+template <$for j, [[typename T$j]]>
+class CartesianProductGenerator$i
+ : public ParamGeneratorInterface< ::std::tr1::tuple<$for j, [[T$j]]> > {
+ public:
+ typedef ::std::tr1::tuple<$for j, [[T$j]]> ParamType;
+
+ CartesianProductGenerator$i($for j, [[const ParamGenerator<T$j>& g$j]])
+ : $for j, [[g$(j)_(g$j)]] {}
+ virtual ~CartesianProductGenerator$i() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, $for j, [[g$(j)_, g$(j)_.begin()]]);
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, $for j, [[g$(j)_, g$(j)_.end()]]);
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base, $for j, [[
+
+ const ParamGenerator<T$j>& g$j,
+ const typename ParamGenerator<T$j>::iterator& current$(j)]])
+ : base_(base),
+$for j, [[
+
+ begin$(j)_(g$j.begin()), end$(j)_(g$j.end()), current$(j)_(current$j)
+]] {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current$(i)_;
+
+$for k [[
+ if (current$(i+2-k)_ == end$(i+2-k)_) {
+ current$(i+2-k)_ = begin$(i+2-k)_;
+ ++current$(i+2-k-1)_;
+ }
+
+]]
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return ¤t_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ ($for j && [[
+
+ current$(j)_ == typed_other->current$(j)_
+]]);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_), $for j, [[
+
+ begin$(j)_(other.begin$(j)_),
+ end$(j)_(other.end$(j)_),
+ current$(j)_(other.current$(j)_)
+]] {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType($for j, [[*current$(j)_]]);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+$for j || [[
+
+ current$(j)_ == end$(j)_
+]];
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+$for j [[
+
+ const typename ParamGenerator<T$j>::iterator begin$(j)_;
+ const typename ParamGenerator<T$j>::iterator end$(j)_;
+ typename ParamGenerator<T$j>::iterator current$(j)_;
+]]
+
+ ParamType current_value_;
+ }; // class CartesianProductGenerator$i::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator$i& other);
+
+
+$for j [[
+ const ParamGenerator<T$j> g$(j)_;
+
+]]
+}; // class CartesianProductGenerator$i
+
+
+]]
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Helper classes providing Combine() with polymorphic features. They allow
+// casting CartesianProductGeneratorN<T> to ParamGenerator<U> if T is
+// convertible to U.
+//
+$range i 2..maxtuple
+$for i [[
+$range j 1..i
+
+template <$for j, [[class Generator$j]]>
+class CartesianProductHolder$i {
+ public:
+CartesianProductHolder$i($for j, [[const Generator$j& g$j]])
+ : $for j, [[g$(j)_(g$j)]] {}
+ template <$for j, [[typename T$j]]>
+ operator ParamGenerator< ::std::tr1::tuple<$for j, [[T$j]]> >() const {
+ return ParamGenerator< ::std::tr1::tuple<$for j, [[T$j]]> >(
+ new CartesianProductGenerator$i<$for j, [[T$j]]>(
+$for j,[[
+
+ static_cast<ParamGenerator<T$j> >(g$(j)_)
+]]));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder$i& other);
+
+
+$for j [[
+ const Generator$j g$(j)_;
+
+]]
+}; // class CartesianProductHolder$i
+
+]]
+
+# endif // GTEST_HAS_COMBINE
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_HAS_PARAM_TEST
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
diff --git a/test/gtest/include/gtest/internal/gtest-param-util.h b/test/gtest/include/gtest/internal/gtest-param-util.h
new file mode 100644
index 0000000..d5e1028
--- /dev/null
+++ b/test/gtest/include/gtest/internal/gtest-param-util.h
@@ -0,0 +1,619 @@
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: vladl at google.com (Vlad Losev)
+
+// Type and function utilities for implementing parameterized tests.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+
+#include <iterator>
+#include <utility>
+#include <vector>
+
+// scripts/fuse_gtest.py depends on gtest's own header being #included
+// *unconditionally*. Therefore these #includes cannot be moved
+// inside #if GTEST_HAS_PARAM_TEST.
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-linked_ptr.h"
+#include "gtest/internal/gtest-port.h"
+#include "gtest/gtest-printers.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace testing {
+namespace internal {
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Outputs a message explaining invalid registration of different
+// fixture class for the same test case. This may happen when
+// TEST_P macro is used to define two tests with the same name
+// but in different namespaces.
+GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name,
+ const char* file, int line);
+
+template <typename> class ParamGeneratorInterface;
+template <typename> class ParamGenerator;
+
+// Interface for iterating over elements provided by an implementation
+// of ParamGeneratorInterface<T>.
+template <typename T>
+class ParamIteratorInterface {
+ public:
+ virtual ~ParamIteratorInterface() {}
+ // A pointer to the base generator instance.
+ // Used only for the purposes of iterator comparison
+ // to make sure that two iterators belong to the same generator.
+ virtual const ParamGeneratorInterface<T>* BaseGenerator() const = 0;
+ // Advances iterator to point to the next element
+ // provided by the generator. The caller is responsible
+ // for not calling Advance() on an iterator equal to
+ // BaseGenerator()->End().
+ virtual void Advance() = 0;
+ // Clones the iterator object. Used for implementing copy semantics
+ // of ParamIterator<T>.
+ virtual ParamIteratorInterface* Clone() const = 0;
+ // Dereferences the current iterator and provides (read-only) access
+ // to the pointed value. It is the caller's responsibility not to call
+ // Current() on an iterator equal to BaseGenerator()->End().
+ // Used for implementing ParamGenerator<T>::operator*().
+ virtual const T* Current() const = 0;
+ // Determines whether the given iterator and other point to the same
+ // element in the sequence generated by the generator.
+ // Used for implementing ParamGenerator<T>::operator==().
+ virtual bool Equals(const ParamIteratorInterface& other) const = 0;
+};
+
+// Class iterating over elements provided by an implementation of
+// ParamGeneratorInterface<T>. It wraps ParamIteratorInterface<T>
+// and implements the const forward iterator concept.
+template <typename T>
+class ParamIterator {
+ public:
+ typedef T value_type;
+ typedef const T& reference;
+ typedef ptrdiff_t difference_type;
+
+ // ParamIterator assumes ownership of the impl_ pointer.
+ ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {}
+ ParamIterator& operator=(const ParamIterator& other) {
+ if (this != &other)
+ impl_.reset(other.impl_->Clone());
+ return *this;
+ }
+
+ const T& operator*() const { return *impl_->Current(); }
+ const T* operator->() const { return impl_->Current(); }
+ // Prefix version of operator++.
+ ParamIterator& operator++() {
+ impl_->Advance();
+ return *this;
+ }
+ // Postfix version of operator++.
+ ParamIterator operator++(int /*unused*/) {
+ ParamIteratorInterface<T>* clone = impl_->Clone();
+ impl_->Advance();
+ return ParamIterator(clone);
+ }
+ bool operator==(const ParamIterator& other) const {
+ return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_);
+ }
+ bool operator!=(const ParamIterator& other) const {
+ return !(*this == other);
+ }
+
+ private:
+ friend class ParamGenerator<T>;
+ explicit ParamIterator(ParamIteratorInterface<T>* impl) : impl_(impl) {}
+ scoped_ptr<ParamIteratorInterface<T> > impl_;
+};
+
+// ParamGeneratorInterface<T> is the binary interface to access generators
+// defined in other translation units.
+template <typename T>
+class ParamGeneratorInterface {
+ public:
+ typedef T ParamType;
+
+ virtual ~ParamGeneratorInterface() {}
+
+ // Generator interface definition
+ virtual ParamIteratorInterface<T>* Begin() const = 0;
+ virtual ParamIteratorInterface<T>* End() const = 0;
+};
+
+// Wraps ParamGeneratorInterface<T> and provides general generator syntax
+// compatible with the STL Container concept.
+// This class implements copy initialization semantics and the contained
+// ParamGeneratorInterface<T> instance is shared among all copies
+// of the original object. This is possible because that instance is immutable.
+template<typename T>
+class ParamGenerator {
+ public:
+ typedef ParamIterator<T> iterator;
+
+ explicit ParamGenerator(ParamGeneratorInterface<T>* impl) : impl_(impl) {}
+ ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {}
+
+ ParamGenerator& operator=(const ParamGenerator& other) {
+ impl_ = other.impl_;
+ return *this;
+ }
+
+ iterator begin() const { return iterator(impl_->Begin()); }
+ iterator end() const { return iterator(impl_->End()); }
+
+ private:
+ linked_ptr<const ParamGeneratorInterface<T> > impl_;
+};
+
+// Generates values from a range of two comparable values. Can be used to
+// generate sequences of user-defined types that implement operator+() and
+// operator<().
+// This class is used in the Range() function.
+template <typename T, typename IncrementT>
+class RangeGenerator : public ParamGeneratorInterface<T> {
+ public:
+ RangeGenerator(T begin, T end, IncrementT step)
+ : begin_(begin), end_(end),
+ step_(step), end_index_(CalculateEndIndex(begin, end, step)) {}
+ virtual ~RangeGenerator() {}
+
+ virtual ParamIteratorInterface<T>* Begin() const {
+ return new Iterator(this, begin_, 0, step_);
+ }
+ virtual ParamIteratorInterface<T>* End() const {
+ return new Iterator(this, end_, end_index_, step_);
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<T> {
+ public:
+ Iterator(const ParamGeneratorInterface<T>* base, T value, int index,
+ IncrementT step)
+ : base_(base), value_(value), index_(index), step_(step) {}
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<T>* BaseGenerator() const {
+ return base_;
+ }
+ virtual void Advance() {
+ value_ = value_ + step_;
+ index_++;
+ }
+ virtual ParamIteratorInterface<T>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const T* Current() const { return &value_; }
+ virtual bool Equals(const ParamIteratorInterface<T>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const int other_index =
+ CheckedDowncastToActualType<const Iterator>(&other)->index_;
+ return index_ == other_index;
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : ParamIteratorInterface<T>(),
+ base_(other.base_), value_(other.value_), index_(other.index_),
+ step_(other.step_) {}
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<T>* const base_;
+ T value_;
+ int index_;
+ const IncrementT step_;
+ }; // class RangeGenerator::Iterator
+
+ static int CalculateEndIndex(const T& begin,
+ const T& end,
+ const IncrementT& step) {
+ int end_index = 0;
+ for (T i = begin; i < end; i = i + step)
+ end_index++;
+ return end_index;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const RangeGenerator& other);
+
+ const T begin_;
+ const T end_;
+ const IncrementT step_;
+ // The index for the end() iterator. All the elements in the generated
+ // sequence are indexed (0-based) to aid iterator comparison.
+ const int end_index_;
+}; // class RangeGenerator
+
+
+// Generates values from a pair of STL-style iterators. Used in the
+// ValuesIn() function. The elements are copied from the source range
+// since the source can be located on the stack, and the generator
+// is likely to persist beyond that stack frame.
+template <typename T>
+class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
+ public:
+ template <typename ForwardIterator>
+ ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end)
+ : container_(begin, end) {}
+ virtual ~ValuesInIteratorRangeGenerator() {}
+
+ virtual ParamIteratorInterface<T>* Begin() const {
+ return new Iterator(this, container_.begin());
+ }
+ virtual ParamIteratorInterface<T>* End() const {
+ return new Iterator(this, container_.end());
+ }
+
+ private:
+ typedef typename ::std::vector<T> ContainerType;
+
+ class Iterator : public ParamIteratorInterface<T> {
+ public:
+ Iterator(const ParamGeneratorInterface<T>* base,
+ typename ContainerType::const_iterator iterator)
+ : base_(base), iterator_(iterator) {}
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<T>* BaseGenerator() const {
+ return base_;
+ }
+ virtual void Advance() {
+ ++iterator_;
+ value_.reset();
+ }
+ virtual ParamIteratorInterface<T>* Clone() const {
+ return new Iterator(*this);
+ }
+ // We need to use cached value referenced by iterator_ because *iterator_
+ // can return a temporary object (and of type other then T), so just
+ // having "return &*iterator_;" doesn't work.
+ // value_ is updated here and not in Advance() because Advance()
+ // can advance iterator_ beyond the end of the range, and we cannot
+ // detect that fact. The client code, on the other hand, is
+ // responsible for not calling Current() on an out-of-range iterator.
+ virtual const T* Current() const {
+ if (value_.get() == NULL)
+ value_.reset(new T(*iterator_));
+ return value_.get();
+ }
+ virtual bool Equals(const ParamIteratorInterface<T>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ return iterator_ ==
+ CheckedDowncastToActualType<const Iterator>(&other)->iterator_;
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ // The explicit constructor call suppresses a false warning
+ // emitted by gcc when supplied with the -Wextra option.
+ : ParamIteratorInterface<T>(),
+ base_(other.base_),
+ iterator_(other.iterator_) {}
+
+ const ParamGeneratorInterface<T>* const base_;
+ typename ContainerType::const_iterator iterator_;
+ // A cached value of *iterator_. We keep it here to allow access by
+ // pointer in the wrapping iterator's operator->().
+ // value_ needs to be mutable to be accessed in Current().
+ // Use of scoped_ptr helps manage cached value's lifetime,
+ // which is bound by the lifespan of the iterator itself.
+ mutable scoped_ptr<const T> value_;
+ }; // class ValuesInIteratorRangeGenerator::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const ValuesInIteratorRangeGenerator& other);
+
+ const ContainerType container_;
+}; // class ValuesInIteratorRangeGenerator
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Stores a parameter value and later creates tests parameterized with that
+// value.
+template <class TestClass>
+class ParameterizedTestFactory : public TestFactoryBase {
+ public:
+ typedef typename TestClass::ParamType ParamType;
+ explicit ParameterizedTestFactory(ParamType parameter) :
+ parameter_(parameter) {}
+ virtual Test* CreateTest() {
+ TestClass::SetParam(¶meter_);
+ return new TestClass();
+ }
+
+ private:
+ const ParamType parameter_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory);
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// TestMetaFactoryBase is a base class for meta-factories that create
+// test factories for passing into MakeAndRegisterTestInfo function.
+template <class ParamType>
+class TestMetaFactoryBase {
+ public:
+ virtual ~TestMetaFactoryBase() {}
+
+ virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0;
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// TestMetaFactory creates test factories for passing into
+// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives
+// ownership of test factory pointer, same factory object cannot be passed
+// into that method twice. But ParameterizedTestCaseInfo is going to call
+// it for each Test/Parameter value combination. Thus it needs meta factory
+// creator class.
+template <class TestCase>
+class TestMetaFactory
+ : public TestMetaFactoryBase<typename TestCase::ParamType> {
+ public:
+ typedef typename TestCase::ParamType ParamType;
+
+ TestMetaFactory() {}
+
+ virtual TestFactoryBase* CreateTestFactory(ParamType parameter) {
+ return new ParameterizedTestFactory<TestCase>(parameter);
+ }
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory);
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// ParameterizedTestCaseInfoBase is a generic interface
+// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase
+// accumulates test information provided by TEST_P macro invocations
+// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations
+// and uses that information to register all resulting test instances
+// in RegisterTests method. The ParameterizeTestCaseRegistry class holds
+// a collection of pointers to the ParameterizedTestCaseInfo objects
+// and calls RegisterTests() on each of them when asked.
+class ParameterizedTestCaseInfoBase {
+ public:
+ virtual ~ParameterizedTestCaseInfoBase() {}
+
+ // Base part of test case name for display purposes.
+ virtual const string& GetTestCaseName() const = 0;
+ // Test case id to verify identity.
+ virtual TypeId GetTestCaseTypeId() const = 0;
+ // UnitTest class invokes this method to register tests in this
+ // test case right before running them in RUN_ALL_TESTS macro.
+ // This method should not be called more then once on any single
+ // instance of a ParameterizedTestCaseInfoBase derived class.
+ virtual void RegisterTests() = 0;
+
+ protected:
+ ParameterizedTestCaseInfoBase() {}
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase);
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P
+// macro invocations for a particular test case and generators
+// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that
+// test case. It registers tests with all values generated by all
+// generators when asked.
+template <class TestCase>
+class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
+ public:
+ // ParamType and GeneratorCreationFunc are private types but are required
+ // for declarations of public methods AddTestPattern() and
+ // AddTestCaseInstantiation().
+ typedef typename TestCase::ParamType ParamType;
+ // A function that returns an instance of appropriate generator type.
+ typedef ParamGenerator<ParamType>(GeneratorCreationFunc)();
+
+ explicit ParameterizedTestCaseInfo(const char* name)
+ : test_case_name_(name) {}
+
+ // Test case base name for display purposes.
+ virtual const string& GetTestCaseName() const { return test_case_name_; }
+ // Test case id to verify identity.
+ virtual TypeId GetTestCaseTypeId() const { return GetTypeId<TestCase>(); }
+ // TEST_P macro uses AddTestPattern() to record information
+ // about a single test in a LocalTestInfo structure.
+ // test_case_name is the base name of the test case (without invocation
+ // prefix). test_base_name is the name of an individual test without
+ // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is
+ // test case base name and DoBar is test base name.
+ void AddTestPattern(const char* test_case_name,
+ const char* test_base_name,
+ TestMetaFactoryBase<ParamType>* meta_factory) {
+ tests_.push_back(linked_ptr<TestInfo>(new TestInfo(test_case_name,
+ test_base_name,
+ meta_factory)));
+ }
+ // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information
+ // about a generator.
+ int AddTestCaseInstantiation(const string& instantiation_name,
+ GeneratorCreationFunc* func,
+ const char* /* file */,
+ int /* line */) {
+ instantiations_.push_back(::std::make_pair(instantiation_name, func));
+ return 0; // Return value used only to run this method in namespace scope.
+ }
+ // UnitTest class invokes this method to register tests in this test case
+ // test cases right before running tests in RUN_ALL_TESTS macro.
+ // This method should not be called more then once on any single
+ // instance of a ParameterizedTestCaseInfoBase derived class.
+ // UnitTest has a guard to prevent from calling this method more then once.
+ virtual void RegisterTests() {
+ for (typename TestInfoContainer::iterator test_it = tests_.begin();
+ test_it != tests_.end(); ++test_it) {
+ linked_ptr<TestInfo> test_info = *test_it;
+ for (typename InstantiationContainer::iterator gen_it =
+ instantiations_.begin(); gen_it != instantiations_.end();
+ ++gen_it) {
+ const string& instantiation_name = gen_it->first;
+ ParamGenerator<ParamType> generator((*gen_it->second)());
+
+ string test_case_name;
+ if ( !instantiation_name.empty() )
+ test_case_name = instantiation_name + "/";
+ test_case_name += test_info->test_case_base_name;
+
+ int i = 0;
+ for (typename ParamGenerator<ParamType>::iterator param_it =
+ generator.begin();
+ param_it != generator.end(); ++param_it, ++i) {
+ Message test_name_stream;
+ test_name_stream << test_info->test_base_name << "/" << i;
+ MakeAndRegisterTestInfo(
+ test_case_name.c_str(),
+ test_name_stream.GetString().c_str(),
+ NULL, // No type parameter.
+ PrintToString(*param_it).c_str(),
+ GetTestCaseTypeId(),
+ TestCase::SetUpTestCase,
+ TestCase::TearDownTestCase,
+ test_info->test_meta_factory->CreateTestFactory(*param_it));
+ } // for param_it
+ } // for gen_it
+ } // for test_it
+ } // RegisterTests
+
+ private:
+ // LocalTestInfo structure keeps information about a single test registered
+ // with TEST_P macro.
+ struct TestInfo {
+ TestInfo(const char* a_test_case_base_name,
+ const char* a_test_base_name,
+ TestMetaFactoryBase<ParamType>* a_test_meta_factory) :
+ test_case_base_name(a_test_case_base_name),
+ test_base_name(a_test_base_name),
+ test_meta_factory(a_test_meta_factory) {}
+
+ const string test_case_base_name;
+ const string test_base_name;
+ const scoped_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory;
+ };
+ typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer;
+ // Keeps pairs of <Instantiation name, Sequence generator creation function>
+ // received from INSTANTIATE_TEST_CASE_P macros.
+ typedef ::std::vector<std::pair<string, GeneratorCreationFunc*> >
+ InstantiationContainer;
+
+ const string test_case_name_;
+ TestInfoContainer tests_;
+ InstantiationContainer instantiations_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo);
+}; // class ParameterizedTestCaseInfo
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase
+// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P
+// macros use it to locate their corresponding ParameterizedTestCaseInfo
+// descriptors.
+class ParameterizedTestCaseRegistry {
+ public:
+ ParameterizedTestCaseRegistry() {}
+ ~ParameterizedTestCaseRegistry() {
+ for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
+ it != test_case_infos_.end(); ++it) {
+ delete *it;
+ }
+ }
+
+ // Looks up or creates and returns a structure containing information about
+ // tests and instantiations of a particular test case.
+ template <class TestCase>
+ ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder(
+ const char* test_case_name,
+ const char* file,
+ int line) {
+ ParameterizedTestCaseInfo<TestCase>* typed_test_info = NULL;
+ for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
+ it != test_case_infos_.end(); ++it) {
+ if ((*it)->GetTestCaseName() == test_case_name) {
+ if ((*it)->GetTestCaseTypeId() != GetTypeId<TestCase>()) {
+ // Complain about incorrect usage of Google Test facilities
+ // and terminate the program since we cannot guaranty correct
+ // test case setup and tear-down in this case.
+ ReportInvalidTestCaseType(test_case_name, file, line);
+ posix::Abort();
+ } else {
+ // At this point we are sure that the object we found is of the same
+ // type we are looking for, so we downcast it to that type
+ // without further checks.
+ typed_test_info = CheckedDowncastToActualType<
+ ParameterizedTestCaseInfo<TestCase> >(*it);
+ }
+ break;
+ }
+ }
+ if (typed_test_info == NULL) {
+ typed_test_info = new ParameterizedTestCaseInfo<TestCase>(test_case_name);
+ test_case_infos_.push_back(typed_test_info);
+ }
+ return typed_test_info;
+ }
+ void RegisterTests() {
+ for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
+ it != test_case_infos_.end(); ++it) {
+ (*it)->RegisterTests();
+ }
+ }
+
+ private:
+ typedef ::std::vector<ParameterizedTestCaseInfoBase*> TestCaseInfoContainer;
+
+ TestCaseInfoContainer test_case_infos_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry);
+};
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_HAS_PARAM_TEST
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
diff --git a/test/gtest/include/gtest/internal/gtest-port.h b/test/gtest/include/gtest/internal/gtest-port.h
new file mode 100644
index 0000000..bc46459
--- /dev/null
+++ b/test/gtest/include/gtest/internal/gtest-port.h
@@ -0,0 +1,1950 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: wan at google.com (Zhanyong Wan)
+//
+// Low-level types and utilities for porting Google Test to various
+// platforms. They are subject to change without notice. DO NOT USE
+// THEM IN USER CODE.
+//
+// This file is fundamental to Google Test. All other Google Test source
+// files are expected to #include this. Therefore, it cannot #include
+// any other Google Test header.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
+#ifndef _WIN32
+#pragma GCC system_header
+#endif
+
+// The user can define the following macros in the build script to
+// control Google Test's behavior. If the user doesn't define a macro
+// in this list, Google Test will define it.
+//
+// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2)
+// is/isn't available.
+// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions
+// are enabled.
+// GTEST_HAS_GLOBAL_STRING - Define it to 1/0 to indicate that ::string
+// is/isn't available (some systems define
+// ::string, which is different to std::string).
+// GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string
+// is/isn't available (some systems define
+// ::wstring, which is different to std::wstring).
+// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular
+// expressions are/aren't available.
+// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that <pthread.h>
+// is/isn't available.
+// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't
+// enabled.
+// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that
+// std::wstring does/doesn't work (Google Test can
+// be used where std::wstring is unavailable).
+// GTEST_HAS_TR1_TUPLE - Define it to 1/0 to indicate tr1::tuple
+// is/isn't available.
+// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the
+// compiler supports Microsoft's "Structured
+// Exception Handling".
+// GTEST_HAS_STREAM_REDIRECTION
+// - Define it to 1/0 to indicate whether the
+// platform supports I/O stream redirection using
+// dup() and dup2().
+// GTEST_USE_OWN_TR1_TUPLE - Define it to 1/0 to indicate whether Google
+// Test's own tr1 tuple implementation should be
+// used. Unused when the user sets
+// GTEST_HAS_TR1_TUPLE to 0.
+// GTEST_LANG_CXX11 - Define it to 1/0 to indicate that Google Test
+// is building in C++11/C++98 mode.
+// GTEST_LINKED_AS_SHARED_LIBRARY
+// - Define to 1 when compiling tests that use
+// Google Test as a shared library (known as
+// DLL on Windows).
+// GTEST_CREATE_SHARED_LIBRARY
+// - Define to 1 when compiling Google Test itself
+// as a shared library.
+
+// This header defines the following utilities:
+//
+// Macros indicating the current platform (defined to 1 if compiled on
+// the given platform; otherwise undefined):
+// GTEST_OS_AIX - IBM AIX
+// GTEST_OS_CYGWIN - Cygwin
+// GTEST_OS_HPUX - HP-UX
+// GTEST_OS_LINUX - Linux
+// GTEST_OS_LINUX_ANDROID - Google Android
+// GTEST_OS_MAC - Mac OS X
+// GTEST_OS_IOS - iOS
+// GTEST_OS_IOS_SIMULATOR - iOS simulator
+// GTEST_OS_NACL - Google Native Client (NaCl)
+// GTEST_OS_OPENBSD - OpenBSD
+// GTEST_OS_QNX - QNX
+// GTEST_OS_SOLARIS - Sun Solaris
+// GTEST_OS_SYMBIAN - Symbian
+// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile)
+// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop
+// GTEST_OS_WINDOWS_MINGW - MinGW
+// GTEST_OS_WINDOWS_MOBILE - Windows Mobile
+// GTEST_OS_ZOS - z/OS
+//
+// Among the platforms, Cygwin, Linux, Max OS X, and Windows have the
+// most stable support. Since core members of the Google Test project
+// don't have access to other platforms, support for them may be less
+// stable. If you notice any problems on your platform, please notify
+// googletestframework at googlegroups.com (patches for fixing them are
+// even more welcome!).
+//
+// Note that it is possible that none of the GTEST_OS_* macros are defined.
+//
+// Macros indicating available Google Test features (defined to 1 if
+// the corresponding feature is supported; otherwise undefined):
+// GTEST_HAS_COMBINE - the Combine() function (for value-parameterized
+// tests)
+// GTEST_HAS_DEATH_TEST - death tests
+// GTEST_HAS_PARAM_TEST - value-parameterized tests
+// GTEST_HAS_TYPED_TEST - typed tests
+// GTEST_HAS_TYPED_TEST_P - type-parameterized tests
+// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with
+// GTEST_HAS_POSIX_RE (see above) which users can
+// define themselves.
+// GTEST_USES_SIMPLE_RE - our own simple regex is used;
+// the above two are mutually exclusive.
+// GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ().
+//
+// Macros for basic C++ coding:
+// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning.
+// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a
+// variable don't have to be used.
+// GTEST_DISALLOW_ASSIGN_ - disables operator=.
+// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=.
+// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used.
+//
+// Synchronization:
+// Mutex, MutexLock, ThreadLocal, GetThreadCount()
+// - synchronization primitives.
+// GTEST_IS_THREADSAFE - defined to 1 to indicate that the above
+// synchronization primitives have real implementations
+// and Google Test is thread-safe; or 0 otherwise.
+//
+// Template meta programming:
+// is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only.
+// IteratorTraits - partial implementation of std::iterator_traits, which
+// is not available in libCstd when compiled with Sun C++.
+//
+// Smart pointers:
+// scoped_ptr - as in TR2.
+//
+// Regular expressions:
+// RE - a simple regular expression class using the POSIX
+// Extended Regular Expression syntax on UNIX-like
+// platforms, or a reduced regular exception syntax on
+// other platforms, including Windows.
+//
+// Logging:
+// GTEST_LOG_() - logs messages at the specified severity level.
+// LogToStderr() - directs all log messages to stderr.
+// FlushInfoLog() - flushes informational log messages.
+//
+// Stdout and stderr capturing:
+// CaptureStdout() - starts capturing stdout.
+// GetCapturedStdout() - stops capturing stdout and returns the captured
+// string.
+// CaptureStderr() - starts capturing stderr.
+// GetCapturedStderr() - stops capturing stderr and returns the captured
+// string.
+//
+// Integer types:
+// TypeWithSize - maps an integer to a int type.
+// Int32, UInt32, Int64, UInt64, TimeInMillis
+// - integers of known sizes.
+// BiggestInt - the biggest signed integer type.
+//
+// Command-line utilities:
+// GTEST_FLAG() - references a flag.
+// GTEST_DECLARE_*() - declares a flag.
+// GTEST_DEFINE_*() - defines a flag.
+// GetInjectableArgvs() - returns the command line as a vector of strings.
+//
+// Environment variable utilities:
+// GetEnv() - gets the value of an environment variable.
+// BoolFromGTestEnv() - parses a bool environment variable.
+// Int32FromGTestEnv() - parses an Int32 environment variable.
+// StringFromGTestEnv() - parses a string environment variable.
+
+#include <ctype.h> // for isspace, etc
+#include <stddef.h> // for ptrdiff_t
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifndef _WIN32_WCE
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif // !_WIN32_WCE
+
+#if defined __APPLE__
+# include <AvailabilityMacros.h>
+# include <TargetConditionals.h>
+#endif
+
+#include <iostream> // NOLINT
+#include <sstream> // NOLINT
+#include <string> // NOLINT
+
+#define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com"
+#define GTEST_FLAG_PREFIX_ "gtest_"
+#define GTEST_FLAG_PREFIX_DASH_ "gtest-"
+#define GTEST_FLAG_PREFIX_UPPER_ "GTEST_"
+#define GTEST_NAME_ "Google Test"
+#define GTEST_PROJECT_URL_ "http://code.google.com/p/googletest/"
+
+// Determines the version of gcc that is used to compile this.
+#ifdef __GNUC__
+// 40302 means version 4.3.2.
+# define GTEST_GCC_VER_ \
+ (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__)
+#endif // __GNUC__
+
+// Determines the platform on which Google Test is compiled.
+#ifdef __CYGWIN__
+# define GTEST_OS_CYGWIN 1
+#elif defined __SYMBIAN32__
+# define GTEST_OS_SYMBIAN 1
+#elif defined _WIN32
+# define GTEST_OS_WINDOWS 1
+# ifdef _WIN32_WCE
+# define GTEST_OS_WINDOWS_MOBILE 1
+# elif defined(__MINGW__) || defined(__MINGW32__)
+# define GTEST_OS_WINDOWS_MINGW 1
+# else
+# define GTEST_OS_WINDOWS_DESKTOP 1
+# endif // _WIN32_WCE
+#elif defined __APPLE__
+# define GTEST_OS_MAC 1
+# if TARGET_OS_IPHONE
+# define GTEST_OS_IOS 1
+# if TARGET_IPHONE_SIMULATOR
+# define GTEST_OS_IOS_SIMULATOR 1
+# endif
+# endif
+#elif defined __linux__
+# define GTEST_OS_LINUX 1
+# if defined __ANDROID__
+# define GTEST_OS_LINUX_ANDROID 1
+# endif
+#elif defined __MVS__
+# define GTEST_OS_ZOS 1
+#elif defined(__sun) && defined(__SVR4)
+# define GTEST_OS_SOLARIS 1
+#elif defined(_AIX)
+# define GTEST_OS_AIX 1
+#elif defined(__hpux)
+# define GTEST_OS_HPUX 1
+#elif defined __native_client__
+# define GTEST_OS_NACL 1
+#elif defined __OpenBSD__
+# define GTEST_OS_OPENBSD 1
+#elif defined __QNX__
+# define GTEST_OS_QNX 1
+#endif // __CYGWIN__
+
+#ifndef GTEST_LANG_CXX11
+// gcc and clang define __GXX_EXPERIMENTAL_CXX0X__ when
+// -std={c,gnu}++{0x,11} is passed. The C++11 standard specifies a
+// value for __cplusplus, and recent versions of clang, gcc, and
+// probably other compilers set that too in C++11 mode.
+# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L
+// Compiling in at least C++11 mode.
+# define GTEST_LANG_CXX11 1
+# else
+# define GTEST_LANG_CXX11 0
+# endif
+#endif
+
+// Brings in definitions for functions used in the testing::internal::posix
+// namespace (read, write, close, chdir, isatty, stat). We do not currently
+// use them on Windows Mobile.
+#if !GTEST_OS_WINDOWS
+// This assumes that non-Windows OSes provide unistd.h. For OSes where this
+// is not the case, we need to include headers that provide the functions
+// mentioned above.
+# include <unistd.h>
+# include <strings.h>
+#elif !GTEST_OS_WINDOWS_MOBILE
+# include <direct.h>
+# include <io.h>
+#endif
+
+#if GTEST_OS_LINUX_ANDROID
+// Used to define __ANDROID_API__ matching the target NDK API level.
+# include <android/api-level.h> // NOLINT
+#endif
+
+// Defines this to true iff Google Test can use POSIX regular expressions.
+#ifndef GTEST_HAS_POSIX_RE
+# if GTEST_OS_LINUX_ANDROID
+// On Android, <regex.h> is only available starting with Gingerbread.
+# define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9)
+# else
+# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS)
+# endif
+#endif
+
+#if GTEST_HAS_POSIX_RE
+
+// On some platforms, <regex.h> needs someone to define size_t, and
+// won't compile otherwise. We can #include it here as we already
+// included <stdlib.h>, which is guaranteed to define size_t through
+// <stddef.h>.
+# include <regex.h> // NOLINT
+
+# define GTEST_USES_POSIX_RE 1
+
+#elif GTEST_OS_WINDOWS
+
+// <regex.h> is not available on Windows. Use our own simple regex
+// implementation instead.
+# define GTEST_USES_SIMPLE_RE 1
+
+#else
+
+// <regex.h> may not be available on this platform. Use our own
+// simple regex implementation instead.
+# define GTEST_USES_SIMPLE_RE 1
+
+#endif // GTEST_HAS_POSIX_RE
+
+#ifndef GTEST_HAS_EXCEPTIONS
+// The user didn't tell us whether exceptions are enabled, so we need
+// to figure it out.
+# if defined(_MSC_VER) || defined(__BORLANDC__)
+// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS
+// macro to enable exceptions, so we'll do the same.
+// Assumes that exceptions are enabled by default.
+# ifndef _HAS_EXCEPTIONS
+# define _HAS_EXCEPTIONS 1
+# endif // _HAS_EXCEPTIONS
+# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS
+# elif defined(__GNUC__) && __EXCEPTIONS
+// gcc defines __EXCEPTIONS to 1 iff exceptions are enabled.
+# define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__SUNPRO_CC)
+// Sun Pro CC supports exceptions. However, there is no compile-time way of
+// detecting whether they are enabled or not. Therefore, we assume that
+// they are enabled unless the user tells us otherwise.
+# define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__IBMCPP__) && __EXCEPTIONS
+// xlC defines __EXCEPTIONS to 1 iff exceptions are enabled.
+# define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__HP_aCC)
+// Exception handling is in effect by default in HP aCC compiler. It has to
+// be turned of by +noeh compiler option if desired.
+# define GTEST_HAS_EXCEPTIONS 1
+# else
+// For other compilers, we assume exceptions are disabled to be
+// conservative.
+# define GTEST_HAS_EXCEPTIONS 0
+# endif // defined(_MSC_VER) || defined(__BORLANDC__)
+#endif // GTEST_HAS_EXCEPTIONS
+
+#if !defined(GTEST_HAS_STD_STRING)
+// Even though we don't use this macro any longer, we keep it in case
+// some clients still depend on it.
+# define GTEST_HAS_STD_STRING 1
+#elif !GTEST_HAS_STD_STRING
+// The user told us that ::std::string isn't available.
+# error "Google Test cannot be used where ::std::string isn't available."
+#endif // !defined(GTEST_HAS_STD_STRING)
+
+#ifndef GTEST_HAS_GLOBAL_STRING
+// The user didn't tell us whether ::string is available, so we need
+// to figure it out.
+
+# define GTEST_HAS_GLOBAL_STRING 0
+
+#endif // GTEST_HAS_GLOBAL_STRING
+
+#ifndef GTEST_HAS_STD_WSTRING
+// The user didn't tell us whether ::std::wstring is available, so we need
+// to figure it out.
+// TODO(wan at google.com): uses autoconf to detect whether ::std::wstring
+// is available.
+
+// Cygwin 1.7 and below doesn't support ::std::wstring.
+// Solaris' libc++ doesn't support it either. Android has
+// no support for it at least as recent as Froyo (2.2).
+# define GTEST_HAS_STD_WSTRING \
+ (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS))
+
+#endif // GTEST_HAS_STD_WSTRING
+
+#ifndef GTEST_HAS_GLOBAL_WSTRING
+// The user didn't tell us whether ::wstring is available, so we need
+// to figure it out.
+# define GTEST_HAS_GLOBAL_WSTRING \
+ (GTEST_HAS_STD_WSTRING && GTEST_HAS_GLOBAL_STRING)
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+// Determines whether RTTI is available.
+#ifndef GTEST_HAS_RTTI
+// The user didn't tell us whether RTTI is enabled, so we need to
+// figure it out.
+
+# ifdef _MSC_VER
+
+# ifdef _CPPRTTI // MSVC defines this macro iff RTTI is enabled.
+# define GTEST_HAS_RTTI 1
+# else
+# define GTEST_HAS_RTTI 0
+# endif
+
+// Starting with version 4.3.2, gcc defines __GXX_RTTI iff RTTI is enabled.
+# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302)
+
+# ifdef __GXX_RTTI
+// When building against STLport with the Android NDK and with
+// -frtti -fno-exceptions, the build fails at link time with undefined
+// references to __cxa_bad_typeid. Note sure if STL or toolchain bug,
+// so disable RTTI when detected.
+# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && \
+ !defined(__EXCEPTIONS)
+# define GTEST_HAS_RTTI 0
+# else
+# define GTEST_HAS_RTTI 1
+# endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS
+# else
+# define GTEST_HAS_RTTI 0
+# endif // __GXX_RTTI
+
+// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends
+// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the
+// first version with C++ support.
+# elif defined(__clang__)
+
+# define GTEST_HAS_RTTI __has_feature(cxx_rtti)
+
+// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if
+// both the typeid and dynamic_cast features are present.
+# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900)
+
+# ifdef __RTTI_ALL__
+# define GTEST_HAS_RTTI 1
+# else
+# define GTEST_HAS_RTTI 0
+# endif
+
+# else
+
+// For all other compilers, we assume RTTI is enabled.
+# define GTEST_HAS_RTTI 1
+
+# endif // _MSC_VER
+
+#endif // GTEST_HAS_RTTI
+
+// It's this header's responsibility to #include <typeinfo> when RTTI
+// is enabled.
+#if GTEST_HAS_RTTI
+# include <typeinfo>
+#endif
+
+// Determines whether Google Test can use the pthreads library.
+#ifndef GTEST_HAS_PTHREAD
+// The user didn't tell us explicitly, so we assume pthreads support is
+// available on Linux and Mac.
+//
+// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0
+// to your compiler flags.
+# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX \
+ || GTEST_OS_QNX)
+#endif // GTEST_HAS_PTHREAD
+
+#if GTEST_HAS_PTHREAD
+// gtest-port.h guarantees to #include <pthread.h> when GTEST_HAS_PTHREAD is
+// true.
+# include <pthread.h> // NOLINT
+
+// For timespec and nanosleep, used below.
+# include <time.h> // NOLINT
+#endif
+
+// Determines whether Google Test can use tr1/tuple. You can define
+// this macro to 0 to prevent Google Test from using tuple (any
+// feature depending on tuple with be disabled in this mode).
+#ifndef GTEST_HAS_TR1_TUPLE
+# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR)
+// STLport, provided with the Android NDK, has neither <tr1/tuple> or <tuple>.
+# define GTEST_HAS_TR1_TUPLE 0
+# else
+// The user didn't tell us not to do it, so we assume it's OK.
+# define GTEST_HAS_TR1_TUPLE 1
+# endif
+#endif // GTEST_HAS_TR1_TUPLE
+
+// Determines whether Google Test's own tr1 tuple implementation
+// should be used.
+#ifndef GTEST_USE_OWN_TR1_TUPLE
+// The user didn't tell us, so we need to figure it out.
+
+// We use our own TR1 tuple if we aren't sure the user has an
+// implementation of it already. At this time, libstdc++ 4.0.0+ and
+// MSVC 2010 are the only mainstream standard libraries that come
+// with a TR1 tuple implementation. NVIDIA's CUDA NVCC compiler
+// pretends to be GCC by defining __GNUC__ and friends, but cannot
+// compile GCC's tuple implementation. MSVC 2008 (9.0) provides TR1
+// tuple in a 323 MB Feature Pack download, which we cannot assume the
+// user has. QNX's QCC compiler is a modified GCC but it doesn't
+// support TR1 tuple. libc++ only provides std::tuple, in C++11 mode,
+// and it can be used with some compilers that define __GNUC__.
+# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \
+ && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) || _MSC_VER >= 1600
+# define GTEST_ENV_HAS_TR1_TUPLE_ 1
+# endif
+
+// C++11 specifies that <tuple> provides std::tuple. Use that if gtest is used
+// in C++11 mode and libstdc++ isn't very old (binaries targeting OS X 10.6
+// can build with clang but need to use gcc4.2's libstdc++).
+# if GTEST_LANG_CXX11 && (!defined(__GLIBCXX__) || __GLIBCXX__ > 20110325)
+# define GTEST_ENV_HAS_STD_TUPLE_ 1
+# endif
+
+# if GTEST_ENV_HAS_TR1_TUPLE_ || GTEST_ENV_HAS_STD_TUPLE_
+# define GTEST_USE_OWN_TR1_TUPLE 0
+# else
+# define GTEST_USE_OWN_TR1_TUPLE 1
+# endif
+
+#endif // GTEST_USE_OWN_TR1_TUPLE
+
+// To avoid conditional compilation everywhere, we make it
+// gtest-port.h's responsibility to #include the header implementing
+// tr1/tuple.
+#if GTEST_HAS_TR1_TUPLE
+
+# if GTEST_USE_OWN_TR1_TUPLE
+# include "gtest/internal/gtest-tuple.h"
+# elif GTEST_ENV_HAS_STD_TUPLE_
+# include <tuple>
+// C++11 puts its tuple into the ::std namespace rather than
+// ::std::tr1. gtest expects tuple to live in ::std::tr1, so put it there.
+// This causes undefined behavior, but supported compilers react in
+// the way we intend.
+namespace std {
+namespace tr1 {
+using ::std::get;
+using ::std::make_tuple;
+using ::std::tuple;
+using ::std::tuple_element;
+using ::std::tuple_size;
+}
+}
+
+# elif GTEST_OS_SYMBIAN
+
+// On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to
+// use STLport's tuple implementation, which unfortunately doesn't
+// work as the copy of STLport distributed with Symbian is incomplete.
+// By making sure BOOST_HAS_TR1_TUPLE is undefined, we force Boost to
+// use its own tuple implementation.
+# ifdef BOOST_HAS_TR1_TUPLE
+# undef BOOST_HAS_TR1_TUPLE
+# endif // BOOST_HAS_TR1_TUPLE
+
+// This prevents <boost/tr1/detail/config.hpp>, which defines
+// BOOST_HAS_TR1_TUPLE, from being #included by Boost's <tuple>.
+# define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED
+# include <tuple>
+
+# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000)
+// GCC 4.0+ implements tr1/tuple in the <tr1/tuple> header. This does
+// not conform to the TR1 spec, which requires the header to be <tuple>.
+
+# if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302
+// Until version 4.3.2, gcc has a bug that causes <tr1/functional>,
+// which is #included by <tr1/tuple>, to not compile when RTTI is
+// disabled. _TR1_FUNCTIONAL is the header guard for
+// <tr1/functional>. Hence the following #define is a hack to prevent
+// <tr1/functional> from being included.
+# define _TR1_FUNCTIONAL 1
+# include <tr1/tuple>
+# undef _TR1_FUNCTIONAL // Allows the user to #include
+ // <tr1/functional> if he chooses to.
+# else
+# include <tr1/tuple> // NOLINT
+# endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302
+
+# else
+// If the compiler is not GCC 4.0+, we assume the user is using a
+// spec-conforming TR1 implementation.
+# include <tuple> // NOLINT
+# endif // GTEST_USE_OWN_TR1_TUPLE
+
+#endif // GTEST_HAS_TR1_TUPLE
+
+// Determines whether clone(2) is supported.
+// Usually it will only be available on Linux, excluding
+// Linux on the Itanium architecture.
+// Also see http://linux.die.net/man/2/clone.
+#ifndef GTEST_HAS_CLONE
+// The user didn't tell us, so we need to figure it out.
+
+# if GTEST_OS_LINUX && !defined(__ia64__)
+# if GTEST_OS_LINUX_ANDROID
+// On Android, clone() is only available on ARM starting with Gingerbread.
+# if defined(__arm__) && __ANDROID_API__ >= 9
+# define GTEST_HAS_CLONE 1
+# else
+# define GTEST_HAS_CLONE 0
+# endif
+# else
+# define GTEST_HAS_CLONE 1
+# endif
+# else
+# define GTEST_HAS_CLONE 0
+# endif // GTEST_OS_LINUX && !defined(__ia64__)
+
+#endif // GTEST_HAS_CLONE
+
+// Determines whether to support stream redirection. This is used to test
+// output correctness and to implement death tests.
+#ifndef GTEST_HAS_STREAM_REDIRECTION
+// By default, we assume that stream redirection is supported on all
+// platforms except known mobile ones.
+# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN
+# define GTEST_HAS_STREAM_REDIRECTION 0
+# else
+# define GTEST_HAS_STREAM_REDIRECTION 1
+# endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN
+#endif // GTEST_HAS_STREAM_REDIRECTION
+
+// Determines whether to support death tests.
+// Google Test does not support death tests for VC 7.1 and earlier as
+// abort() in a VC 7.1 application compiled as GUI in debug config
+// pops up a dialog window that cannot be suppressed programmatically.
+#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \
+ (GTEST_OS_MAC && !GTEST_OS_IOS) || GTEST_OS_IOS_SIMULATOR || \
+ (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \
+ GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \
+ GTEST_OS_OPENBSD || GTEST_OS_QNX)
+# define GTEST_HAS_DEATH_TEST 1
+# include <vector> // NOLINT
+#endif
+
+// We don't support MSVC 7.1 with exceptions disabled now. Therefore
+// all the compilers we care about are adequate for supporting
+// value-parameterized tests.
+#define GTEST_HAS_PARAM_TEST 1
+
+// Determines whether to support type-driven tests.
+
+// Typed tests need <typeinfo> and variadic macros, which GCC, VC++ 8.0,
+// Sun Pro CC, IBM Visual Age, and HP aCC support.
+#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__SUNPRO_CC) || \
+ defined(__IBMCPP__) || defined(__HP_aCC)
+# define GTEST_HAS_TYPED_TEST 1
+# define GTEST_HAS_TYPED_TEST_P 1
+#endif
+
+// Determines whether to support Combine(). This only makes sense when
+// value-parameterized tests are enabled. The implementation doesn't
+// work on Sun Studio since it doesn't understand templated conversion
+// operators.
+#if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC)
+# define GTEST_HAS_COMBINE 1
+#endif
+
+// Determines whether the system compiler uses UTF-16 for encoding wide strings.
+#define GTEST_WIDE_STRING_USES_UTF16_ \
+ (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_SYMBIAN || GTEST_OS_AIX)
+
+// Determines whether test results can be streamed to a socket.
+#if GTEST_OS_LINUX
+# define GTEST_CAN_STREAM_RESULTS_ 1
+#endif
+
+// Defines some utility macros.
+
+// The GNU compiler emits a warning if nested "if" statements are followed by
+// an "else" statement and braces are not used to explicitly disambiguate the
+// "else" binding. This leads to problems with code like:
+//
+// if (gate)
+// ASSERT_*(condition) << "Some message";
+//
+// The "switch (0) case 0:" idiom is used to suppress this.
+#ifdef __INTEL_COMPILER
+# define GTEST_AMBIGUOUS_ELSE_BLOCKER_
+#else
+# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default: // NOLINT
+#endif
+
+// Use this annotation at the end of a struct/class definition to
+// prevent the compiler from optimizing away instances that are never
+// used. This is useful when all interesting logic happens inside the
+// c'tor and / or d'tor. Example:
+//
+// struct Foo {
+// Foo() { ... }
+// } GTEST_ATTRIBUTE_UNUSED_;
+//
+// Also use it after a variable or parameter declaration to tell the
+// compiler the variable/parameter does not have to be used.
+#if defined(__GNUC__) && !defined(COMPILER_ICC)
+# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused))
+#else
+# define GTEST_ATTRIBUTE_UNUSED_
+#endif
+
+// A macro to disallow operator=
+// This should be used in the private: declarations for a class.
+#define GTEST_DISALLOW_ASSIGN_(type)\
+ void operator=(type const &)
+
+// A macro to disallow copy constructor and operator=
+// This should be used in the private: declarations for a class.
+#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type)\
+ type(type const &);\
+ GTEST_DISALLOW_ASSIGN_(type)
+
+// Tell the compiler to warn about unused return values for functions declared
+// with this macro. The macro should be used on function declarations
+// following the argument list:
+//
+// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_;
+#if defined(__GNUC__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC)
+# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result))
+#else
+# define GTEST_MUST_USE_RESULT_
+#endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC
+
+// Determine whether the compiler supports Microsoft's Structured Exception
+// Handling. This is supported by several Windows compilers but generally
+// does not exist on any other system.
+#ifndef GTEST_HAS_SEH
+// The user didn't tell us, so we need to figure it out.
+
+# if defined(_MSC_VER) || defined(__BORLANDC__)
+// These two compilers are known to support SEH.
+# define GTEST_HAS_SEH 1
+# else
+// Assume no SEH.
+# define GTEST_HAS_SEH 0
+# endif
+
+#endif // GTEST_HAS_SEH
+
+#ifdef _MSC_VER
+
+# if GTEST_LINKED_AS_SHARED_LIBRARY
+# define GTEST_API_ __declspec(dllimport)
+# elif GTEST_CREATE_SHARED_LIBRARY
+# define GTEST_API_ __declspec(dllexport)
+# endif
+
+#endif // _MSC_VER
+
+#ifndef GTEST_API_
+# define GTEST_API_
+#endif
+
+#ifdef __GNUC__
+// Ask the compiler to never inline a given function.
+# define GTEST_NO_INLINE_ __attribute__((noinline))
+#else
+# define GTEST_NO_INLINE_
+#endif
+
+// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project.
+#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
+# define GTEST_HAS_CXXABI_H_ 1
+#else
+# define GTEST_HAS_CXXABI_H_ 0
+#endif
+
+namespace testing {
+
+class Message;
+
+namespace internal {
+
+// A secret type that Google Test users don't know about. It has no
+// definition on purpose. Therefore it's impossible to create a
+// Secret object, which is what we want.
+class Secret;
+
+// The GTEST_COMPILE_ASSERT_ macro can be used to verify that a compile time
+// expression is true. For example, you could use it to verify the
+// size of a static array:
+//
+// GTEST_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
+// content_type_names_incorrect_size);
+//
+// or to make sure a struct is smaller than a certain size:
+//
+// GTEST_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large);
+//
+// The second argument to the macro is the name of the variable. If
+// the expression is false, most compilers will issue a warning/error
+// containing the name of the variable.
+
+template <bool>
+struct CompileAssert {
+};
+
+#define GTEST_COMPILE_ASSERT_(expr, msg) \
+ typedef ::testing::internal::CompileAssert<(static_cast<bool>(expr))> \
+ msg[static_cast<bool>(expr) ? 1 : -1] GTEST_ATTRIBUTE_UNUSED_
+
+// Implementation details of GTEST_COMPILE_ASSERT_:
+//
+// - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1
+// elements (and thus is invalid) when the expression is false.
+//
+// - The simpler definition
+//
+// #define GTEST_COMPILE_ASSERT_(expr, msg) typedef char msg[(expr) ? 1 : -1]
+//
+// does not work, as gcc supports variable-length arrays whose sizes
+// are determined at run-time (this is gcc's extension and not part
+// of the C++ standard). As a result, gcc fails to reject the
+// following code with the simple definition:
+//
+// int foo;
+// GTEST_COMPILE_ASSERT_(foo, msg); // not supposed to compile as foo is
+// // not a compile-time constant.
+//
+// - By using the type CompileAssert<(bool(expr))>, we ensures that
+// expr is a compile-time constant. (Template arguments must be
+// determined at compile-time.)
+//
+// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
+// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written
+//
+// CompileAssert<bool(expr)>
+//
+// instead, these compilers will refuse to compile
+//
+// GTEST_COMPILE_ASSERT_(5 > 0, some_message);
+//
+// (They seem to think the ">" in "5 > 0" marks the end of the
+// template argument list.)
+//
+// - The array size is (bool(expr) ? 1 : -1), instead of simply
+//
+// ((expr) ? 1 : -1).
+//
+// This is to avoid running into a bug in MS VC 7.1, which
+// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
+
+// StaticAssertTypeEqHelper is used by StaticAssertTypeEq defined in gtest.h.
+//
+// This template is declared, but intentionally undefined.
+template <typename T1, typename T2>
+struct StaticAssertTypeEqHelper;
+
+template <typename T>
+struct StaticAssertTypeEqHelper<T, T> {};
+
+#if GTEST_HAS_GLOBAL_STRING
+typedef ::string string;
+#else
+typedef ::std::string string;
+#endif // GTEST_HAS_GLOBAL_STRING
+
+#if GTEST_HAS_GLOBAL_WSTRING
+typedef ::wstring wstring;
+#elif GTEST_HAS_STD_WSTRING
+typedef ::std::wstring wstring;
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+// A helper for suppressing warnings on constant condition. It just
+// returns 'condition'.
+GTEST_API_ bool IsTrue(bool condition);
+
+// Defines scoped_ptr.
+
+// This implementation of scoped_ptr is PARTIAL - it only contains
+// enough stuff to satisfy Google Test's need.
+template <typename T>
+class scoped_ptr {
+ public:
+ typedef T element_type;
+
+ explicit scoped_ptr(T* p = NULL) : ptr_(p) {}
+ ~scoped_ptr() { reset(); }
+
+ T& operator*() const { return *ptr_; }
+ T* operator->() const { return ptr_; }
+ T* get() const { return ptr_; }
+
+ T* release() {
+ T* const ptr = ptr_;
+ ptr_ = NULL;
+ return ptr;
+ }
+
+ void reset(T* p = NULL) {
+ if (p != ptr_) {
+ if (IsTrue(sizeof(T) > 0)) { // Makes sure T is a complete type.
+ delete ptr_;
+ }
+ ptr_ = p;
+ }
+ }
+
+ private:
+ T* ptr_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr);
+};
+
+// Defines RE.
+
+// A simple C++ wrapper for <regex.h>. It uses the POSIX Extended
+// Regular Expression syntax.
+class GTEST_API_ RE {
+ public:
+ // A copy constructor is required by the Standard to initialize object
+ // references from r-values.
+ RE(const RE& other) { Init(other.pattern()); }
+
+ // Constructs an RE from a string.
+ RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT
+
+#if GTEST_HAS_GLOBAL_STRING
+
+ RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT
+
+#endif // GTEST_HAS_GLOBAL_STRING
+
+ RE(const char* regex) { Init(regex); } // NOLINT
+ ~RE();
+
+ // Returns the string representation of the regex.
+ const char* pattern() const { return pattern_; }
+
+ // FullMatch(str, re) returns true iff regular expression re matches
+ // the entire str.
+ // PartialMatch(str, re) returns true iff regular expression re
+ // matches a substring of str (including str itself).
+ //
+ // TODO(wan at google.com): make FullMatch() and PartialMatch() work
+ // when str contains NUL characters.
+ static bool FullMatch(const ::std::string& str, const RE& re) {
+ return FullMatch(str.c_str(), re);
+ }
+ static bool PartialMatch(const ::std::string& str, const RE& re) {
+ return PartialMatch(str.c_str(), re);
+ }
+
+#if GTEST_HAS_GLOBAL_STRING
+
+ static bool FullMatch(const ::string& str, const RE& re) {
+ return FullMatch(str.c_str(), re);
+ }
+ static bool PartialMatch(const ::string& str, const RE& re) {
+ return PartialMatch(str.c_str(), re);
+ }
+
+#endif // GTEST_HAS_GLOBAL_STRING
+
+ static bool FullMatch(const char* str, const RE& re);
+ static bool PartialMatch(const char* str, const RE& re);
+
+ private:
+ void Init(const char* regex);
+
+ // We use a const char* instead of an std::string, as Google Test used to be
+ // used where std::string is not available. TODO(wan at google.com): change to
+ // std::string.
+ const char* pattern_;
+ bool is_valid_;
+
+#if GTEST_USES_POSIX_RE
+
+ regex_t full_regex_; // For FullMatch().
+ regex_t partial_regex_; // For PartialMatch().
+
+#else // GTEST_USES_SIMPLE_RE
+
+ const char* full_pattern_; // For FullMatch();
+
+#endif
+
+ GTEST_DISALLOW_ASSIGN_(RE);
+};
+
+// Formats a source file path and a line number as they would appear
+// in an error message from the compiler used to compile this code.
+GTEST_API_ ::std::string FormatFileLocation(const char* file, int line);
+
+// Formats a file location for compiler-independent XML output.
+// Although this function is not platform dependent, we put it next to
+// FormatFileLocation in order to contrast the two functions.
+GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file,
+ int line);
+
+// Defines logging utilities:
+// GTEST_LOG_(severity) - logs messages at the specified severity level. The
+// message itself is streamed into the macro.
+// LogToStderr() - directs all log messages to stderr.
+// FlushInfoLog() - flushes informational log messages.
+
+enum GTestLogSeverity {
+ GTEST_INFO,
+ GTEST_WARNING,
+ GTEST_ERROR,
+ GTEST_FATAL
+};
+
+// Formats log entry severity, provides a stream object for streaming the
+// log message, and terminates the message with a newline when going out of
+// scope.
+class GTEST_API_ GTestLog {
+ public:
+ GTestLog(GTestLogSeverity severity, const char* file, int line);
+
+ // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program.
+ ~GTestLog();
+
+ ::std::ostream& GetStream() { return ::std::cerr; }
+
+ private:
+ const GTestLogSeverity severity_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog);
+};
+
+#define GTEST_LOG_(severity) \
+ ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \
+ __FILE__, __LINE__).GetStream()
+
+inline void LogToStderr() {}
+inline void FlushInfoLog() { fflush(NULL); }
+
+// INTERNAL IMPLEMENTATION - DO NOT USE.
+//
+// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition
+// is not satisfied.
+// Synopsys:
+// GTEST_CHECK_(boolean_condition);
+// or
+// GTEST_CHECK_(boolean_condition) << "Additional message";
+//
+// This checks the condition and if the condition is not satisfied
+// it prints message about the condition violation, including the
+// condition itself, plus additional message streamed into it, if any,
+// and then it aborts the program. It aborts the program irrespective of
+// whether it is built in the debug mode or not.
+#define GTEST_CHECK_(condition) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::IsTrue(condition)) \
+ ; \
+ else \
+ GTEST_LOG_(FATAL) << "Condition " #condition " failed. "
+
+// An all-mode assert to verify that the given POSIX-style function
+// call returns 0 (indicating success). Known limitation: this
+// doesn't expand to a balanced 'if' statement, so enclose the macro
+// in {} if you need to use it as the only statement in an 'if'
+// branch.
+#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \
+ if (const int gtest_error = (posix_call)) \
+ GTEST_LOG_(FATAL) << #posix_call << "failed with error " \
+ << gtest_error
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Use ImplicitCast_ as a safe version of static_cast for upcasting in
+// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a
+// const Foo*). When you use ImplicitCast_, the compiler checks that
+// the cast is safe. Such explicit ImplicitCast_s are necessary in
+// surprisingly many situations where C++ demands an exact type match
+// instead of an argument type convertable to a target type.
+//
+// The syntax for using ImplicitCast_ is the same as for static_cast:
+//
+// ImplicitCast_<ToType>(expr)
+//
+// ImplicitCast_ would have been part of the C++ standard library,
+// but the proposal was submitted too late. It will probably make
+// its way into the language in the future.
+//
+// This relatively ugly name is intentional. It prevents clashes with
+// similar functions users may have (e.g., implicit_cast). The internal
+// namespace alone is not enough because the function can be found by ADL.
+template<typename To>
+inline To ImplicitCast_(To x) { return x; }
+
+// When you upcast (that is, cast a pointer from type Foo to type
+// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts
+// always succeed. When you downcast (that is, cast a pointer from
+// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because
+// how do you know the pointer is really of type SubclassOfFoo? It
+// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus,
+// when you downcast, you should use this macro. In debug mode, we
+// use dynamic_cast<> to double-check the downcast is legal (we die
+// if it's not). In normal mode, we do the efficient static_cast<>
+// instead. Thus, it's important to test in debug mode to make sure
+// the cast is legal!
+// This is the only place in the code we should use dynamic_cast<>.
+// In particular, you SHOULDN'T be using dynamic_cast<> in order to
+// do RTTI (eg code like this:
+// if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);
+// if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);
+// You should design the code some other way not to need this.
+//
+// This relatively ugly name is intentional. It prevents clashes with
+// similar functions users may have (e.g., down_cast). The internal
+// namespace alone is not enough because the function can be found by ADL.
+template<typename To, typename From> // use like this: DownCast_<T*>(foo);
+inline To DownCast_(From* f) { // so we only accept pointers
+ // Ensures that To is a sub-type of From *. This test is here only
+ // for compile-time type checking, and has no overhead in an
+ // optimized build at run-time, as it will be optimized away
+ // completely.
+ if (false) {
+ const To to = NULL;
+ ::testing::internal::ImplicitCast_<From*>(to);
+ }
+
+#if GTEST_HAS_RTTI
+ // RTTI: debug mode only!
+ GTEST_CHECK_(f == NULL || dynamic_cast<To>(f) != NULL);
+#endif
+ return static_cast<To>(f);
+}
+
+// Downcasts the pointer of type Base to Derived.
+// Derived must be a subclass of Base. The parameter MUST
+// point to a class of type Derived, not any subclass of it.
+// When RTTI is available, the function performs a runtime
+// check to enforce this.
+template <class Derived, class Base>
+Derived* CheckedDowncastToActualType(Base* base) {
+#if GTEST_HAS_RTTI
+ GTEST_CHECK_(typeid(*base) == typeid(Derived));
+ return dynamic_cast<Derived*>(base); // NOLINT
+#else
+ return static_cast<Derived*>(base); // Poor man's downcast.
+#endif
+}
+
+#if GTEST_HAS_STREAM_REDIRECTION
+
+// Defines the stderr capturer:
+// CaptureStdout - starts capturing stdout.
+// GetCapturedStdout - stops capturing stdout and returns the captured string.
+// CaptureStderr - starts capturing stderr.
+// GetCapturedStderr - stops capturing stderr and returns the captured string.
+//
+GTEST_API_ void CaptureStdout();
+GTEST_API_ std::string GetCapturedStdout();
+GTEST_API_ void CaptureStderr();
+GTEST_API_ std::string GetCapturedStderr();
+
+#endif // GTEST_HAS_STREAM_REDIRECTION
+
+
+#if GTEST_HAS_DEATH_TEST
+
+const ::std::vector<testing::internal::string>& GetInjectableArgvs();
+void SetInjectableArgvs(const ::std::vector<testing::internal::string>*
+ new_argvs);
+
+// A copy of all command line arguments. Set by InitGoogleTest().
+extern ::std::vector<testing::internal::string> g_argvs;
+
+#endif // GTEST_HAS_DEATH_TEST
+
+// Defines synchronization primitives.
+
+#if GTEST_HAS_PTHREAD
+
+// Sleeps for (roughly) n milli-seconds. This function is only for
+// testing Google Test's own constructs. Don't use it in user tests,
+// either directly or indirectly.
+inline void SleepMilliseconds(int n) {
+ const timespec time = {
+ 0, // 0 seconds.
+ n * 1000L * 1000L, // And n ms.
+ };
+ nanosleep(&time, NULL);
+}
+
+// Allows a controller thread to pause execution of newly created
+// threads until notified. Instances of this class must be created
+// and destroyed in the controller thread.
+//
+// This class is only for testing Google Test's own constructs. Do not
+// use it in user tests, either directly or indirectly.
+class Notification {
+ public:
+ Notification() : notified_(false) {
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL));
+ }
+ ~Notification() {
+ pthread_mutex_destroy(&mutex_);
+ }
+
+ // Notifies all threads created with this notification to start. Must
+ // be called from the controller thread.
+ void Notify() {
+ pthread_mutex_lock(&mutex_);
+ notified_ = true;
+ pthread_mutex_unlock(&mutex_);
+ }
+
+ // Blocks until the controller thread notifies. Must be called from a test
+ // thread.
+ void WaitForNotification() {
+ for (;;) {
+ pthread_mutex_lock(&mutex_);
+ const bool notified = notified_;
+ pthread_mutex_unlock(&mutex_);
+ if (notified)
+ break;
+ SleepMilliseconds(10);
+ }
+ }
+
+ private:
+ pthread_mutex_t mutex_;
+ bool notified_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification);
+};
+
+// As a C-function, ThreadFuncWithCLinkage cannot be templated itself.
+// Consequently, it cannot select a correct instantiation of ThreadWithParam
+// in order to call its Run(). Introducing ThreadWithParamBase as a
+// non-templated base class for ThreadWithParam allows us to bypass this
+// problem.
+class ThreadWithParamBase {
+ public:
+ virtual ~ThreadWithParamBase() {}
+ virtual void Run() = 0;
+};
+
+// pthread_create() accepts a pointer to a function type with the C linkage.
+// According to the Standard (7.5/1), function types with different linkages
+// are different even if they are otherwise identical. Some compilers (for
+// example, SunStudio) treat them as different types. Since class methods
+// cannot be defined with C-linkage we need to define a free C-function to
+// pass into pthread_create().
+extern "C" inline void* ThreadFuncWithCLinkage(void* thread) {
+ static_cast<ThreadWithParamBase*>(thread)->Run();
+ return NULL;
+}
+
+// Helper class for testing Google Test's multi-threading constructs.
+// To use it, write:
+//
+// void ThreadFunc(int param) { /* Do things with param */ }
+// Notification thread_can_start;
+// ...
+// // The thread_can_start parameter is optional; you can supply NULL.
+// ThreadWithParam<int> thread(&ThreadFunc, 5, &thread_can_start);
+// thread_can_start.Notify();
+//
+// These classes are only for testing Google Test's own constructs. Do
+// not use them in user tests, either directly or indirectly.
+template <typename T>
+class ThreadWithParam : public ThreadWithParamBase {
+ public:
+ typedef void (*UserThreadFunc)(T);
+
+ ThreadWithParam(
+ UserThreadFunc func, T param, Notification* thread_can_start)
+ : func_(func),
+ param_(param),
+ thread_can_start_(thread_can_start),
+ finished_(false) {
+ ThreadWithParamBase* const base = this;
+ // The thread can be created only after all fields except thread_
+ // have been initialized.
+ GTEST_CHECK_POSIX_SUCCESS_(
+ pthread_create(&thread_, 0, &ThreadFuncWithCLinkage, base));
+ }
+ ~ThreadWithParam() { Join(); }
+
+ void Join() {
+ if (!finished_) {
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, 0));
+ finished_ = true;
+ }
+ }
+
+ virtual void Run() {
+ if (thread_can_start_ != NULL)
+ thread_can_start_->WaitForNotification();
+ func_(param_);
+ }
+
+ private:
+ const UserThreadFunc func_; // User-supplied thread function.
+ const T param_; // User-supplied parameter to the thread function.
+ // When non-NULL, used to block execution until the controller thread
+ // notifies.
+ Notification* const thread_can_start_;
+ bool finished_; // true iff we know that the thread function has finished.
+ pthread_t thread_; // The native thread object.
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam);
+};
+
+// MutexBase and Mutex implement mutex on pthreads-based platforms. They
+// are used in conjunction with class MutexLock:
+//
+// Mutex mutex;
+// ...
+// MutexLock lock(&mutex); // Acquires the mutex and releases it at the end
+// // of the current scope.
+//
+// MutexBase implements behavior for both statically and dynamically
+// allocated mutexes. Do not use MutexBase directly. Instead, write
+// the following to define a static mutex:
+//
+// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex);
+//
+// You can forward declare a static mutex like this:
+//
+// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex);
+//
+// To create a dynamic mutex, just define an object of type Mutex.
+class MutexBase {
+ public:
+ // Acquires this mutex.
+ void Lock() {
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_));
+ owner_ = pthread_self();
+ has_owner_ = true;
+ }
+
+ // Releases this mutex.
+ void Unlock() {
+ // Since the lock is being released the owner_ field should no longer be
+ // considered valid. We don't protect writing to has_owner_ here, as it's
+ // the caller's responsibility to ensure that the current thread holds the
+ // mutex when this is called.
+ has_owner_ = false;
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_));
+ }
+
+ // Does nothing if the current thread holds the mutex. Otherwise, crashes
+ // with high probability.
+ void AssertHeld() const {
+ GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self()))
+ << "The current thread is not holding the mutex @" << this;
+ }
+
+ // A static mutex may be used before main() is entered. It may even
+ // be used before the dynamic initialization stage. Therefore we
+ // must be able to initialize a static mutex object at link time.
+ // This means MutexBase has to be a POD and its member variables
+ // have to be public.
+ public:
+ pthread_mutex_t mutex_; // The underlying pthread mutex.
+ // has_owner_ indicates whether the owner_ field below contains a valid thread
+ // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All
+ // accesses to the owner_ field should be protected by a check of this field.
+ // An alternative might be to memset() owner_ to all zeros, but there's no
+ // guarantee that a zero'd pthread_t is necessarily invalid or even different
+ // from pthread_self().
+ bool has_owner_;
+ pthread_t owner_; // The thread holding the mutex.
+};
+
+// Forward-declares a static mutex.
+# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
+ extern ::testing::internal::MutexBase mutex
+
+// Defines and statically (i.e. at link time) initializes a static mutex.
+// The initialization list here does not explicitly initialize each field,
+// instead relying on default initialization for the unspecified fields. In
+// particular, the owner_ field (a pthread_t) is not explicitly initialized.
+// This allows initialization to work whether pthread_t is a scalar or struct.
+// The flag -Wmissing-field-initializers must not be specified for this to work.
+# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
+ ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false }
+
+// The Mutex class can only be used for mutexes created at runtime. It
+// shares its API with MutexBase otherwise.
+class Mutex : public MutexBase {
+ public:
+ Mutex() {
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL));
+ has_owner_ = false;
+ }
+ ~Mutex() {
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_));
+ }
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex);
+};
+
+// We cannot name this class MutexLock as the ctor declaration would
+// conflict with a macro named MutexLock, which is defined on some
+// platforms. Hence the typedef trick below.
+class GTestMutexLock {
+ public:
+ explicit GTestMutexLock(MutexBase* mutex)
+ : mutex_(mutex) { mutex_->Lock(); }
+
+ ~GTestMutexLock() { mutex_->Unlock(); }
+
+ private:
+ MutexBase* const mutex_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock);
+};
+
+typedef GTestMutexLock MutexLock;
+
+// Helpers for ThreadLocal.
+
+// pthread_key_create() requires DeleteThreadLocalValue() to have
+// C-linkage. Therefore it cannot be templatized to access
+// ThreadLocal<T>. Hence the need for class
+// ThreadLocalValueHolderBase.
+class ThreadLocalValueHolderBase {
+ public:
+ virtual ~ThreadLocalValueHolderBase() {}
+};
+
+// Called by pthread to delete thread-local data stored by
+// pthread_setspecific().
+extern "C" inline void DeleteThreadLocalValue(void* value_holder) {
+ delete static_cast<ThreadLocalValueHolderBase*>(value_holder);
+}
+
+// Implements thread-local storage on pthreads-based systems.
+//
+// // Thread 1
+// ThreadLocal<int> tl(100); // 100 is the default value for each thread.
+//
+// // Thread 2
+// tl.set(150); // Changes the value for thread 2 only.
+// EXPECT_EQ(150, tl.get());
+//
+// // Thread 1
+// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value.
+// tl.set(200);
+// EXPECT_EQ(200, tl.get());
+//
+// The template type argument T must have a public copy constructor.
+// In addition, the default ThreadLocal constructor requires T to have
+// a public default constructor.
+//
+// An object managed for a thread by a ThreadLocal instance is deleted
+// when the thread exits. Or, if the ThreadLocal instance dies in
+// that thread, when the ThreadLocal dies. It's the user's
+// responsibility to ensure that all other threads using a ThreadLocal
+// have exited when it dies, or the per-thread objects for those
+// threads will not be deleted.
+//
+// Google Test only uses global ThreadLocal objects. That means they
+// will die after main() has returned. Therefore, no per-thread
+// object managed by Google Test will be leaked as long as all threads
+// using Google Test have exited when main() returns.
+template <typename T>
+class ThreadLocal {
+ public:
+ ThreadLocal() : key_(CreateKey()),
+ default_() {}
+ explicit ThreadLocal(const T& value) : key_(CreateKey()),
+ default_(value) {}
+
+ ~ThreadLocal() {
+ // Destroys the managed object for the current thread, if any.
+ DeleteThreadLocalValue(pthread_getspecific(key_));
+
+ // Releases resources associated with the key. This will *not*
+ // delete managed objects for other threads.
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_));
+ }
+
+ T* pointer() { return GetOrCreateValue(); }
+ const T* pointer() const { return GetOrCreateValue(); }
+ const T& get() const { return *pointer(); }
+ void set(const T& value) { *pointer() = value; }
+
+ private:
+ // Holds a value of type T.
+ class ValueHolder : public ThreadLocalValueHolderBase {
+ public:
+ explicit ValueHolder(const T& value) : value_(value) {}
+
+ T* pointer() { return &value_; }
+
+ private:
+ T value_;
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder);
+ };
+
+ static pthread_key_t CreateKey() {
+ pthread_key_t key;
+ // When a thread exits, DeleteThreadLocalValue() will be called on
+ // the object managed for that thread.
+ GTEST_CHECK_POSIX_SUCCESS_(
+ pthread_key_create(&key, &DeleteThreadLocalValue));
+ return key;
+ }
+
+ T* GetOrCreateValue() const {
+ ThreadLocalValueHolderBase* const holder =
+ static_cast<ThreadLocalValueHolderBase*>(pthread_getspecific(key_));
+ if (holder != NULL) {
+ return CheckedDowncastToActualType<ValueHolder>(holder)->pointer();
+ }
+
+ ValueHolder* const new_holder = new ValueHolder(default_);
+ ThreadLocalValueHolderBase* const holder_base = new_holder;
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base));
+ return new_holder->pointer();
+ }
+
+ // A key pthreads uses for looking up per-thread values.
+ const pthread_key_t key_;
+ const T default_; // The default value for each thread.
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
+};
+
+# define GTEST_IS_THREADSAFE 1
+
+#else // GTEST_HAS_PTHREAD
+
+// A dummy implementation of synchronization primitives (mutex, lock,
+// and thread-local variable). Necessary for compiling Google Test where
+// mutex is not supported - using Google Test in multiple threads is not
+// supported on such platforms.
+
+class Mutex {
+ public:
+ Mutex() {}
+ void Lock() {}
+ void Unlock() {}
+ void AssertHeld() const {}
+};
+
+# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
+ extern ::testing::internal::Mutex mutex
+
+# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex
+
+class GTestMutexLock {
+ public:
+ explicit GTestMutexLock(Mutex*) {} // NOLINT
+};
+
+typedef GTestMutexLock MutexLock;
+
+template <typename T>
+class ThreadLocal {
+ public:
+ ThreadLocal() : value_() {}
+ explicit ThreadLocal(const T& value) : value_(value) {}
+ T* pointer() { return &value_; }
+ const T* pointer() const { return &value_; }
+ const T& get() const { return value_; }
+ void set(const T& value) { value_ = value; }
+ private:
+ T value_;
+};
+
+// The above synchronization primitives have dummy implementations.
+// Therefore Google Test is not thread-safe.
+# define GTEST_IS_THREADSAFE 0
+
+#endif // GTEST_HAS_PTHREAD
+
+// Returns the number of threads running in the process, or 0 to indicate that
+// we cannot detect it.
+GTEST_API_ size_t GetThreadCount();
+
+// Passing non-POD classes through ellipsis (...) crashes the ARM
+// compiler and generates a warning in Sun Studio. The Nokia Symbian
+// and the IBM XL C/C++ compiler try to instantiate a copy constructor
+// for objects passed through ellipsis (...), failing for uncopyable
+// objects. We define this to ensure that only POD is passed through
+// ellipsis on these systems.
+#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || defined(__SUNPRO_CC)
+// We lose support for NULL detection where the compiler doesn't like
+// passing non-POD classes through ellipsis (...).
+# define GTEST_ELLIPSIS_NEEDS_POD_ 1
+#else
+# define GTEST_CAN_COMPARE_NULL 1
+#endif
+
+// The Nokia Symbian and IBM XL C/C++ compilers cannot decide between
+// const T& and const T* in a function template. These compilers
+// _can_ decide between class template specializations for T and T*,
+// so a tr1::type_traits-like is_pointer works.
+#if defined(__SYMBIAN32__) || defined(__IBMCPP__)
+# define GTEST_NEEDS_IS_POINTER_ 1
+#endif
+
+template <bool bool_value>
+struct bool_constant {
+ typedef bool_constant<bool_value> type;
+ static const bool value = bool_value;
+};
+template <bool bool_value> const bool bool_constant<bool_value>::value;
+
+typedef bool_constant<false> false_type;
+typedef bool_constant<true> true_type;
+
+template <typename T>
+struct is_pointer : public false_type {};
+
+template <typename T>
+struct is_pointer<T*> : public true_type {};
+
+template <typename Iterator>
+struct IteratorTraits {
+ typedef typename Iterator::value_type value_type;
+};
+
+template <typename T>
+struct IteratorTraits<T*> {
+ typedef T value_type;
+};
+
+template <typename T>
+struct IteratorTraits<const T*> {
+ typedef T value_type;
+};
+
+#if GTEST_OS_WINDOWS
+# define GTEST_PATH_SEP_ "\\"
+# define GTEST_HAS_ALT_PATH_SEP_ 1
+// The biggest signed integer type the compiler supports.
+typedef __int64 BiggestInt;
+#else
+# define GTEST_PATH_SEP_ "/"
+# define GTEST_HAS_ALT_PATH_SEP_ 0
+typedef long long BiggestInt; // NOLINT
+#endif // GTEST_OS_WINDOWS
+
+// Utilities for char.
+
+// isspace(int ch) and friends accept an unsigned char or EOF. char
+// may be signed, depending on the compiler (or compiler flags).
+// Therefore we need to cast a char to unsigned char before calling
+// isspace(), etc.
+
+inline bool IsAlpha(char ch) {
+ return isalpha(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsAlNum(char ch) {
+ return isalnum(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsDigit(char ch) {
+ return isdigit(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsLower(char ch) {
+ return islower(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsSpace(char ch) {
+ return isspace(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsUpper(char ch) {
+ return isupper(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsXDigit(char ch) {
+ return isxdigit(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsXDigit(wchar_t ch) {
+ const unsigned char low_byte = static_cast<unsigned char>(ch);
+ return ch == low_byte && isxdigit(low_byte) != 0;
+}
+
+inline char ToLower(char ch) {
+ return static_cast<char>(tolower(static_cast<unsigned char>(ch)));
+}
+inline char ToUpper(char ch) {
+ return static_cast<char>(toupper(static_cast<unsigned char>(ch)));
+}
+
+// The testing::internal::posix namespace holds wrappers for common
+// POSIX functions. These wrappers hide the differences between
+// Windows/MSVC and POSIX systems. Since some compilers define these
+// standard functions as macros, the wrapper cannot have the same name
+// as the wrapped function.
+
+namespace posix {
+
+// Functions with a different name on Windows.
+
+#if GTEST_OS_WINDOWS
+
+typedef struct _stat StatStruct;
+
+# ifdef __BORLANDC__
+inline int IsATTY(int fd) { return isatty(fd); }
+inline int StrCaseCmp(const char* s1, const char* s2) {
+ return stricmp(s1, s2);
+}
+inline char* StrDup(const char* src) { return strdup(src); }
+# else // !__BORLANDC__
+# if GTEST_OS_WINDOWS_MOBILE
+inline int IsATTY(int /* fd */) { return 0; }
+# else
+inline int IsATTY(int fd) { return _isatty(fd); }
+# endif // GTEST_OS_WINDOWS_MOBILE
+inline int StrCaseCmp(const char* s1, const char* s2) {
+ return _stricmp(s1, s2);
+}
+inline char* StrDup(const char* src) { return _strdup(src); }
+# endif // __BORLANDC__
+
+# if GTEST_OS_WINDOWS_MOBILE
+inline int FileNo(FILE* file) { return reinterpret_cast<int>(_fileno(file)); }
+// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this
+// time and thus not defined there.
+# else
+inline int FileNo(FILE* file) { return _fileno(file); }
+inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); }
+inline int RmDir(const char* dir) { return _rmdir(dir); }
+inline bool IsDir(const StatStruct& st) {
+ return (_S_IFDIR & st.st_mode) != 0;
+}
+# endif // GTEST_OS_WINDOWS_MOBILE
+
+#else
+
+typedef struct stat StatStruct;
+
+inline int FileNo(FILE* file) { return fileno(file); }
+inline int IsATTY(int fd) { return isatty(fd); }
+inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); }
+inline int StrCaseCmp(const char* s1, const char* s2) {
+ return strcasecmp(s1, s2);
+}
+inline char* StrDup(const char* src) { return strdup(src); }
+inline int RmDir(const char* dir) { return rmdir(dir); }
+inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); }
+
+#endif // GTEST_OS_WINDOWS
+
+// Functions deprecated by MSVC 8.0.
+
+#ifdef _MSC_VER
+// Temporarily disable warning 4996 (deprecated function).
+# pragma warning(push)
+# pragma warning(disable:4996)
+#endif
+
+inline const char* StrNCpy(char* dest, const char* src, size_t n) {
+ return strncpy(dest, src, n);
+}
+
+// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and
+// StrError() aren't needed on Windows CE at this time and thus not
+// defined there.
+
+#if !GTEST_OS_WINDOWS_MOBILE
+inline int ChDir(const char* dir) { return chdir(dir); }
+#endif
+inline FILE* FOpen(const char* path, const char* mode) {
+ return fopen(path, mode);
+}
+#if !GTEST_OS_WINDOWS_MOBILE
+inline FILE *FReopen(const char* path, const char* mode, FILE* stream) {
+ return freopen(path, mode, stream);
+}
+inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); }
+#endif
+inline int FClose(FILE* fp) { return fclose(fp); }
+#if !GTEST_OS_WINDOWS_MOBILE
+inline int Read(int fd, void* buf, unsigned int count) {
+ return static_cast<int>(read(fd, buf, count));
+}
+inline int Write(int fd, const void* buf, unsigned int count) {
+ return static_cast<int>(write(fd, buf, count));
+}
+inline int Close(int fd) { return close(fd); }
+inline const char* StrError(int errnum) { return strerror(errnum); }
+#endif
+inline const char* GetEnv(const char* name) {
+#if GTEST_OS_WINDOWS_MOBILE
+ // We are on Windows CE, which has no environment variables.
+ return NULL;
+#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9)
+ // Environment variables which we programmatically clear will be set to the
+ // empty string rather than unset (NULL). Handle that case.
+ const char* const env = getenv(name);
+ return (env != NULL && env[0] != '\0') ? env : NULL;
+#else
+ return getenv(name);
+#endif
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop) // Restores the warning state.
+#endif
+
+#if GTEST_OS_WINDOWS_MOBILE
+// Windows CE has no C library. The abort() function is used in
+// several places in Google Test. This implementation provides a reasonable
+// imitation of standard behaviour.
+void Abort();
+#else
+inline void Abort() { abort(); }
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+} // namespace posix
+
+// MSVC "deprecates" snprintf and issues warnings wherever it is used. In
+// order to avoid these warnings, we need to use _snprintf or _snprintf_s on
+// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate
+// function in order to achieve that. We use macro definition here because
+// snprintf is a variadic function.
+#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE
+// MSVC 2005 and above support variadic macros.
+# define GTEST_SNPRINTF_(buffer, size, format, ...) \
+ _snprintf_s(buffer, size, size, format, __VA_ARGS__)
+#elif defined(_MSC_VER)
+// Windows CE does not define _snprintf_s and MSVC prior to 2005 doesn't
+// complain about _snprintf.
+# define GTEST_SNPRINTF_ _snprintf
+#else
+# define GTEST_SNPRINTF_ snprintf
+#endif
+
+// The maximum number a BiggestInt can represent. This definition
+// works no matter BiggestInt is represented in one's complement or
+// two's complement.
+//
+// We cannot rely on numeric_limits in STL, as __int64 and long long
+// are not part of standard C++ and numeric_limits doesn't need to be
+// defined for them.
+const BiggestInt kMaxBiggestInt =
+ ~(static_cast<BiggestInt>(1) << (8*sizeof(BiggestInt) - 1));
+
+// This template class serves as a compile-time function from size to
+// type. It maps a size in bytes to a primitive type with that
+// size. e.g.
+//
+// TypeWithSize<4>::UInt
+//
+// is typedef-ed to be unsigned int (unsigned integer made up of 4
+// bytes).
+//
+// Such functionality should belong to STL, but I cannot find it
+// there.
+//
+// Google Test uses this class in the implementation of floating-point
+// comparison.
+//
+// For now it only handles UInt (unsigned int) as that's all Google Test
+// needs. Other types can be easily added in the future if need
+// arises.
+template <size_t size>
+class TypeWithSize {
+ public:
+ // This prevents the user from using TypeWithSize<N> with incorrect
+ // values of N.
+ typedef void UInt;
+};
+
+// The specialization for size 4.
+template <>
+class TypeWithSize<4> {
+ public:
+ // unsigned int has size 4 in both gcc and MSVC.
+ //
+ // As base/basictypes.h doesn't compile on Windows, we cannot use
+ // uint32, uint64, and etc here.
+ typedef int Int;
+ typedef unsigned int UInt;
+};
+
+// The specialization for size 8.
+template <>
+class TypeWithSize<8> {
+ public:
+#if GTEST_OS_WINDOWS
+ typedef __int64 Int;
+ typedef unsigned __int64 UInt;
+#else
+ typedef long long Int; // NOLINT
+ typedef unsigned long long UInt; // NOLINT
+#endif // GTEST_OS_WINDOWS
+};
+
+// Integer types of known sizes.
+typedef TypeWithSize<4>::Int Int32;
+typedef TypeWithSize<4>::UInt UInt32;
+typedef TypeWithSize<8>::Int Int64;
+typedef TypeWithSize<8>::UInt UInt64;
+typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds.
+
+// Utilities for command line flags and environment variables.
+
+// Macro for referencing flags.
+#define GTEST_FLAG(name) FLAGS_gtest_##name
+
+// Macros for declaring flags.
+#define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name)
+#define GTEST_DECLARE_int32_(name) \
+ GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name)
+#define GTEST_DECLARE_string_(name) \
+ GTEST_API_ extern ::std::string GTEST_FLAG(name)
+
+// Macros for defining flags.
+#define GTEST_DEFINE_bool_(name, default_val, doc) \
+ GTEST_API_ bool GTEST_FLAG(name) = (default_val)
+#define GTEST_DEFINE_int32_(name, default_val, doc) \
+ GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val)
+#define GTEST_DEFINE_string_(name, default_val, doc) \
+ GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val)
+
+// Thread annotations
+#define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)
+#define GTEST_LOCK_EXCLUDED_(locks)
+
+// Parses 'str' for a 32-bit signed integer. If successful, writes the result
+// to *value and returns true; otherwise leaves *value unchanged and returns
+// false.
+// TODO(chandlerc): Find a better way to refactor flag and environment parsing
+// out of both gtest-port.cc and gtest.cc to avoid exporting this utility
+// function.
+bool ParseInt32(const Message& src_text, const char* str, Int32* value);
+
+// Parses a bool/Int32/string from the environment variable
+// corresponding to the given Google Test flag.
+bool BoolFromGTestEnv(const char* flag, bool default_val);
+GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val);
+const char* StringFromGTestEnv(const char* flag, const char* default_val);
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
diff --git a/test/gtest/include/gtest/internal/gtest-string.h b/test/gtest/include/gtest/internal/gtest-string.h
new file mode 100644
index 0000000..97f1a7f
--- /dev/null
+++ b/test/gtest/include/gtest/internal/gtest-string.h
@@ -0,0 +1,167 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: wan at google.com (Zhanyong Wan), eefacm at gmail.com (Sean Mcafee)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file declares the String class and functions used internally by
+// Google Test. They are subject to change without notice. They should not used
+// by code external to Google Test.
+//
+// This header file is #included by <gtest/internal/gtest-internal.h>.
+// It should not be #included by other files.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
+
+#ifdef __BORLANDC__
+// string.h is not guaranteed to provide strcpy on C++ Builder.
+# include <mem.h>
+#endif
+
+#include <string.h>
+#include <string>
+
+#include "gtest/internal/gtest-port.h"
+
+namespace testing {
+namespace internal {
+
+// String - an abstract class holding static string utilities.
+class GTEST_API_ String {
+ public:
+ // Static utility methods
+
+ // Clones a 0-terminated C string, allocating memory using new. The
+ // caller is responsible for deleting the return value using
+ // delete[]. Returns the cloned string, or NULL if the input is
+ // NULL.
+ //
+ // This is different from strdup() in string.h, which allocates
+ // memory using malloc().
+ static const char* CloneCString(const char* c_str);
+
+#if GTEST_OS_WINDOWS_MOBILE
+ // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be
+ // able to pass strings to Win32 APIs on CE we need to convert them
+ // to 'Unicode', UTF-16.
+
+ // Creates a UTF-16 wide string from the given ANSI string, allocating
+ // memory using new. The caller is responsible for deleting the return
+ // value using delete[]. Returns the wide string, or NULL if the
+ // input is NULL.
+ //
+ // The wide string is created using the ANSI codepage (CP_ACP) to
+ // match the behaviour of the ANSI versions of Win32 calls and the
+ // C runtime.
+ static LPCWSTR AnsiToUtf16(const char* c_str);
+
+ // Creates an ANSI string from the given wide string, allocating
+ // memory using new. The caller is responsible for deleting the return
+ // value using delete[]. Returns the ANSI string, or NULL if the
+ // input is NULL.
+ //
+ // The returned string is created using the ANSI codepage (CP_ACP) to
+ // match the behaviour of the ANSI versions of Win32 calls and the
+ // C runtime.
+ static const char* Utf16ToAnsi(LPCWSTR utf16_str);
+#endif
+
+ // Compares two C strings. Returns true iff they have the same content.
+ //
+ // Unlike strcmp(), this function can handle NULL argument(s). A
+ // NULL C string is considered different to any non-NULL C string,
+ // including the empty string.
+ static bool CStringEquals(const char* lhs, const char* rhs);
+
+ // Converts a wide C string to a String using the UTF-8 encoding.
+ // NULL will be converted to "(null)". If an error occurred during
+ // the conversion, "(failed to convert from wide string)" is
+ // returned.
+ static std::string ShowWideCString(const wchar_t* wide_c_str);
+
+ // Compares two wide C strings. Returns true iff they have the same
+ // content.
+ //
+ // Unlike wcscmp(), this function can handle NULL argument(s). A
+ // NULL C string is considered different to any non-NULL C string,
+ // including the empty string.
+ static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs);
+
+ // Compares two C strings, ignoring case. Returns true iff they
+ // have the same content.
+ //
+ // Unlike strcasecmp(), this function can handle NULL argument(s).
+ // A NULL C string is considered different to any non-NULL C string,
+ // including the empty string.
+ static bool CaseInsensitiveCStringEquals(const char* lhs,
+ const char* rhs);
+
+ // Compares two wide C strings, ignoring case. Returns true iff they
+ // have the same content.
+ //
+ // Unlike wcscasecmp(), this function can handle NULL argument(s).
+ // A NULL C string is considered different to any non-NULL wide C string,
+ // including the empty string.
+ // NB: The implementations on different platforms slightly differ.
+ // On windows, this method uses _wcsicmp which compares according to LC_CTYPE
+ // environment variable. On GNU platform this method uses wcscasecmp
+ // which compares according to LC_CTYPE category of the current locale.
+ // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the
+ // current locale.
+ static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs,
+ const wchar_t* rhs);
+
+ // Returns true iff the given string ends with the given suffix, ignoring
+ // case. Any string is considered to end with an empty suffix.
+ static bool EndsWithCaseInsensitive(
+ const std::string& str, const std::string& suffix);
+
+ // Formats an int value as "%02d".
+ static std::string FormatIntWidth2(int value); // "%02d" for width == 2
+
+ // Formats an int value as "%X".
+ static std::string FormatHexInt(int value);
+
+ // Formats a byte as "%02X".
+ static std::string FormatByte(unsigned char value);
+
+ private:
+ String(); // Not meant to be instantiated.
+}; // class String
+
+// Gets the content of the stringstream's buffer as an std::string. Each '\0'
+// character in the buffer is replaced with "\\0".
+GTEST_API_ std::string StringStreamToString(::std::stringstream* stream);
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
diff --git a/test/gtest/include/gtest/internal/gtest-tuple.h b/test/gtest/include/gtest/internal/gtest-tuple.h
new file mode 100644
index 0000000..7b3dfc3
--- /dev/null
+++ b/test/gtest/include/gtest/internal/gtest-tuple.h
@@ -0,0 +1,1012 @@
+// This file was GENERATED by command:
+// pump.py gtest-tuple.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2009 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+// Implements a subset of TR1 tuple needed by Google Test and Google Mock.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
+
+#include <utility> // For ::std::pair.
+
+// The compiler used in Symbian has a bug that prevents us from declaring the
+// tuple template as a friend (it complains that tuple is redefined). This
+// hack bypasses the bug by declaring the members that should otherwise be
+// private as public.
+// Sun Studio versions < 12 also have the above bug.
+#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590)
+# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public:
+#else
+# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \
+ template <GTEST_10_TYPENAMES_(U)> friend class tuple; \
+ private:
+#endif
+
+// GTEST_n_TUPLE_(T) is the type of an n-tuple.
+#define GTEST_0_TUPLE_(T) tuple<>
+#define GTEST_1_TUPLE_(T) tuple<T##0, void, void, void, void, void, void, \
+ void, void, void>
+#define GTEST_2_TUPLE_(T) tuple<T##0, T##1, void, void, void, void, void, \
+ void, void, void>
+#define GTEST_3_TUPLE_(T) tuple<T##0, T##1, T##2, void, void, void, void, \
+ void, void, void>
+#define GTEST_4_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, void, void, void, \
+ void, void, void>
+#define GTEST_5_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, void, void, \
+ void, void, void>
+#define GTEST_6_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, void, \
+ void, void, void>
+#define GTEST_7_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+ void, void, void>
+#define GTEST_8_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+ T##7, void, void>
+#define GTEST_9_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+ T##7, T##8, void>
+#define GTEST_10_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+ T##7, T##8, T##9>
+
+// GTEST_n_TYPENAMES_(T) declares a list of n typenames.
+#define GTEST_0_TYPENAMES_(T)
+#define GTEST_1_TYPENAMES_(T) typename T##0
+#define GTEST_2_TYPENAMES_(T) typename T##0, typename T##1
+#define GTEST_3_TYPENAMES_(T) typename T##0, typename T##1, typename T##2
+#define GTEST_4_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3
+#define GTEST_5_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4
+#define GTEST_6_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5
+#define GTEST_7_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5, typename T##6
+#define GTEST_8_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5, typename T##6, typename T##7
+#define GTEST_9_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5, typename T##6, \
+ typename T##7, typename T##8
+#define GTEST_10_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5, typename T##6, \
+ typename T##7, typename T##8, typename T##9
+
+// In theory, defining stuff in the ::std namespace is undefined
+// behavior. We can do this as we are playing the role of a standard
+// library vendor.
+namespace std {
+namespace tr1 {
+
+template <typename T0 = void, typename T1 = void, typename T2 = void,
+ typename T3 = void, typename T4 = void, typename T5 = void,
+ typename T6 = void, typename T7 = void, typename T8 = void,
+ typename T9 = void>
+class tuple;
+
+// Anything in namespace gtest_internal is Google Test's INTERNAL
+// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code.
+namespace gtest_internal {
+
+// ByRef<T>::type is T if T is a reference; otherwise it's const T&.
+template <typename T>
+struct ByRef { typedef const T& type; }; // NOLINT
+template <typename T>
+struct ByRef<T&> { typedef T& type; }; // NOLINT
+
+// A handy wrapper for ByRef.
+#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef<T>::type
+
+// AddRef<T>::type is T if T is a reference; otherwise it's T&. This
+// is the same as tr1::add_reference<T>::type.
+template <typename T>
+struct AddRef { typedef T& type; }; // NOLINT
+template <typename T>
+struct AddRef<T&> { typedef T& type; }; // NOLINT
+
+// A handy wrapper for AddRef.
+#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef<T>::type
+
+// A helper for implementing get<k>().
+template <int k> class Get;
+
+// A helper for implementing tuple_element<k, T>. kIndexValid is true
+// iff k < the number of fields in tuple type T.
+template <bool kIndexValid, int kIndex, class Tuple>
+struct TupleElement;
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 0, GTEST_10_TUPLE_(T) > {
+ typedef T0 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 1, GTEST_10_TUPLE_(T) > {
+ typedef T1 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 2, GTEST_10_TUPLE_(T) > {
+ typedef T2 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 3, GTEST_10_TUPLE_(T) > {
+ typedef T3 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 4, GTEST_10_TUPLE_(T) > {
+ typedef T4 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 5, GTEST_10_TUPLE_(T) > {
+ typedef T5 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 6, GTEST_10_TUPLE_(T) > {
+ typedef T6 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 7, GTEST_10_TUPLE_(T) > {
+ typedef T7 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 8, GTEST_10_TUPLE_(T) > {
+ typedef T8 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 9, GTEST_10_TUPLE_(T) > {
+ typedef T9 type;
+};
+
+} // namespace gtest_internal
+
+template <>
+class tuple<> {
+ public:
+ tuple() {}
+ tuple(const tuple& /* t */) {}
+ tuple& operator=(const tuple& /* t */) { return *this; }
+};
+
+template <GTEST_1_TYPENAMES_(T)>
+class GTEST_1_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0) : f0_(f0) {}
+
+ tuple(const tuple& t) : f0_(t.f0_) {}
+
+ template <GTEST_1_TYPENAMES_(U)>
+ tuple(const GTEST_1_TUPLE_(U)& t) : f0_(t.f0_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_1_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_1_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_1_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_1_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ return *this;
+ }
+
+ T0 f0_;
+};
+
+template <GTEST_2_TYPENAMES_(T)>
+class GTEST_2_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1) : f0_(f0),
+ f1_(f1) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_) {}
+
+ template <GTEST_2_TYPENAMES_(U)>
+ tuple(const GTEST_2_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_) {}
+ template <typename U0, typename U1>
+ tuple(const ::std::pair<U0, U1>& p) : f0_(p.first), f1_(p.second) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_2_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_2_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+ template <typename U0, typename U1>
+ tuple& operator=(const ::std::pair<U0, U1>& p) {
+ f0_ = p.first;
+ f1_ = p.second;
+ return *this;
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_2_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_2_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+};
+
+template <GTEST_3_TYPENAMES_(T)>
+class GTEST_3_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2) : f0_(f0), f1_(f1), f2_(f2) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {}
+
+ template <GTEST_3_TYPENAMES_(U)>
+ tuple(const GTEST_3_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_3_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_3_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_3_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_3_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+};
+
+template <GTEST_4_TYPENAMES_(T)>
+class GTEST_4_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3) : f0_(f0), f1_(f1), f2_(f2),
+ f3_(f3) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_) {}
+
+ template <GTEST_4_TYPENAMES_(U)>
+ tuple(const GTEST_4_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_4_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_4_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_4_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_4_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+};
+
+template <GTEST_5_TYPENAMES_(T)>
+class GTEST_5_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3,
+ GTEST_BY_REF_(T4) f4) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_) {}
+
+ template <GTEST_5_TYPENAMES_(U)>
+ tuple(const GTEST_5_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_5_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_5_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_5_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_5_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+};
+
+template <GTEST_6_TYPENAMES_(T)>
+class GTEST_6_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
+ f5_(f5) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_) {}
+
+ template <GTEST_6_TYPENAMES_(U)>
+ tuple(const GTEST_6_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_6_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_6_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_6_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_6_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+};
+
+template <GTEST_7_TYPENAMES_(T)>
+class GTEST_7_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6) : f0_(f0), f1_(f1), f2_(f2),
+ f3_(f3), f4_(f4), f5_(f5), f6_(f6) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {}
+
+ template <GTEST_7_TYPENAMES_(U)>
+ tuple(const GTEST_7_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_7_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_7_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_7_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_7_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ f6_ = t.f6_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+ T6 f6_;
+};
+
+template <GTEST_8_TYPENAMES_(T)>
+class GTEST_8_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6,
+ GTEST_BY_REF_(T7) f7) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
+ f5_(f5), f6_(f6), f7_(f7) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {}
+
+ template <GTEST_8_TYPENAMES_(U)>
+ tuple(const GTEST_8_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_8_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_8_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_8_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_8_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ f6_ = t.f6_;
+ f7_ = t.f7_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+ T6 f6_;
+ T7 f7_;
+};
+
+template <GTEST_9_TYPENAMES_(T)>
+class GTEST_9_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7,
+ GTEST_BY_REF_(T8) f8) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
+ f5_(f5), f6_(f6), f7_(f7), f8_(f8) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {}
+
+ template <GTEST_9_TYPENAMES_(U)>
+ tuple(const GTEST_9_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_9_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_9_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_9_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_9_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ f6_ = t.f6_;
+ f7_ = t.f7_;
+ f8_ = t.f8_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+ T6 f6_;
+ T7 f7_;
+ T8 f8_;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+class tuple {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_(),
+ f9_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7,
+ GTEST_BY_REF_(T8) f8, GTEST_BY_REF_(T9) f9) : f0_(f0), f1_(f1), f2_(f2),
+ f3_(f3), f4_(f4), f5_(f5), f6_(f6), f7_(f7), f8_(f8), f9_(f9) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), f9_(t.f9_) {}
+
+ template <GTEST_10_TYPENAMES_(U)>
+ tuple(const GTEST_10_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_),
+ f9_(t.f9_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_10_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_10_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_10_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_10_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ f6_ = t.f6_;
+ f7_ = t.f7_;
+ f8_ = t.f8_;
+ f9_ = t.f9_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+ T6 f6_;
+ T7 f7_;
+ T8 f8_;
+ T9 f9_;
+};
+
+// 6.1.3.2 Tuple creation functions.
+
+// Known limitations: we don't support passing an
+// std::tr1::reference_wrapper<T> to make_tuple(). And we don't
+// implement tie().
+
+inline tuple<> make_tuple() { return tuple<>(); }
+
+template <GTEST_1_TYPENAMES_(T)>
+inline GTEST_1_TUPLE_(T) make_tuple(const T0& f0) {
+ return GTEST_1_TUPLE_(T)(f0);
+}
+
+template <GTEST_2_TYPENAMES_(T)>
+inline GTEST_2_TUPLE_(T) make_tuple(const T0& f0, const T1& f1) {
+ return GTEST_2_TUPLE_(T)(f0, f1);
+}
+
+template <GTEST_3_TYPENAMES_(T)>
+inline GTEST_3_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2) {
+ return GTEST_3_TUPLE_(T)(f0, f1, f2);
+}
+
+template <GTEST_4_TYPENAMES_(T)>
+inline GTEST_4_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3) {
+ return GTEST_4_TUPLE_(T)(f0, f1, f2, f3);
+}
+
+template <GTEST_5_TYPENAMES_(T)>
+inline GTEST_5_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4) {
+ return GTEST_5_TUPLE_(T)(f0, f1, f2, f3, f4);
+}
+
+template <GTEST_6_TYPENAMES_(T)>
+inline GTEST_6_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5) {
+ return GTEST_6_TUPLE_(T)(f0, f1, f2, f3, f4, f5);
+}
+
+template <GTEST_7_TYPENAMES_(T)>
+inline GTEST_7_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5, const T6& f6) {
+ return GTEST_7_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6);
+}
+
+template <GTEST_8_TYPENAMES_(T)>
+inline GTEST_8_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7) {
+ return GTEST_8_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7);
+}
+
+template <GTEST_9_TYPENAMES_(T)>
+inline GTEST_9_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7,
+ const T8& f8) {
+ return GTEST_9_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8);
+}
+
+template <GTEST_10_TYPENAMES_(T)>
+inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7,
+ const T8& f8, const T9& f9) {
+ return GTEST_10_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9);
+}
+
+// 6.1.3.3 Tuple helper classes.
+
+template <typename Tuple> struct tuple_size;
+
+template <GTEST_0_TYPENAMES_(T)>
+struct tuple_size<GTEST_0_TUPLE_(T) > {
+ static const int value = 0;
+};
+
+template <GTEST_1_TYPENAMES_(T)>
+struct tuple_size<GTEST_1_TUPLE_(T) > {
+ static const int value = 1;
+};
+
+template <GTEST_2_TYPENAMES_(T)>
+struct tuple_size<GTEST_2_TUPLE_(T) > {
+ static const int value = 2;
+};
+
+template <GTEST_3_TYPENAMES_(T)>
+struct tuple_size<GTEST_3_TUPLE_(T) > {
+ static const int value = 3;
+};
+
+template <GTEST_4_TYPENAMES_(T)>
+struct tuple_size<GTEST_4_TUPLE_(T) > {
+ static const int value = 4;
+};
+
+template <GTEST_5_TYPENAMES_(T)>
+struct tuple_size<GTEST_5_TUPLE_(T) > {
+ static const int value = 5;
+};
+
+template <GTEST_6_TYPENAMES_(T)>
+struct tuple_size<GTEST_6_TUPLE_(T) > {
+ static const int value = 6;
+};
+
+template <GTEST_7_TYPENAMES_(T)>
+struct tuple_size<GTEST_7_TUPLE_(T) > {
+ static const int value = 7;
+};
+
+template <GTEST_8_TYPENAMES_(T)>
+struct tuple_size<GTEST_8_TUPLE_(T) > {
+ static const int value = 8;
+};
+
+template <GTEST_9_TYPENAMES_(T)>
+struct tuple_size<GTEST_9_TUPLE_(T) > {
+ static const int value = 9;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct tuple_size<GTEST_10_TUPLE_(T) > {
+ static const int value = 10;
+};
+
+template <int k, class Tuple>
+struct tuple_element {
+ typedef typename gtest_internal::TupleElement<
+ k < (tuple_size<Tuple>::value), k, Tuple>::type type;
+};
+
+#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element<k, Tuple >::type
+
+// 6.1.3.4 Element access.
+
+namespace gtest_internal {
+
+template <>
+class Get<0> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple))
+ Field(Tuple& t) { return t.f0_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple))
+ ConstField(const Tuple& t) { return t.f0_; }
+};
+
+template <>
+class Get<1> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple))
+ Field(Tuple& t) { return t.f1_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple))
+ ConstField(const Tuple& t) { return t.f1_; }
+};
+
+template <>
+class Get<2> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple))
+ Field(Tuple& t) { return t.f2_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple))
+ ConstField(const Tuple& t) { return t.f2_; }
+};
+
+template <>
+class Get<3> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple))
+ Field(Tuple& t) { return t.f3_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple))
+ ConstField(const Tuple& t) { return t.f3_; }
+};
+
+template <>
+class Get<4> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple))
+ Field(Tuple& t) { return t.f4_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple))
+ ConstField(const Tuple& t) { return t.f4_; }
+};
+
+template <>
+class Get<5> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple))
+ Field(Tuple& t) { return t.f5_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple))
+ ConstField(const Tuple& t) { return t.f5_; }
+};
+
+template <>
+class Get<6> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple))
+ Field(Tuple& t) { return t.f6_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple))
+ ConstField(const Tuple& t) { return t.f6_; }
+};
+
+template <>
+class Get<7> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple))
+ Field(Tuple& t) { return t.f7_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple))
+ ConstField(const Tuple& t) { return t.f7_; }
+};
+
+template <>
+class Get<8> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple))
+ Field(Tuple& t) { return t.f8_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple))
+ ConstField(const Tuple& t) { return t.f8_; }
+};
+
+template <>
+class Get<9> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple))
+ Field(Tuple& t) { return t.f9_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple))
+ ConstField(const Tuple& t) { return t.f9_; }
+};
+
+} // namespace gtest_internal
+
+template <int k, GTEST_10_TYPENAMES_(T)>
+GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T)))
+get(GTEST_10_TUPLE_(T)& t) {
+ return gtest_internal::Get<k>::Field(t);
+}
+
+template <int k, GTEST_10_TYPENAMES_(T)>
+GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T)))
+get(const GTEST_10_TUPLE_(T)& t) {
+ return gtest_internal::Get<k>::ConstField(t);
+}
+
+// 6.1.3.5 Relational operators
+
+// We only implement == and !=, as we don't have a need for the rest yet.
+
+namespace gtest_internal {
+
+// SameSizeTuplePrefixComparator<k, k>::Eq(t1, t2) returns true if the
+// first k fields of t1 equals the first k fields of t2.
+// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if
+// k1 != k2.
+template <int kSize1, int kSize2>
+struct SameSizeTuplePrefixComparator;
+
+template <>
+struct SameSizeTuplePrefixComparator<0, 0> {
+ template <class Tuple1, class Tuple2>
+ static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) {
+ return true;
+ }
+};
+
+template <int k>
+struct SameSizeTuplePrefixComparator<k, k> {
+ template <class Tuple1, class Tuple2>
+ static bool Eq(const Tuple1& t1, const Tuple2& t2) {
+ return SameSizeTuplePrefixComparator<k - 1, k - 1>::Eq(t1, t2) &&
+ ::std::tr1::get<k - 1>(t1) == ::std::tr1::get<k - 1>(t2);
+ }
+};
+
+} // namespace gtest_internal
+
+template <GTEST_10_TYPENAMES_(T), GTEST_10_TYPENAMES_(U)>
+inline bool operator==(const GTEST_10_TUPLE_(T)& t,
+ const GTEST_10_TUPLE_(U)& u) {
+ return gtest_internal::SameSizeTuplePrefixComparator<
+ tuple_size<GTEST_10_TUPLE_(T) >::value,
+ tuple_size<GTEST_10_TUPLE_(U) >::value>::Eq(t, u);
+}
+
+template <GTEST_10_TYPENAMES_(T), GTEST_10_TYPENAMES_(U)>
+inline bool operator!=(const GTEST_10_TUPLE_(T)& t,
+ const GTEST_10_TUPLE_(U)& u) { return !(t == u); }
+
+// 6.1.4 Pairs.
+// Unimplemented.
+
+} // namespace tr1
+} // namespace std
+
+#undef GTEST_0_TUPLE_
+#undef GTEST_1_TUPLE_
+#undef GTEST_2_TUPLE_
+#undef GTEST_3_TUPLE_
+#undef GTEST_4_TUPLE_
+#undef GTEST_5_TUPLE_
+#undef GTEST_6_TUPLE_
+#undef GTEST_7_TUPLE_
+#undef GTEST_8_TUPLE_
+#undef GTEST_9_TUPLE_
+#undef GTEST_10_TUPLE_
+
+#undef GTEST_0_TYPENAMES_
+#undef GTEST_1_TYPENAMES_
+#undef GTEST_2_TYPENAMES_
+#undef GTEST_3_TYPENAMES_
+#undef GTEST_4_TYPENAMES_
+#undef GTEST_5_TYPENAMES_
+#undef GTEST_6_TYPENAMES_
+#undef GTEST_7_TYPENAMES_
+#undef GTEST_8_TYPENAMES_
+#undef GTEST_9_TYPENAMES_
+#undef GTEST_10_TYPENAMES_
+
+#undef GTEST_DECLARE_TUPLE_AS_FRIEND_
+#undef GTEST_BY_REF_
+#undef GTEST_ADD_REF_
+#undef GTEST_TUPLE_ELEMENT_
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
diff --git a/test/gtest/include/gtest/internal/gtest-tuple.h.pump b/test/gtest/include/gtest/internal/gtest-tuple.h.pump
new file mode 100644
index 0000000..c7d9e03
--- /dev/null
+++ b/test/gtest/include/gtest/internal/gtest-tuple.h.pump
@@ -0,0 +1,339 @@
+$$ -*- mode: c++; -*-
+$var n = 10 $$ Maximum number of tuple fields we want to support.
+$$ This meta comment fixes auto-indentation in Emacs. }}
+// Copyright 2009 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+// Implements a subset of TR1 tuple needed by Google Test and Google Mock.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
+
+#include <utility> // For ::std::pair.
+
+// The compiler used in Symbian has a bug that prevents us from declaring the
+// tuple template as a friend (it complains that tuple is redefined). This
+// hack bypasses the bug by declaring the members that should otherwise be
+// private as public.
+// Sun Studio versions < 12 also have the above bug.
+#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590)
+# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public:
+#else
+# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \
+ template <GTEST_$(n)_TYPENAMES_(U)> friend class tuple; \
+ private:
+#endif
+
+
+$range i 0..n-1
+$range j 0..n
+$range k 1..n
+// GTEST_n_TUPLE_(T) is the type of an n-tuple.
+#define GTEST_0_TUPLE_(T) tuple<>
+
+$for k [[
+$range m 0..k-1
+$range m2 k..n-1
+#define GTEST_$(k)_TUPLE_(T) tuple<$for m, [[T##$m]]$for m2 [[, void]]>
+
+]]
+
+// GTEST_n_TYPENAMES_(T) declares a list of n typenames.
+
+$for j [[
+$range m 0..j-1
+#define GTEST_$(j)_TYPENAMES_(T) $for m, [[typename T##$m]]
+
+
+]]
+
+// In theory, defining stuff in the ::std namespace is undefined
+// behavior. We can do this as we are playing the role of a standard
+// library vendor.
+namespace std {
+namespace tr1 {
+
+template <$for i, [[typename T$i = void]]>
+class tuple;
+
+// Anything in namespace gtest_internal is Google Test's INTERNAL
+// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code.
+namespace gtest_internal {
+
+// ByRef<T>::type is T if T is a reference; otherwise it's const T&.
+template <typename T>
+struct ByRef { typedef const T& type; }; // NOLINT
+template <typename T>
+struct ByRef<T&> { typedef T& type; }; // NOLINT
+
+// A handy wrapper for ByRef.
+#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef<T>::type
+
+// AddRef<T>::type is T if T is a reference; otherwise it's T&. This
+// is the same as tr1::add_reference<T>::type.
+template <typename T>
+struct AddRef { typedef T& type; }; // NOLINT
+template <typename T>
+struct AddRef<T&> { typedef T& type; }; // NOLINT
+
+// A handy wrapper for AddRef.
+#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef<T>::type
+
+// A helper for implementing get<k>().
+template <int k> class Get;
+
+// A helper for implementing tuple_element<k, T>. kIndexValid is true
+// iff k < the number of fields in tuple type T.
+template <bool kIndexValid, int kIndex, class Tuple>
+struct TupleElement;
+
+
+$for i [[
+template <GTEST_$(n)_TYPENAMES_(T)>
+struct TupleElement<true, $i, GTEST_$(n)_TUPLE_(T) > {
+ typedef T$i type;
+};
+
+
+]]
+} // namespace gtest_internal
+
+template <>
+class tuple<> {
+ public:
+ tuple() {}
+ tuple(const tuple& /* t */) {}
+ tuple& operator=(const tuple& /* t */) { return *this; }
+};
+
+
+$for k [[
+$range m 0..k-1
+template <GTEST_$(k)_TYPENAMES_(T)>
+class $if k < n [[GTEST_$(k)_TUPLE_(T)]] $else [[tuple]] {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : $for m, [[f$(m)_()]] {}
+
+ explicit tuple($for m, [[GTEST_BY_REF_(T$m) f$m]]) : [[]]
+$for m, [[f$(m)_(f$m)]] {}
+
+ tuple(const tuple& t) : $for m, [[f$(m)_(t.f$(m)_)]] {}
+
+ template <GTEST_$(k)_TYPENAMES_(U)>
+ tuple(const GTEST_$(k)_TUPLE_(U)& t) : $for m, [[f$(m)_(t.f$(m)_)]] {}
+
+$if k == 2 [[
+ template <typename U0, typename U1>
+ tuple(const ::std::pair<U0, U1>& p) : f0_(p.first), f1_(p.second) {}
+
+]]
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_$(k)_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_$(k)_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+$if k == 2 [[
+ template <typename U0, typename U1>
+ tuple& operator=(const ::std::pair<U0, U1>& p) {
+ f0_ = p.first;
+ f1_ = p.second;
+ return *this;
+ }
+
+]]
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_$(k)_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_$(k)_TUPLE_(U)& t) {
+
+$for m [[
+ f$(m)_ = t.f$(m)_;
+
+]]
+ return *this;
+ }
+
+
+$for m [[
+ T$m f$(m)_;
+
+]]
+};
+
+
+]]
+// 6.1.3.2 Tuple creation functions.
+
+// Known limitations: we don't support passing an
+// std::tr1::reference_wrapper<T> to make_tuple(). And we don't
+// implement tie().
+
+inline tuple<> make_tuple() { return tuple<>(); }
+
+$for k [[
+$range m 0..k-1
+
+template <GTEST_$(k)_TYPENAMES_(T)>
+inline GTEST_$(k)_TUPLE_(T) make_tuple($for m, [[const T$m& f$m]]) {
+ return GTEST_$(k)_TUPLE_(T)($for m, [[f$m]]);
+}
+
+]]
+
+// 6.1.3.3 Tuple helper classes.
+
+template <typename Tuple> struct tuple_size;
+
+
+$for j [[
+template <GTEST_$(j)_TYPENAMES_(T)>
+struct tuple_size<GTEST_$(j)_TUPLE_(T) > {
+ static const int value = $j;
+};
+
+
+]]
+template <int k, class Tuple>
+struct tuple_element {
+ typedef typename gtest_internal::TupleElement<
+ k < (tuple_size<Tuple>::value), k, Tuple>::type type;
+};
+
+#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element<k, Tuple >::type
+
+// 6.1.3.4 Element access.
+
+namespace gtest_internal {
+
+
+$for i [[
+template <>
+class Get<$i> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_($i, Tuple))
+ Field(Tuple& t) { return t.f$(i)_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_($i, Tuple))
+ ConstField(const Tuple& t) { return t.f$(i)_; }
+};
+
+
+]]
+} // namespace gtest_internal
+
+template <int k, GTEST_$(n)_TYPENAMES_(T)>
+GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_$(n)_TUPLE_(T)))
+get(GTEST_$(n)_TUPLE_(T)& t) {
+ return gtest_internal::Get<k>::Field(t);
+}
+
+template <int k, GTEST_$(n)_TYPENAMES_(T)>
+GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_$(n)_TUPLE_(T)))
+get(const GTEST_$(n)_TUPLE_(T)& t) {
+ return gtest_internal::Get<k>::ConstField(t);
+}
+
+// 6.1.3.5 Relational operators
+
+// We only implement == and !=, as we don't have a need for the rest yet.
+
+namespace gtest_internal {
+
+// SameSizeTuplePrefixComparator<k, k>::Eq(t1, t2) returns true if the
+// first k fields of t1 equals the first k fields of t2.
+// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if
+// k1 != k2.
+template <int kSize1, int kSize2>
+struct SameSizeTuplePrefixComparator;
+
+template <>
+struct SameSizeTuplePrefixComparator<0, 0> {
+ template <class Tuple1, class Tuple2>
+ static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) {
+ return true;
+ }
+};
+
+template <int k>
+struct SameSizeTuplePrefixComparator<k, k> {
+ template <class Tuple1, class Tuple2>
+ static bool Eq(const Tuple1& t1, const Tuple2& t2) {
+ return SameSizeTuplePrefixComparator<k - 1, k - 1>::Eq(t1, t2) &&
+ ::std::tr1::get<k - 1>(t1) == ::std::tr1::get<k - 1>(t2);
+ }
+};
+
+} // namespace gtest_internal
+
+template <GTEST_$(n)_TYPENAMES_(T), GTEST_$(n)_TYPENAMES_(U)>
+inline bool operator==(const GTEST_$(n)_TUPLE_(T)& t,
+ const GTEST_$(n)_TUPLE_(U)& u) {
+ return gtest_internal::SameSizeTuplePrefixComparator<
+ tuple_size<GTEST_$(n)_TUPLE_(T) >::value,
+ tuple_size<GTEST_$(n)_TUPLE_(U) >::value>::Eq(t, u);
+}
+
+template <GTEST_$(n)_TYPENAMES_(T), GTEST_$(n)_TYPENAMES_(U)>
+inline bool operator!=(const GTEST_$(n)_TUPLE_(T)& t,
+ const GTEST_$(n)_TUPLE_(U)& u) { return !(t == u); }
+
+// 6.1.4 Pairs.
+// Unimplemented.
+
+} // namespace tr1
+} // namespace std
+
+
+$for j [[
+#undef GTEST_$(j)_TUPLE_
+
+]]
+
+
+$for j [[
+#undef GTEST_$(j)_TYPENAMES_
+
+]]
+
+#undef GTEST_DECLARE_TUPLE_AS_FRIEND_
+#undef GTEST_BY_REF_
+#undef GTEST_ADD_REF_
+#undef GTEST_TUPLE_ELEMENT_
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
diff --git a/test/gtest/include/gtest/internal/gtest-type-util.h b/test/gtest/include/gtest/internal/gtest-type-util.h
new file mode 100644
index 0000000..e46f7cf
--- /dev/null
+++ b/test/gtest/include/gtest/internal/gtest-type-util.h
@@ -0,0 +1,3331 @@
+// This file was GENERATED by command:
+// pump.py gtest-type-util.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+// Type utilities needed for implementing typed and type-parameterized
+// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
+//
+// Currently we support at most 50 types in a list, and at most 50
+// type-parameterized tests in one type-parameterized test case.
+// Please contact googletestframework at googlegroups.com if you need
+// more.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+
+#include "gtest/internal/gtest-port.h"
+
+// #ifdef __GNUC__ is too general here. It is possible to use gcc without using
+// libstdc++ (which is where cxxabi.h comes from).
+# if GTEST_HAS_CXXABI_H_
+# include <cxxabi.h>
+# elif defined(__HP_aCC)
+# include <acxx_demangle.h>
+# endif // GTEST_HASH_CXXABI_H_
+
+namespace testing {
+namespace internal {
+
+// GetTypeName<T>() returns a human-readable name of type T.
+// NB: This function is also used in Google Mock, so don't move it inside of
+// the typed-test-only section below.
+template <typename T>
+std::string GetTypeName() {
+# if GTEST_HAS_RTTI
+
+ const char* const name = typeid(T).name();
+# if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC)
+ int status = 0;
+ // gcc's implementation of typeid(T).name() mangles the type name,
+ // so we have to demangle it.
+# if GTEST_HAS_CXXABI_H_
+ using abi::__cxa_demangle;
+# endif // GTEST_HAS_CXXABI_H_
+ char* const readable_name = __cxa_demangle(name, 0, 0, &status);
+ const std::string name_str(status == 0 ? readable_name : name);
+ free(readable_name);
+ return name_str;
+# else
+ return name;
+# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC
+
+# else
+
+ return "<type>";
+
+# endif // GTEST_HAS_RTTI
+}
+
+#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+// AssertyTypeEq<T1, T2>::type is defined iff T1 and T2 are the same
+// type. This can be used as a compile-time assertion to ensure that
+// two types are equal.
+
+template <typename T1, typename T2>
+struct AssertTypeEq;
+
+template <typename T>
+struct AssertTypeEq<T, T> {
+ typedef bool type;
+};
+
+// A unique type used as the default value for the arguments of class
+// template Types. This allows us to simulate variadic templates
+// (e.g. Types<int>, Type<int, double>, and etc), which C++ doesn't
+// support directly.
+struct None {};
+
+// The following family of struct and struct templates are used to
+// represent type lists. In particular, TypesN<T1, T2, ..., TN>
+// represents a type list with N types (T1, T2, ..., and TN) in it.
+// Except for Types0, every struct in the family has two member types:
+// Head for the first type in the list, and Tail for the rest of the
+// list.
+
+// The empty type list.
+struct Types0 {};
+
+// Type lists of length 1, 2, 3, and so on.
+
+template <typename T1>
+struct Types1 {
+ typedef T1 Head;
+ typedef Types0 Tail;
+};
+template <typename T1, typename T2>
+struct Types2 {
+ typedef T1 Head;
+ typedef Types1<T2> Tail;
+};
+
+template <typename T1, typename T2, typename T3>
+struct Types3 {
+ typedef T1 Head;
+ typedef Types2<T2, T3> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4>
+struct Types4 {
+ typedef T1 Head;
+ typedef Types3<T2, T3, T4> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+struct Types5 {
+ typedef T1 Head;
+ typedef Types4<T2, T3, T4, T5> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+struct Types6 {
+ typedef T1 Head;
+ typedef Types5<T2, T3, T4, T5, T6> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+struct Types7 {
+ typedef T1 Head;
+ typedef Types6<T2, T3, T4, T5, T6, T7> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+struct Types8 {
+ typedef T1 Head;
+ typedef Types7<T2, T3, T4, T5, T6, T7, T8> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+struct Types9 {
+ typedef T1 Head;
+ typedef Types8<T2, T3, T4, T5, T6, T7, T8, T9> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+struct Types10 {
+ typedef T1 Head;
+ typedef Types9<T2, T3, T4, T5, T6, T7, T8, T9, T10> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11>
+struct Types11 {
+ typedef T1 Head;
+ typedef Types10<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12>
+struct Types12 {
+ typedef T1 Head;
+ typedef Types11<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13>
+struct Types13 {
+ typedef T1 Head;
+ typedef Types12<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14>
+struct Types14 {
+ typedef T1 Head;
+ typedef Types13<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15>
+struct Types15 {
+ typedef T1 Head;
+ typedef Types14<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16>
+struct Types16 {
+ typedef T1 Head;
+ typedef Types15<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17>
+struct Types17 {
+ typedef T1 Head;
+ typedef Types16<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18>
+struct Types18 {
+ typedef T1 Head;
+ typedef Types17<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19>
+struct Types19 {
+ typedef T1 Head;
+ typedef Types18<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20>
+struct Types20 {
+ typedef T1 Head;
+ typedef Types19<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21>
+struct Types21 {
+ typedef T1 Head;
+ typedef Types20<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22>
+struct Types22 {
+ typedef T1 Head;
+ typedef Types21<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23>
+struct Types23 {
+ typedef T1 Head;
+ typedef Types22<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24>
+struct Types24 {
+ typedef T1 Head;
+ typedef Types23<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25>
+struct Types25 {
+ typedef T1 Head;
+ typedef Types24<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26>
+struct Types26 {
+ typedef T1 Head;
+ typedef Types25<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27>
+struct Types27 {
+ typedef T1 Head;
+ typedef Types26<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28>
+struct Types28 {
+ typedef T1 Head;
+ typedef Types27<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29>
+struct Types29 {
+ typedef T1 Head;
+ typedef Types28<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30>
+struct Types30 {
+ typedef T1 Head;
+ typedef Types29<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31>
+struct Types31 {
+ typedef T1 Head;
+ typedef Types30<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32>
+struct Types32 {
+ typedef T1 Head;
+ typedef Types31<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33>
+struct Types33 {
+ typedef T1 Head;
+ typedef Types32<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34>
+struct Types34 {
+ typedef T1 Head;
+ typedef Types33<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35>
+struct Types35 {
+ typedef T1 Head;
+ typedef Types34<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36>
+struct Types36 {
+ typedef T1 Head;
+ typedef Types35<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37>
+struct Types37 {
+ typedef T1 Head;
+ typedef Types36<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38>
+struct Types38 {
+ typedef T1 Head;
+ typedef Types37<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39>
+struct Types39 {
+ typedef T1 Head;
+ typedef Types38<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40>
+struct Types40 {
+ typedef T1 Head;
+ typedef Types39<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41>
+struct Types41 {
+ typedef T1 Head;
+ typedef Types40<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42>
+struct Types42 {
+ typedef T1 Head;
+ typedef Types41<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43>
+struct Types43 {
+ typedef T1 Head;
+ typedef Types42<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44>
+struct Types44 {
+ typedef T1 Head;
+ typedef Types43<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45>
+struct Types45 {
+ typedef T1 Head;
+ typedef Types44<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46>
+struct Types46 {
+ typedef T1 Head;
+ typedef Types45<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47>
+struct Types47 {
+ typedef T1 Head;
+ typedef Types46<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48>
+struct Types48 {
+ typedef T1 Head;
+ typedef Types47<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47, T48> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49>
+struct Types49 {
+ typedef T1 Head;
+ typedef Types48<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47, T48, T49> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49, typename T50>
+struct Types50 {
+ typedef T1 Head;
+ typedef Types49<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47, T48, T49, T50> Tail;
+};
+
+
+} // namespace internal
+
+// We don't want to require the users to write TypesN<...> directly,
+// as that would require them to count the length. Types<...> is much
+// easier to write, but generates horrible messages when there is a
+// compiler error, as gcc insists on printing out each template
+// argument, even if it has the default value (this means Types<int>
+// will appear as Types<int, None, None, ..., None> in the compiler
+// errors).
+//
+// Our solution is to combine the best part of the two approaches: a
+// user would write Types<T1, ..., TN>, and Google Test will translate
+// that to TypesN<T1, ..., TN> internally to make error messages
+// readable. The translation is done by the 'type' member of the
+// Types template.
+template <typename T1 = internal::None, typename T2 = internal::None,
+ typename T3 = internal::None, typename T4 = internal::None,
+ typename T5 = internal::None, typename T6 = internal::None,
+ typename T7 = internal::None, typename T8 = internal::None,
+ typename T9 = internal::None, typename T10 = internal::None,
+ typename T11 = internal::None, typename T12 = internal::None,
+ typename T13 = internal::None, typename T14 = internal::None,
+ typename T15 = internal::None, typename T16 = internal::None,
+ typename T17 = internal::None, typename T18 = internal::None,
+ typename T19 = internal::None, typename T20 = internal::None,
+ typename T21 = internal::None, typename T22 = internal::None,
+ typename T23 = internal::None, typename T24 = internal::None,
+ typename T25 = internal::None, typename T26 = internal::None,
+ typename T27 = internal::None, typename T28 = internal::None,
+ typename T29 = internal::None, typename T30 = internal::None,
+ typename T31 = internal::None, typename T32 = internal::None,
+ typename T33 = internal::None, typename T34 = internal::None,
+ typename T35 = internal::None, typename T36 = internal::None,
+ typename T37 = internal::None, typename T38 = internal::None,
+ typename T39 = internal::None, typename T40 = internal::None,
+ typename T41 = internal::None, typename T42 = internal::None,
+ typename T43 = internal::None, typename T44 = internal::None,
+ typename T45 = internal::None, typename T46 = internal::None,
+ typename T47 = internal::None, typename T48 = internal::None,
+ typename T49 = internal::None, typename T50 = internal::None>
+struct Types {
+ typedef internal::Types50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44, T45, T46, T47, T48, T49, T50> type;
+};
+
+template <>
+struct Types<internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types0 type;
+};
+template <typename T1>
+struct Types<T1, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types1<T1> type;
+};
+template <typename T1, typename T2>
+struct Types<T1, T2, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types2<T1, T2> type;
+};
+template <typename T1, typename T2, typename T3>
+struct Types<T1, T2, T3, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types3<T1, T2, T3> type;
+};
+template <typename T1, typename T2, typename T3, typename T4>
+struct Types<T1, T2, T3, T4, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types4<T1, T2, T3, T4> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+struct Types<T1, T2, T3, T4, T5, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types5<T1, T2, T3, T4, T5> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+struct Types<T1, T2, T3, T4, T5, T6, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types6<T1, T2, T3, T4, T5, T6> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+struct Types<T1, T2, T3, T4, T5, T6, T7, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types7<T1, T2, T3, T4, T5, T6, T7> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types8<T1, T2, T3, T4, T5, T6, T7, T8> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types9<T1, T2, T3, T4, T5, T6, T7, T8, T9> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44, T45> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+ T46, internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44, T45, T46> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+ T46, T47, internal::None, internal::None, internal::None> {
+ typedef internal::Types47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44, T45, T46, T47> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+ T46, T47, T48, internal::None, internal::None> {
+ typedef internal::Types48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44, T45, T46, T47, T48> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+ T46, T47, T48, T49, internal::None> {
+ typedef internal::Types49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44, T45, T46, T47, T48, T49> type;
+};
+
+namespace internal {
+
+# define GTEST_TEMPLATE_ template <typename T> class
+
+// The template "selector" struct TemplateSel<Tmpl> is used to
+// represent Tmpl, which must be a class template with one type
+// parameter, as a type. TemplateSel<Tmpl>::Bind<T>::type is defined
+// as the type Tmpl<T>. This allows us to actually instantiate the
+// template "selected" by TemplateSel<Tmpl>.
+//
+// This trick is necessary for simulating typedef for class templates,
+// which C++ doesn't support directly.
+template <GTEST_TEMPLATE_ Tmpl>
+struct TemplateSel {
+ template <typename T>
+ struct Bind {
+ typedef Tmpl<T> type;
+ };
+};
+
+# define GTEST_BIND_(TmplSel, T) \
+ TmplSel::template Bind<T>::type
+
+// A unique struct template used as the default value for the
+// arguments of class template Templates. This allows us to simulate
+// variadic templates (e.g. Templates<int>, Templates<int, double>,
+// and etc), which C++ doesn't support directly.
+template <typename T>
+struct NoneT {};
+
+// The following family of struct and struct templates are used to
+// represent template lists. In particular, TemplatesN<T1, T2, ...,
+// TN> represents a list of N templates (T1, T2, ..., and TN). Except
+// for Templates0, every struct in the family has two member types:
+// Head for the selector of the first template in the list, and Tail
+// for the rest of the list.
+
+// The empty template list.
+struct Templates0 {};
+
+// Template lists of length 1, 2, 3, and so on.
+
+template <GTEST_TEMPLATE_ T1>
+struct Templates1 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates0 Tail;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2>
+struct Templates2 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates1<T2> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3>
+struct Templates3 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates2<T2, T3> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4>
+struct Templates4 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates3<T2, T3, T4> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5>
+struct Templates5 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates4<T2, T3, T4, T5> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6>
+struct Templates6 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates5<T2, T3, T4, T5, T6> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7>
+struct Templates7 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates6<T2, T3, T4, T5, T6, T7> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8>
+struct Templates8 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates7<T2, T3, T4, T5, T6, T7, T8> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9>
+struct Templates9 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates8<T2, T3, T4, T5, T6, T7, T8, T9> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10>
+struct Templates10 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates9<T2, T3, T4, T5, T6, T7, T8, T9, T10> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11>
+struct Templates11 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates10<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12>
+struct Templates12 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates11<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13>
+struct Templates13 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates12<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14>
+struct Templates14 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates13<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15>
+struct Templates15 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates14<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16>
+struct Templates16 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates15<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17>
+struct Templates17 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates16<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18>
+struct Templates18 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates17<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19>
+struct Templates19 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates18<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20>
+struct Templates20 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates19<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21>
+struct Templates21 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates20<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22>
+struct Templates22 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates21<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23>
+struct Templates23 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates22<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24>
+struct Templates24 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates23<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25>
+struct Templates25 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates24<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26>
+struct Templates26 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates25<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27>
+struct Templates27 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates26<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28>
+struct Templates28 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates27<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29>
+struct Templates29 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates28<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30>
+struct Templates30 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates29<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31>
+struct Templates31 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates30<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32>
+struct Templates32 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates31<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33>
+struct Templates33 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates32<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34>
+struct Templates34 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates33<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35>
+struct Templates35 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates34<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36>
+struct Templates36 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates35<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37>
+struct Templates37 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates36<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38>
+struct Templates38 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates37<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39>
+struct Templates39 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates38<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40>
+struct Templates40 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates39<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41>
+struct Templates41 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates40<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42>
+struct Templates42 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates41<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43>
+struct Templates43 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates42<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44>
+struct Templates44 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates43<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43, T44> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45>
+struct Templates45 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates44<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43, T44, T45> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46>
+struct Templates46 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates45<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43, T44, T45, T46> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47>
+struct Templates47 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates46<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43, T44, T45, T46, T47> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48>
+struct Templates48 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates47<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43, T44, T45, T46, T47, T48> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48,
+ GTEST_TEMPLATE_ T49>
+struct Templates49 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates48<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43, T44, T45, T46, T47, T48, T49> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48,
+ GTEST_TEMPLATE_ T49, GTEST_TEMPLATE_ T50>
+struct Templates50 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates49<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43, T44, T45, T46, T47, T48, T49, T50> Tail;
+};
+
+
+// We don't want to require the users to write TemplatesN<...> directly,
+// as that would require them to count the length. Templates<...> is much
+// easier to write, but generates horrible messages when there is a
+// compiler error, as gcc insists on printing out each template
+// argument, even if it has the default value (this means Templates<list>
+// will appear as Templates<list, NoneT, NoneT, ..., NoneT> in the compiler
+// errors).
+//
+// Our solution is to combine the best part of the two approaches: a
+// user would write Templates<T1, ..., TN>, and Google Test will translate
+// that to TemplatesN<T1, ..., TN> internally to make error messages
+// readable. The translation is done by the 'type' member of the
+// Templates template.
+template <GTEST_TEMPLATE_ T1 = NoneT, GTEST_TEMPLATE_ T2 = NoneT,
+ GTEST_TEMPLATE_ T3 = NoneT, GTEST_TEMPLATE_ T4 = NoneT,
+ GTEST_TEMPLATE_ T5 = NoneT, GTEST_TEMPLATE_ T6 = NoneT,
+ GTEST_TEMPLATE_ T7 = NoneT, GTEST_TEMPLATE_ T8 = NoneT,
+ GTEST_TEMPLATE_ T9 = NoneT, GTEST_TEMPLATE_ T10 = NoneT,
+ GTEST_TEMPLATE_ T11 = NoneT, GTEST_TEMPLATE_ T12 = NoneT,
+ GTEST_TEMPLATE_ T13 = NoneT, GTEST_TEMPLATE_ T14 = NoneT,
+ GTEST_TEMPLATE_ T15 = NoneT, GTEST_TEMPLATE_ T16 = NoneT,
+ GTEST_TEMPLATE_ T17 = NoneT, GTEST_TEMPLATE_ T18 = NoneT,
+ GTEST_TEMPLATE_ T19 = NoneT, GTEST_TEMPLATE_ T20 = NoneT,
+ GTEST_TEMPLATE_ T21 = NoneT, GTEST_TEMPLATE_ T22 = NoneT,
+ GTEST_TEMPLATE_ T23 = NoneT, GTEST_TEMPLATE_ T24 = NoneT,
+ GTEST_TEMPLATE_ T25 = NoneT, GTEST_TEMPLATE_ T26 = NoneT,
+ GTEST_TEMPLATE_ T27 = NoneT, GTEST_TEMPLATE_ T28 = NoneT,
+ GTEST_TEMPLATE_ T29 = NoneT, GTEST_TEMPLATE_ T30 = NoneT,
+ GTEST_TEMPLATE_ T31 = NoneT, GTEST_TEMPLATE_ T32 = NoneT,
+ GTEST_TEMPLATE_ T33 = NoneT, GTEST_TEMPLATE_ T34 = NoneT,
+ GTEST_TEMPLATE_ T35 = NoneT, GTEST_TEMPLATE_ T36 = NoneT,
+ GTEST_TEMPLATE_ T37 = NoneT, GTEST_TEMPLATE_ T38 = NoneT,
+ GTEST_TEMPLATE_ T39 = NoneT, GTEST_TEMPLATE_ T40 = NoneT,
+ GTEST_TEMPLATE_ T41 = NoneT, GTEST_TEMPLATE_ T42 = NoneT,
+ GTEST_TEMPLATE_ T43 = NoneT, GTEST_TEMPLATE_ T44 = NoneT,
+ GTEST_TEMPLATE_ T45 = NoneT, GTEST_TEMPLATE_ T46 = NoneT,
+ GTEST_TEMPLATE_ T47 = NoneT, GTEST_TEMPLATE_ T48 = NoneT,
+ GTEST_TEMPLATE_ T49 = NoneT, GTEST_TEMPLATE_ T50 = NoneT>
+struct Templates {
+ typedef Templates50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43, T44, T45, T46, T47, T48, T49, T50> type;
+};
+
+template <>
+struct Templates<NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT> {
+ typedef Templates0 type;
+};
+template <GTEST_TEMPLATE_ T1>
+struct Templates<T1, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT> {
+ typedef Templates1<T1> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2>
+struct Templates<T1, T2, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT> {
+ typedef Templates2<T1, T2> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3>
+struct Templates<T1, T2, T3, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates3<T1, T2, T3> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4>
+struct Templates<T1, T2, T3, T4, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates4<T1, T2, T3, T4> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5>
+struct Templates<T1, T2, T3, T4, T5, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates5<T1, T2, T3, T4, T5> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6>
+struct Templates<T1, T2, T3, T4, T5, T6, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates6<T1, T2, T3, T4, T5, T6> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates7<T1, T2, T3, T4, T5, T6, T7> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates8<T1, T2, T3, T4, T5, T6, T7, T8> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates9<T1, T2, T3, T4, T5, T6, T7, T8, T9> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT> {
+ typedef Templates22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT> {
+ typedef Templates23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT> {
+ typedef Templates24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT> {
+ typedef Templates25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT> {
+ typedef Templates26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT> {
+ typedef Templates27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT> {
+ typedef Templates28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT> {
+ typedef Templates29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43, T44> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+ T45, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43, T44, T45> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+ T45, T46, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43, T44, T45, T46> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+ T45, T46, T47, NoneT, NoneT, NoneT> {
+ typedef Templates47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43, T44, T45, T46, T47> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+ T45, T46, T47, T48, NoneT, NoneT> {
+ typedef Templates48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43, T44, T45, T46, T47, T48> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48,
+ GTEST_TEMPLATE_ T49>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+ T45, T46, T47, T48, T49, NoneT> {
+ typedef Templates49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43, T44, T45, T46, T47, T48, T49> type;
+};
+
+// The TypeList template makes it possible to use either a single type
+// or a Types<...> list in TYPED_TEST_CASE() and
+// INSTANTIATE_TYPED_TEST_CASE_P().
+
+template <typename T>
+struct TypeList {
+ typedef Types1<T> type;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49, typename T50>
+struct TypeList<Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47, T48, T49, T50> > {
+ typedef typename Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44, T45, T46, T47, T48, T49, T50>::type type;
+};
+
+#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
diff --git a/test/gtest/include/gtest/internal/gtest-type-util.h.pump b/test/gtest/include/gtest/internal/gtest-type-util.h.pump
new file mode 100644
index 0000000..251fdf0
--- /dev/null
+++ b/test/gtest/include/gtest/internal/gtest-type-util.h.pump
@@ -0,0 +1,297 @@
+$$ -*- mode: c++; -*-
+$var n = 50 $$ Maximum length of type lists we want to support.
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+// Type utilities needed for implementing typed and type-parameterized
+// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
+//
+// Currently we support at most $n types in a list, and at most $n
+// type-parameterized tests in one type-parameterized test case.
+// Please contact googletestframework at googlegroups.com if you need
+// more.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+
+#include "gtest/internal/gtest-port.h"
+
+// #ifdef __GNUC__ is too general here. It is possible to use gcc without using
+// libstdc++ (which is where cxxabi.h comes from).
+# if GTEST_HAS_CXXABI_H_
+# include <cxxabi.h>
+# elif defined(__HP_aCC)
+# include <acxx_demangle.h>
+# endif // GTEST_HASH_CXXABI_H_
+
+namespace testing {
+namespace internal {
+
+// GetTypeName<T>() returns a human-readable name of type T.
+// NB: This function is also used in Google Mock, so don't move it inside of
+// the typed-test-only section below.
+template <typename T>
+std::string GetTypeName() {
+# if GTEST_HAS_RTTI
+
+ const char* const name = typeid(T).name();
+# if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC)
+ int status = 0;
+ // gcc's implementation of typeid(T).name() mangles the type name,
+ // so we have to demangle it.
+# if GTEST_HAS_CXXABI_H_
+ using abi::__cxa_demangle;
+# endif // GTEST_HAS_CXXABI_H_
+ char* const readable_name = __cxa_demangle(name, 0, 0, &status);
+ const std::string name_str(status == 0 ? readable_name : name);
+ free(readable_name);
+ return name_str;
+# else
+ return name;
+# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC
+
+# else
+
+ return "<type>";
+
+# endif // GTEST_HAS_RTTI
+}
+
+#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+// AssertyTypeEq<T1, T2>::type is defined iff T1 and T2 are the same
+// type. This can be used as a compile-time assertion to ensure that
+// two types are equal.
+
+template <typename T1, typename T2>
+struct AssertTypeEq;
+
+template <typename T>
+struct AssertTypeEq<T, T> {
+ typedef bool type;
+};
+
+// A unique type used as the default value for the arguments of class
+// template Types. This allows us to simulate variadic templates
+// (e.g. Types<int>, Type<int, double>, and etc), which C++ doesn't
+// support directly.
+struct None {};
+
+// The following family of struct and struct templates are used to
+// represent type lists. In particular, TypesN<T1, T2, ..., TN>
+// represents a type list with N types (T1, T2, ..., and TN) in it.
+// Except for Types0, every struct in the family has two member types:
+// Head for the first type in the list, and Tail for the rest of the
+// list.
+
+// The empty type list.
+struct Types0 {};
+
+// Type lists of length 1, 2, 3, and so on.
+
+template <typename T1>
+struct Types1 {
+ typedef T1 Head;
+ typedef Types0 Tail;
+};
+
+$range i 2..n
+
+$for i [[
+$range j 1..i
+$range k 2..i
+template <$for j, [[typename T$j]]>
+struct Types$i {
+ typedef T1 Head;
+ typedef Types$(i-1)<$for k, [[T$k]]> Tail;
+};
+
+
+]]
+
+} // namespace internal
+
+// We don't want to require the users to write TypesN<...> directly,
+// as that would require them to count the length. Types<...> is much
+// easier to write, but generates horrible messages when there is a
+// compiler error, as gcc insists on printing out each template
+// argument, even if it has the default value (this means Types<int>
+// will appear as Types<int, None, None, ..., None> in the compiler
+// errors).
+//
+// Our solution is to combine the best part of the two approaches: a
+// user would write Types<T1, ..., TN>, and Google Test will translate
+// that to TypesN<T1, ..., TN> internally to make error messages
+// readable. The translation is done by the 'type' member of the
+// Types template.
+
+$range i 1..n
+template <$for i, [[typename T$i = internal::None]]>
+struct Types {
+ typedef internal::Types$n<$for i, [[T$i]]> type;
+};
+
+template <>
+struct Types<$for i, [[internal::None]]> {
+ typedef internal::Types0 type;
+};
+
+$range i 1..n-1
+$for i [[
+$range j 1..i
+$range k i+1..n
+template <$for j, [[typename T$j]]>
+struct Types<$for j, [[T$j]]$for k[[, internal::None]]> {
+ typedef internal::Types$i<$for j, [[T$j]]> type;
+};
+
+]]
+
+namespace internal {
+
+# define GTEST_TEMPLATE_ template <typename T> class
+
+// The template "selector" struct TemplateSel<Tmpl> is used to
+// represent Tmpl, which must be a class template with one type
+// parameter, as a type. TemplateSel<Tmpl>::Bind<T>::type is defined
+// as the type Tmpl<T>. This allows us to actually instantiate the
+// template "selected" by TemplateSel<Tmpl>.
+//
+// This trick is necessary for simulating typedef for class templates,
+// which C++ doesn't support directly.
+template <GTEST_TEMPLATE_ Tmpl>
+struct TemplateSel {
+ template <typename T>
+ struct Bind {
+ typedef Tmpl<T> type;
+ };
+};
+
+# define GTEST_BIND_(TmplSel, T) \
+ TmplSel::template Bind<T>::type
+
+// A unique struct template used as the default value for the
+// arguments of class template Templates. This allows us to simulate
+// variadic templates (e.g. Templates<int>, Templates<int, double>,
+// and etc), which C++ doesn't support directly.
+template <typename T>
+struct NoneT {};
+
+// The following family of struct and struct templates are used to
+// represent template lists. In particular, TemplatesN<T1, T2, ...,
+// TN> represents a list of N templates (T1, T2, ..., and TN). Except
+// for Templates0, every struct in the family has two member types:
+// Head for the selector of the first template in the list, and Tail
+// for the rest of the list.
+
+// The empty template list.
+struct Templates0 {};
+
+// Template lists of length 1, 2, 3, and so on.
+
+template <GTEST_TEMPLATE_ T1>
+struct Templates1 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates0 Tail;
+};
+
+$range i 2..n
+
+$for i [[
+$range j 1..i
+$range k 2..i
+template <$for j, [[GTEST_TEMPLATE_ T$j]]>
+struct Templates$i {
+ typedef TemplateSel<T1> Head;
+ typedef Templates$(i-1)<$for k, [[T$k]]> Tail;
+};
+
+
+]]
+
+// We don't want to require the users to write TemplatesN<...> directly,
+// as that would require them to count the length. Templates<...> is much
+// easier to write, but generates horrible messages when there is a
+// compiler error, as gcc insists on printing out each template
+// argument, even if it has the default value (this means Templates<list>
+// will appear as Templates<list, NoneT, NoneT, ..., NoneT> in the compiler
+// errors).
+//
+// Our solution is to combine the best part of the two approaches: a
+// user would write Templates<T1, ..., TN>, and Google Test will translate
+// that to TemplatesN<T1, ..., TN> internally to make error messages
+// readable. The translation is done by the 'type' member of the
+// Templates template.
+
+$range i 1..n
+template <$for i, [[GTEST_TEMPLATE_ T$i = NoneT]]>
+struct Templates {
+ typedef Templates$n<$for i, [[T$i]]> type;
+};
+
+template <>
+struct Templates<$for i, [[NoneT]]> {
+ typedef Templates0 type;
+};
+
+$range i 1..n-1
+$for i [[
+$range j 1..i
+$range k i+1..n
+template <$for j, [[GTEST_TEMPLATE_ T$j]]>
+struct Templates<$for j, [[T$j]]$for k[[, NoneT]]> {
+ typedef Templates$i<$for j, [[T$j]]> type;
+};
+
+]]
+
+// The TypeList template makes it possible to use either a single type
+// or a Types<...> list in TYPED_TEST_CASE() and
+// INSTANTIATE_TYPED_TEST_CASE_P().
+
+template <typename T>
+struct TypeList {
+ typedef Types1<T> type;
+};
+
+
+$range i 1..n
+template <$for i, [[typename T$i]]>
+struct TypeList<Types<$for i, [[T$i]]> > {
+ typedef typename Types<$for i, [[T$i]]>::type type;
+};
+
+#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
diff --git a/test/gtest/src/gtest-all.cc b/test/gtest/src/gtest-all.cc
new file mode 100644
index 0000000..0a9cee5
--- /dev/null
+++ b/test/gtest/src/gtest-all.cc
@@ -0,0 +1,48 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: mheule at google.com (Markus Heule)
+//
+// Google C++ Testing Framework (Google Test)
+//
+// Sometimes it's desirable to build Google Test by compiling a single file.
+// This file serves this purpose.
+
+// This line ensures that gtest.h can be compiled on its own, even
+// when it's fused.
+#include "gtest/gtest.h"
+
+// The following lines pull in the real gtest *.cc files.
+#include "src/gtest.cc"
+#include "src/gtest-death-test.cc"
+#include "src/gtest-filepath.cc"
+#include "src/gtest-port.cc"
+#include "src/gtest-printers.cc"
+#include "src/gtest-test-part.cc"
+#include "src/gtest-typed-test.cc"
diff --git a/test/gtest/src/gtest-death-test.cc b/test/gtest/src/gtest-death-test.cc
new file mode 100644
index 0000000..a6023fc
--- /dev/null
+++ b/test/gtest/src/gtest-death-test.cc
@@ -0,0 +1,1344 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan), vladl at google.com (Vlad Losev)
+//
+// This file implements death tests.
+
+#include "gtest/gtest-death-test.h"
+#include "gtest/internal/gtest-port.h"
+
+#if GTEST_HAS_DEATH_TEST
+
+# if GTEST_OS_MAC
+# include <crt_externs.h>
+# endif // GTEST_OS_MAC
+
+# include <errno.h>
+# include <fcntl.h>
+# include <limits.h>
+
+# if GTEST_OS_LINUX
+# include <signal.h>
+# endif // GTEST_OS_LINUX
+
+# include <stdarg.h>
+
+# if GTEST_OS_WINDOWS
+# include <windows.h>
+# else
+# include <sys/mman.h>
+# include <sys/wait.h>
+# endif // GTEST_OS_WINDOWS
+
+# if GTEST_OS_QNX
+# include <spawn.h>
+# endif // GTEST_OS_QNX
+
+#endif // GTEST_HAS_DEATH_TEST
+
+#include "gtest/gtest-message.h"
+#include "gtest/internal/gtest-string.h"
+
+// Indicates that this translation unit is part of Google Test's
+// implementation. It must come before gtest-internal-inl.h is
+// included, or there will be a compiler error. This trick is to
+// prevent a user from accidentally including gtest-internal-inl.h in
+// his code.
+#define GTEST_IMPLEMENTATION_ 1
+#include "src/gtest-internal-inl.h"
+#undef GTEST_IMPLEMENTATION_
+
+namespace testing {
+
+// Constants.
+
+// The default death test style.
+static const char kDefaultDeathTestStyle[] = "fast";
+
+GTEST_DEFINE_string_(
+ death_test_style,
+ internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle),
+ "Indicates how to run a death test in a forked child process: "
+ "\"threadsafe\" (child process re-executes the test binary "
+ "from the beginning, running only the specific death test) or "
+ "\"fast\" (child process runs the death test immediately "
+ "after forking).");
+
+GTEST_DEFINE_bool_(
+ death_test_use_fork,
+ internal::BoolFromGTestEnv("death_test_use_fork", false),
+ "Instructs to use fork()/_exit() instead of clone() in death tests. "
+ "Ignored and always uses fork() on POSIX systems where clone() is not "
+ "implemented. Useful when running under valgrind or similar tools if "
+ "those do not support clone(). Valgrind 3.3.1 will just fail if "
+ "it sees an unsupported combination of clone() flags. "
+ "It is not recommended to use this flag w/o valgrind though it will "
+ "work in 99% of the cases. Once valgrind is fixed, this flag will "
+ "most likely be removed.");
+
+namespace internal {
+GTEST_DEFINE_string_(
+ internal_run_death_test, "",
+ "Indicates the file, line number, temporal index of "
+ "the single death test to run, and a file descriptor to "
+ "which a success code may be sent, all separated by "
+ "the '|' characters. This flag is specified if and only if the current "
+ "process is a sub-process launched for running a thread-safe "
+ "death test. FOR INTERNAL USE ONLY.");
+} // namespace internal
+
+#if GTEST_HAS_DEATH_TEST
+
+namespace internal {
+
+// Valid only for fast death tests. Indicates the code is running in the
+// child process of a fast style death test.
+static bool g_in_fast_death_test_child = false;
+
+// Returns a Boolean value indicating whether the caller is currently
+// executing in the context of the death test child process. Tools such as
+// Valgrind heap checkers may need this to modify their behavior in death
+// tests. IMPORTANT: This is an internal utility. Using it may break the
+// implementation of death tests. User code MUST NOT use it.
+bool InDeathTestChild() {
+# if GTEST_OS_WINDOWS
+
+ // On Windows, death tests are thread-safe regardless of the value of the
+ // death_test_style flag.
+ return !GTEST_FLAG(internal_run_death_test).empty();
+
+# else
+
+ if (GTEST_FLAG(death_test_style) == "threadsafe")
+ return !GTEST_FLAG(internal_run_death_test).empty();
+ else
+ return g_in_fast_death_test_child;
+#endif
+}
+
+} // namespace internal
+
+// ExitedWithCode constructor.
+ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) {
+}
+
+// ExitedWithCode function-call operator.
+bool ExitedWithCode::operator()(int exit_status) const {
+# if GTEST_OS_WINDOWS
+
+ return exit_status == exit_code_;
+
+# else
+
+ return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_;
+
+# endif // GTEST_OS_WINDOWS
+}
+
+# if !GTEST_OS_WINDOWS
+// KilledBySignal constructor.
+KilledBySignal::KilledBySignal(int signum) : signum_(signum) {
+}
+
+// KilledBySignal function-call operator.
+bool KilledBySignal::operator()(int exit_status) const {
+ return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_;
+}
+# endif // !GTEST_OS_WINDOWS
+
+namespace internal {
+
+// Utilities needed for death tests.
+
+// Generates a textual description of a given exit code, in the format
+// specified by wait(2).
+static std::string ExitSummary(int exit_code) {
+ Message m;
+
+# if GTEST_OS_WINDOWS
+
+ m << "Exited with exit status " << exit_code;
+
+# else
+
+ if (WIFEXITED(exit_code)) {
+ m << "Exited with exit status " << WEXITSTATUS(exit_code);
+ } else if (WIFSIGNALED(exit_code)) {
+ m << "Terminated by signal " << WTERMSIG(exit_code);
+ }
+# ifdef WCOREDUMP
+ if (WCOREDUMP(exit_code)) {
+ m << " (core dumped)";
+ }
+# endif
+# endif // GTEST_OS_WINDOWS
+
+ return m.GetString();
+}
+
+// Returns true if exit_status describes a process that was terminated
+// by a signal, or exited normally with a nonzero exit code.
+bool ExitedUnsuccessfully(int exit_status) {
+ return !ExitedWithCode(0)(exit_status);
+}
+
+# if !GTEST_OS_WINDOWS
+// Generates a textual failure message when a death test finds more than
+// one thread running, or cannot determine the number of threads, prior
+// to executing the given statement. It is the responsibility of the
+// caller not to pass a thread_count of 1.
+static std::string DeathTestThreadWarning(size_t thread_count) {
+ Message msg;
+ msg << "Death tests use fork(), which is unsafe particularly"
+ << " in a threaded context. For this test, " << GTEST_NAME_ << " ";
+ if (thread_count == 0)
+ msg << "couldn't detect the number of threads.";
+ else
+ msg << "detected " << thread_count << " threads.";
+ return msg.GetString();
+}
+# endif // !GTEST_OS_WINDOWS
+
+// Flag characters for reporting a death test that did not die.
+static const char kDeathTestLived = 'L';
+static const char kDeathTestReturned = 'R';
+static const char kDeathTestThrew = 'T';
+static const char kDeathTestInternalError = 'I';
+
+// An enumeration describing all of the possible ways that a death test can
+// conclude. DIED means that the process died while executing the test
+// code; LIVED means that process lived beyond the end of the test code;
+// RETURNED means that the test statement attempted to execute a return
+// statement, which is not allowed; THREW means that the test statement
+// returned control by throwing an exception. IN_PROGRESS means the test
+// has not yet concluded.
+// TODO(vladl at google.com): Unify names and possibly values for
+// AbortReason, DeathTestOutcome, and flag characters above.
+enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW };
+
+// Routine for aborting the program which is safe to call from an
+// exec-style death test child process, in which case the error
+// message is propagated back to the parent process. Otherwise, the
+// message is simply printed to stderr. In either case, the program
+// then exits with status 1.
+void DeathTestAbort(const std::string& message) {
+ // On a POSIX system, this function may be called from a threadsafe-style
+ // death test child process, which operates on a very small stack. Use
+ // the heap for any additional non-minuscule memory requirements.
+ const InternalRunDeathTestFlag* const flag =
+ GetUnitTestImpl()->internal_run_death_test_flag();
+ if (flag != NULL) {
+ FILE* parent = posix::FDOpen(flag->write_fd(), "w");
+ fputc(kDeathTestInternalError, parent);
+ fprintf(parent, "%s", message.c_str());
+ fflush(parent);
+ _exit(1);
+ } else {
+ fprintf(stderr, "%s", message.c_str());
+ fflush(stderr);
+ posix::Abort();
+ }
+}
+
+// A replacement for CHECK that calls DeathTestAbort if the assertion
+// fails.
+# define GTEST_DEATH_TEST_CHECK_(expression) \
+ do { \
+ if (!::testing::internal::IsTrue(expression)) { \
+ DeathTestAbort( \
+ ::std::string("CHECK failed: File ") + __FILE__ + ", line " \
+ + ::testing::internal::StreamableToString(__LINE__) + ": " \
+ + #expression); \
+ } \
+ } while (::testing::internal::AlwaysFalse())
+
+// This macro is similar to GTEST_DEATH_TEST_CHECK_, but it is meant for
+// evaluating any system call that fulfills two conditions: it must return
+// -1 on failure, and set errno to EINTR when it is interrupted and
+// should be tried again. The macro expands to a loop that repeatedly
+// evaluates the expression as long as it evaluates to -1 and sets
+// errno to EINTR. If the expression evaluates to -1 but errno is
+// something other than EINTR, DeathTestAbort is called.
+# define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \
+ do { \
+ int gtest_retval; \
+ do { \
+ gtest_retval = (expression); \
+ } while (gtest_retval == -1 && errno == EINTR); \
+ if (gtest_retval == -1) { \
+ DeathTestAbort( \
+ ::std::string("CHECK failed: File ") + __FILE__ + ", line " \
+ + ::testing::internal::StreamableToString(__LINE__) + ": " \
+ + #expression + " != -1"); \
+ } \
+ } while (::testing::internal::AlwaysFalse())
+
+// Returns the message describing the last system error in errno.
+std::string GetLastErrnoDescription() {
+ return errno == 0 ? "" : posix::StrError(errno);
+}
+
+// This is called from a death test parent process to read a failure
+// message from the death test child process and log it with the FATAL
+// severity. On Windows, the message is read from a pipe handle. On other
+// platforms, it is read from a file descriptor.
+static void FailFromInternalError(int fd) {
+ Message error;
+ char buffer[256];
+ int num_read;
+
+ do {
+ while ((num_read = posix::Read(fd, buffer, 255)) > 0) {
+ buffer[num_read] = '\0';
+ error << buffer;
+ }
+ } while (num_read == -1 && errno == EINTR);
+
+ if (num_read == 0) {
+ GTEST_LOG_(FATAL) << error.GetString();
+ } else {
+ const int last_error = errno;
+ GTEST_LOG_(FATAL) << "Error while reading death test internal: "
+ << GetLastErrnoDescription() << " [" << last_error << "]";
+ }
+}
+
+// Death test constructor. Increments the running death test count
+// for the current test.
+DeathTest::DeathTest() {
+ TestInfo* const info = GetUnitTestImpl()->current_test_info();
+ if (info == NULL) {
+ DeathTestAbort("Cannot run a death test outside of a TEST or "
+ "TEST_F construct");
+ }
+}
+
+// Creates and returns a death test by dispatching to the current
+// death test factory.
+bool DeathTest::Create(const char* statement, const RE* regex,
+ const char* file, int line, DeathTest** test) {
+ return GetUnitTestImpl()->death_test_factory()->Create(
+ statement, regex, file, line, test);
+}
+
+const char* DeathTest::LastMessage() {
+ return last_death_test_message_.c_str();
+}
+
+void DeathTest::set_last_death_test_message(const std::string& message) {
+ last_death_test_message_ = message;
+}
+
+std::string DeathTest::last_death_test_message_;
+
+// Provides cross platform implementation for some death functionality.
+class DeathTestImpl : public DeathTest {
+ protected:
+ DeathTestImpl(const char* a_statement, const RE* a_regex)
+ : statement_(a_statement),
+ regex_(a_regex),
+ spawned_(false),
+ status_(-1),
+ outcome_(IN_PROGRESS),
+ read_fd_(-1),
+ write_fd_(-1) {}
+
+ // read_fd_ is expected to be closed and cleared by a derived class.
+ ~DeathTestImpl() { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); }
+
+ void Abort(AbortReason reason);
+ virtual bool Passed(bool status_ok);
+
+ const char* statement() const { return statement_; }
+ const RE* regex() const { return regex_; }
+ bool spawned() const { return spawned_; }
+ void set_spawned(bool is_spawned) { spawned_ = is_spawned; }
+ int status() const { return status_; }
+ void set_status(int a_status) { status_ = a_status; }
+ DeathTestOutcome outcome() const { return outcome_; }
+ void set_outcome(DeathTestOutcome an_outcome) { outcome_ = an_outcome; }
+ int read_fd() const { return read_fd_; }
+ void set_read_fd(int fd) { read_fd_ = fd; }
+ int write_fd() const { return write_fd_; }
+ void set_write_fd(int fd) { write_fd_ = fd; }
+
+ // Called in the parent process only. Reads the result code of the death
+ // test child process via a pipe, interprets it to set the outcome_
+ // member, and closes read_fd_. Outputs diagnostics and terminates in
+ // case of unexpected codes.
+ void ReadAndInterpretStatusByte();
+
+ private:
+ // The textual content of the code this object is testing. This class
+ // doesn't own this string and should not attempt to delete it.
+ const char* const statement_;
+ // The regular expression which test output must match. DeathTestImpl
+ // doesn't own this object and should not attempt to delete it.
+ const RE* const regex_;
+ // True if the death test child process has been successfully spawned.
+ bool spawned_;
+ // The exit status of the child process.
+ int status_;
+ // How the death test concluded.
+ DeathTestOutcome outcome_;
+ // Descriptor to the read end of the pipe to the child process. It is
+ // always -1 in the child process. The child keeps its write end of the
+ // pipe in write_fd_.
+ int read_fd_;
+ // Descriptor to the child's write end of the pipe to the parent process.
+ // It is always -1 in the parent process. The parent keeps its end of the
+ // pipe in read_fd_.
+ int write_fd_;
+};
+
+// Called in the parent process only. Reads the result code of the death
+// test child process via a pipe, interprets it to set the outcome_
+// member, and closes read_fd_. Outputs diagnostics and terminates in
+// case of unexpected codes.
+void DeathTestImpl::ReadAndInterpretStatusByte() {
+ char flag;
+ int bytes_read;
+
+ // The read() here blocks until data is available (signifying the
+ // failure of the death test) or until the pipe is closed (signifying
+ // its success), so it's okay to call this in the parent before
+ // the child process has exited.
+ do {
+ bytes_read = posix::Read(read_fd(), &flag, 1);
+ } while (bytes_read == -1 && errno == EINTR);
+
+ if (bytes_read == 0) {
+ set_outcome(DIED);
+ } else if (bytes_read == 1) {
+ switch (flag) {
+ case kDeathTestReturned:
+ set_outcome(RETURNED);
+ break;
+ case kDeathTestThrew:
+ set_outcome(THREW);
+ break;
+ case kDeathTestLived:
+ set_outcome(LIVED);
+ break;
+ case kDeathTestInternalError:
+ FailFromInternalError(read_fd()); // Does not return.
+ break;
+ default:
+ GTEST_LOG_(FATAL) << "Death test child process reported "
+ << "unexpected status byte ("
+ << static_cast<unsigned int>(flag) << ")";
+ }
+ } else {
+ GTEST_LOG_(FATAL) << "Read from death test child process failed: "
+ << GetLastErrnoDescription();
+ }
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(read_fd()));
+ set_read_fd(-1);
+}
+
+// Signals that the death test code which should have exited, didn't.
+// Should be called only in a death test child process.
+// Writes a status byte to the child's status file descriptor, then
+// calls _exit(1).
+void DeathTestImpl::Abort(AbortReason reason) {
+ // The parent process considers the death test to be a failure if
+ // it finds any data in our pipe. So, here we write a single flag byte
+ // to the pipe, then exit.
+ const char status_ch =
+ reason == TEST_DID_NOT_DIE ? kDeathTestLived :
+ reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned;
+
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1));
+ // We are leaking the descriptor here because on some platforms (i.e.,
+ // when built as Windows DLL), destructors of global objects will still
+ // run after calling _exit(). On such systems, write_fd_ will be
+ // indirectly closed from the destructor of UnitTestImpl, causing double
+ // close if it is also closed here. On debug configurations, double close
+ // may assert. As there are no in-process buffers to flush here, we are
+ // relying on the OS to close the descriptor after the process terminates
+ // when the destructors are not run.
+ _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash)
+}
+
+// Returns an indented copy of stderr output for a death test.
+// This makes distinguishing death test output lines from regular log lines
+// much easier.
+static ::std::string FormatDeathTestOutput(const ::std::string& output) {
+ ::std::string ret;
+ for (size_t at = 0; ; ) {
+ const size_t line_end = output.find('\n', at);
+ ret += "[ DEATH ] ";
+ if (line_end == ::std::string::npos) {
+ ret += output.substr(at);
+ break;
+ }
+ ret += output.substr(at, line_end + 1 - at);
+ at = line_end + 1;
+ }
+ return ret;
+}
+
+// Assesses the success or failure of a death test, using both private
+// members which have previously been set, and one argument:
+//
+// Private data members:
+// outcome: An enumeration describing how the death test
+// concluded: DIED, LIVED, THREW, or RETURNED. The death test
+// fails in the latter three cases.
+// status: The exit status of the child process. On *nix, it is in the
+// in the format specified by wait(2). On Windows, this is the
+// value supplied to the ExitProcess() API or a numeric code
+// of the exception that terminated the program.
+// regex: A regular expression object to be applied to
+// the test's captured standard error output; the death test
+// fails if it does not match.
+//
+// Argument:
+// status_ok: true if exit_status is acceptable in the context of
+// this particular death test, which fails if it is false
+//
+// Returns true iff all of the above conditions are met. Otherwise, the
+// first failing condition, in the order given above, is the one that is
+// reported. Also sets the last death test message string.
+bool DeathTestImpl::Passed(bool status_ok) {
+ if (!spawned())
+ return false;
+
+ const std::string error_message = GetCapturedStderr();
+
+ bool success = false;
+ Message buffer;
+
+ buffer << "Death test: " << statement() << "\n";
+ switch (outcome()) {
+ case LIVED:
+ buffer << " Result: failed to die.\n"
+ << " Error msg:\n" << FormatDeathTestOutput(error_message);
+ break;
+ case THREW:
+ buffer << " Result: threw an exception.\n"
+ << " Error msg:\n" << FormatDeathTestOutput(error_message);
+ break;
+ case RETURNED:
+ buffer << " Result: illegal return in test statement.\n"
+ << " Error msg:\n" << FormatDeathTestOutput(error_message);
+ break;
+ case DIED:
+ if (status_ok) {
+ const bool matched = RE::PartialMatch(error_message.c_str(), *regex());
+ if (matched) {
+ success = true;
+ } else {
+ buffer << " Result: died but not with expected error.\n"
+ << " Expected: " << regex()->pattern() << "\n"
+ << "Actual msg:\n" << FormatDeathTestOutput(error_message);
+ }
+ } else {
+ buffer << " Result: died but not with expected exit code:\n"
+ << " " << ExitSummary(status()) << "\n"
+ << "Actual msg:\n" << FormatDeathTestOutput(error_message);
+ }
+ break;
+ case IN_PROGRESS:
+ default:
+ GTEST_LOG_(FATAL)
+ << "DeathTest::Passed somehow called before conclusion of test";
+ }
+
+ DeathTest::set_last_death_test_message(buffer.GetString());
+ return success;
+}
+
+# if GTEST_OS_WINDOWS
+// WindowsDeathTest implements death tests on Windows. Due to the
+// specifics of starting new processes on Windows, death tests there are
+// always threadsafe, and Google Test considers the
+// --gtest_death_test_style=fast setting to be equivalent to
+// --gtest_death_test_style=threadsafe there.
+//
+// A few implementation notes: Like the Linux version, the Windows
+// implementation uses pipes for child-to-parent communication. But due to
+// the specifics of pipes on Windows, some extra steps are required:
+//
+// 1. The parent creates a communication pipe and stores handles to both
+// ends of it.
+// 2. The parent starts the child and provides it with the information
+// necessary to acquire the handle to the write end of the pipe.
+// 3. The child acquires the write end of the pipe and signals the parent
+// using a Windows event.
+// 4. Now the parent can release the write end of the pipe on its side. If
+// this is done before step 3, the object's reference count goes down to
+// 0 and it is destroyed, preventing the child from acquiring it. The
+// parent now has to release it, or read operations on the read end of
+// the pipe will not return when the child terminates.
+// 5. The parent reads child's output through the pipe (outcome code and
+// any possible error messages) from the pipe, and its stderr and then
+// determines whether to fail the test.
+//
+// Note: to distinguish Win32 API calls from the local method and function
+// calls, the former are explicitly resolved in the global namespace.
+//
+class WindowsDeathTest : public DeathTestImpl {
+ public:
+ WindowsDeathTest(const char* a_statement,
+ const RE* a_regex,
+ const char* file,
+ int line)
+ : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {}
+
+ // All of these virtual functions are inherited from DeathTest.
+ virtual int Wait();
+ virtual TestRole AssumeRole();
+
+ private:
+ // The name of the file in which the death test is located.
+ const char* const file_;
+ // The line number on which the death test is located.
+ const int line_;
+ // Handle to the write end of the pipe to the child process.
+ AutoHandle write_handle_;
+ // Child process handle.
+ AutoHandle child_handle_;
+ // Event the child process uses to signal the parent that it has
+ // acquired the handle to the write end of the pipe. After seeing this
+ // event the parent can release its own handles to make sure its
+ // ReadFile() calls return when the child terminates.
+ AutoHandle event_handle_;
+};
+
+// Waits for the child in a death test to exit, returning its exit
+// status, or 0 if no child process exists. As a side effect, sets the
+// outcome data member.
+int WindowsDeathTest::Wait() {
+ if (!spawned())
+ return 0;
+
+ // Wait until the child either signals that it has acquired the write end
+ // of the pipe or it dies.
+ const HANDLE wait_handles[2] = { child_handle_.Get(), event_handle_.Get() };
+ switch (::WaitForMultipleObjects(2,
+ wait_handles,
+ FALSE, // Waits for any of the handles.
+ INFINITE)) {
+ case WAIT_OBJECT_0:
+ case WAIT_OBJECT_0 + 1:
+ break;
+ default:
+ GTEST_DEATH_TEST_CHECK_(false); // Should not get here.
+ }
+
+ // The child has acquired the write end of the pipe or exited.
+ // We release the handle on our side and continue.
+ write_handle_.Reset();
+ event_handle_.Reset();
+
+ ReadAndInterpretStatusByte();
+
+ // Waits for the child process to exit if it haven't already. This
+ // returns immediately if the child has already exited, regardless of
+ // whether previous calls to WaitForMultipleObjects synchronized on this
+ // handle or not.
+ GTEST_DEATH_TEST_CHECK_(
+ WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(),
+ INFINITE));
+ DWORD status_code;
+ GTEST_DEATH_TEST_CHECK_(
+ ::GetExitCodeProcess(child_handle_.Get(), &status_code) != FALSE);
+ child_handle_.Reset();
+ set_status(static_cast<int>(status_code));
+ return status();
+}
+
+// The AssumeRole process for a Windows death test. It creates a child
+// process with the same executable as the current process to run the
+// death test. The child process is given the --gtest_filter and
+// --gtest_internal_run_death_test flags such that it knows to run the
+// current death test only.
+DeathTest::TestRole WindowsDeathTest::AssumeRole() {
+ const UnitTestImpl* const impl = GetUnitTestImpl();
+ const InternalRunDeathTestFlag* const flag =
+ impl->internal_run_death_test_flag();
+ const TestInfo* const info = impl->current_test_info();
+ const int death_test_index = info->result()->death_test_count();
+
+ if (flag != NULL) {
+ // ParseInternalRunDeathTestFlag() has performed all the necessary
+ // processing.
+ set_write_fd(flag->write_fd());
+ return EXECUTE_TEST;
+ }
+
+ // WindowsDeathTest uses an anonymous pipe to communicate results of
+ // a death test.
+ SECURITY_ATTRIBUTES handles_are_inheritable = {
+ sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
+ HANDLE read_handle, write_handle;
+ GTEST_DEATH_TEST_CHECK_(
+ ::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable,
+ 0) // Default buffer size.
+ != FALSE);
+ set_read_fd(::_open_osfhandle(reinterpret_cast<intptr_t>(read_handle),
+ O_RDONLY));
+ write_handle_.Reset(write_handle);
+ event_handle_.Reset(::CreateEvent(
+ &handles_are_inheritable,
+ TRUE, // The event will automatically reset to non-signaled state.
+ FALSE, // The initial state is non-signalled.
+ NULL)); // The even is unnamed.
+ GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL);
+ const std::string filter_flag =
+ std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" +
+ info->test_case_name() + "." + info->name();
+ const std::string internal_flag =
+ std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag +
+ "=" + file_ + "|" + StreamableToString(line_) + "|" +
+ StreamableToString(death_test_index) + "|" +
+ StreamableToString(static_cast<unsigned int>(::GetCurrentProcessId())) +
+ // size_t has the same width as pointers on both 32-bit and 64-bit
+ // Windows platforms.
+ // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx.
+ "|" + StreamableToString(reinterpret_cast<size_t>(write_handle)) +
+ "|" + StreamableToString(reinterpret_cast<size_t>(event_handle_.Get()));
+
+ char executable_path[_MAX_PATH + 1]; // NOLINT
+ GTEST_DEATH_TEST_CHECK_(
+ _MAX_PATH + 1 != ::GetModuleFileNameA(NULL,
+ executable_path,
+ _MAX_PATH));
+
+ std::string command_line =
+ std::string(::GetCommandLineA()) + " " + filter_flag + " \"" +
+ internal_flag + "\"";
+
+ DeathTest::set_last_death_test_message("");
+
+ CaptureStderr();
+ // Flush the log buffers since the log streams are shared with the child.
+ FlushInfoLog();
+
+ // The child process will share the standard handles with the parent.
+ STARTUPINFOA startup_info;
+ memset(&startup_info, 0, sizeof(STARTUPINFO));
+ startup_info.dwFlags = STARTF_USESTDHANDLES;
+ startup_info.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE);
+ startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
+ startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
+
+ PROCESS_INFORMATION process_info;
+ GTEST_DEATH_TEST_CHECK_(::CreateProcessA(
+ executable_path,
+ const_cast<char*>(command_line.c_str()),
+ NULL, // Retuned process handle is not inheritable.
+ NULL, // Retuned thread handle is not inheritable.
+ TRUE, // Child inherits all inheritable handles (for write_handle_).
+ 0x0, // Default creation flags.
+ NULL, // Inherit the parent's environment.
+ UnitTest::GetInstance()->original_working_dir(),
+ &startup_info,
+ &process_info) != FALSE);
+ child_handle_.Reset(process_info.hProcess);
+ ::CloseHandle(process_info.hThread);
+ set_spawned(true);
+ return OVERSEE_TEST;
+}
+# else // We are not on Windows.
+
+// ForkingDeathTest provides implementations for most of the abstract
+// methods of the DeathTest interface. Only the AssumeRole method is
+// left undefined.
+class ForkingDeathTest : public DeathTestImpl {
+ public:
+ ForkingDeathTest(const char* statement, const RE* regex);
+
+ // All of these virtual functions are inherited from DeathTest.
+ virtual int Wait();
+
+ protected:
+ void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; }
+
+ private:
+ // PID of child process during death test; 0 in the child process itself.
+ pid_t child_pid_;
+};
+
+// Constructs a ForkingDeathTest.
+ForkingDeathTest::ForkingDeathTest(const char* a_statement, const RE* a_regex)
+ : DeathTestImpl(a_statement, a_regex),
+ child_pid_(-1) {}
+
+// Waits for the child in a death test to exit, returning its exit
+// status, or 0 if no child process exists. As a side effect, sets the
+// outcome data member.
+int ForkingDeathTest::Wait() {
+ if (!spawned())
+ return 0;
+
+ ReadAndInterpretStatusByte();
+
+ int status_value;
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_value, 0));
+ set_status(status_value);
+ return status_value;
+}
+
+// A concrete death test class that forks, then immediately runs the test
+// in the child process.
+class NoExecDeathTest : public ForkingDeathTest {
+ public:
+ NoExecDeathTest(const char* a_statement, const RE* a_regex) :
+ ForkingDeathTest(a_statement, a_regex) { }
+ virtual TestRole AssumeRole();
+};
+
+// The AssumeRole process for a fork-and-run death test. It implements a
+// straightforward fork, with a simple pipe to transmit the status byte.
+DeathTest::TestRole NoExecDeathTest::AssumeRole() {
+ const size_t thread_count = GetThreadCount();
+ if (thread_count != 1) {
+ GTEST_LOG_(WARNING) << DeathTestThreadWarning(thread_count);
+ }
+
+ int pipe_fd[2];
+ GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1);
+
+ DeathTest::set_last_death_test_message("");
+ CaptureStderr();
+ // When we fork the process below, the log file buffers are copied, but the
+ // file descriptors are shared. We flush all log files here so that closing
+ // the file descriptors in the child process doesn't throw off the
+ // synchronization between descriptors and buffers in the parent process.
+ // This is as close to the fork as possible to avoid a race condition in case
+ // there are multiple threads running before the death test, and another
+ // thread writes to the log file.
+ FlushInfoLog();
+
+ const pid_t child_pid = fork();
+ GTEST_DEATH_TEST_CHECK_(child_pid != -1);
+ set_child_pid(child_pid);
+ if (child_pid == 0) {
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0]));
+ set_write_fd(pipe_fd[1]);
+ // Redirects all logging to stderr in the child process to prevent
+ // concurrent writes to the log files. We capture stderr in the parent
+ // process and append the child process' output to a log.
+ LogToStderr();
+ // Event forwarding to the listeners of event listener API mush be shut
+ // down in death test subprocesses.
+ GetUnitTestImpl()->listeners()->SuppressEventForwarding();
+ g_in_fast_death_test_child = true;
+ return EXECUTE_TEST;
+ } else {
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1]));
+ set_read_fd(pipe_fd[0]);
+ set_spawned(true);
+ return OVERSEE_TEST;
+ }
+}
+
+// A concrete death test class that forks and re-executes the main
+// program from the beginning, with command-line flags set that cause
+// only this specific death test to be run.
+class ExecDeathTest : public ForkingDeathTest {
+ public:
+ ExecDeathTest(const char* a_statement, const RE* a_regex,
+ const char* file, int line) :
+ ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { }
+ virtual TestRole AssumeRole();
+ private:
+ static ::std::vector<testing::internal::string>
+ GetArgvsForDeathTestChildProcess() {
+ ::std::vector<testing::internal::string> args = GetInjectableArgvs();
+ return args;
+ }
+ // The name of the file in which the death test is located.
+ const char* const file_;
+ // The line number on which the death test is located.
+ const int line_;
+};
+
+// Utility class for accumulating command-line arguments.
+class Arguments {
+ public:
+ Arguments() {
+ args_.push_back(NULL);
+ }
+
+ ~Arguments() {
+ for (std::vector<char*>::iterator i = args_.begin(); i != args_.end();
+ ++i) {
+ free(*i);
+ }
+ }
+ void AddArgument(const char* argument) {
+ args_.insert(args_.end() - 1, posix::StrDup(argument));
+ }
+
+ template <typename Str>
+ void AddArguments(const ::std::vector<Str>& arguments) {
+ for (typename ::std::vector<Str>::const_iterator i = arguments.begin();
+ i != arguments.end();
+ ++i) {
+ args_.insert(args_.end() - 1, posix::StrDup(i->c_str()));
+ }
+ }
+ char* const* Argv() {
+ return &args_[0];
+ }
+
+ private:
+ std::vector<char*> args_;
+};
+
+// A struct that encompasses the arguments to the child process of a
+// threadsafe-style death test process.
+struct ExecDeathTestArgs {
+ char* const* argv; // Command-line arguments for the child's call to exec
+ int close_fd; // File descriptor to close; the read end of a pipe
+};
+
+# if GTEST_OS_MAC
+inline char** GetEnviron() {
+ // When Google Test is built as a framework on MacOS X, the environ variable
+ // is unavailable. Apple's documentation (man environ) recommends using
+ // _NSGetEnviron() instead.
+ return *_NSGetEnviron();
+}
+# else
+// Some POSIX platforms expect you to declare environ. extern "C" makes
+// it reside in the global namespace.
+extern "C" char** environ;
+inline char** GetEnviron() { return environ; }
+# endif // GTEST_OS_MAC
+
+# if !GTEST_OS_QNX
+// The main function for a threadsafe-style death test child process.
+// This function is called in a clone()-ed process and thus must avoid
+// any potentially unsafe operations like malloc or libc functions.
+static int ExecDeathTestChildMain(void* child_arg) {
+ ExecDeathTestArgs* const args = static_cast<ExecDeathTestArgs*>(child_arg);
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd));
+
+ // We need to execute the test program in the same environment where
+ // it was originally invoked. Therefore we change to the original
+ // working directory first.
+ const char* const original_dir =
+ UnitTest::GetInstance()->original_working_dir();
+ // We can safely call chdir() as it's a direct system call.
+ if (chdir(original_dir) != 0) {
+ DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " +
+ GetLastErrnoDescription());
+ return EXIT_FAILURE;
+ }
+
+ // We can safely call execve() as it's a direct system call. We
+ // cannot use execvp() as it's a libc function and thus potentially
+ // unsafe. Since execve() doesn't search the PATH, the user must
+ // invoke the test program via a valid path that contains at least
+ // one path separator.
+ execve(args->argv[0], args->argv, GetEnviron());
+ DeathTestAbort(std::string("execve(") + args->argv[0] + ", ...) in " +
+ original_dir + " failed: " +
+ GetLastErrnoDescription());
+ return EXIT_FAILURE;
+}
+# endif // !GTEST_OS_QNX
+
+// Two utility routines that together determine the direction the stack
+// grows.
+// This could be accomplished more elegantly by a single recursive
+// function, but we want to guard against the unlikely possibility of
+// a smart compiler optimizing the recursion away.
+//
+// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining
+// StackLowerThanAddress into StackGrowsDown, which then doesn't give
+// correct answer.
+void StackLowerThanAddress(const void* ptr, bool* result) GTEST_NO_INLINE_;
+void StackLowerThanAddress(const void* ptr, bool* result) {
+ int dummy;
+ *result = (&dummy < ptr);
+}
+
+bool StackGrowsDown() {
+ int dummy;
+ bool result;
+ StackLowerThanAddress(&dummy, &result);
+ return result;
+}
+
+// Spawns a child process with the same executable as the current process in
+// a thread-safe manner and instructs it to run the death test. The
+// implementation uses fork(2) + exec. On systems where clone(2) is
+// available, it is used instead, being slightly more thread-safe. On QNX,
+// fork supports only single-threaded environments, so this function uses
+// spawn(2) there instead. The function dies with an error message if
+// anything goes wrong.
+static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) {
+ ExecDeathTestArgs args = { argv, close_fd };
+ pid_t child_pid = -1;
+
+# if GTEST_OS_QNX
+ // Obtains the current directory and sets it to be closed in the child
+ // process.
+ const int cwd_fd = open(".", O_RDONLY);
+ GTEST_DEATH_TEST_CHECK_(cwd_fd != -1);
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(cwd_fd, F_SETFD, FD_CLOEXEC));
+ // We need to execute the test program in the same environment where
+ // it was originally invoked. Therefore we change to the original
+ // working directory first.
+ const char* const original_dir =
+ UnitTest::GetInstance()->original_working_dir();
+ // We can safely call chdir() as it's a direct system call.
+ if (chdir(original_dir) != 0) {
+ DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " +
+ GetLastErrnoDescription());
+ return EXIT_FAILURE;
+ }
+
+ int fd_flags;
+ // Set close_fd to be closed after spawn.
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(fd_flags = fcntl(close_fd, F_GETFD));
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(close_fd, F_SETFD,
+ fd_flags | FD_CLOEXEC));
+ struct inheritance inherit = {0};
+ // spawn is a system call.
+ child_pid = spawn(args.argv[0], 0, NULL, &inherit, args.argv, GetEnviron());
+ // Restores the current working directory.
+ GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1);
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd));
+
+# else // GTEST_OS_QNX
+# if GTEST_OS_LINUX
+ // When a SIGPROF signal is received while fork() or clone() are executing,
+ // the process may hang. To avoid this, we ignore SIGPROF here and re-enable
+ // it after the call to fork()/clone() is complete.
+ struct sigaction saved_sigprof_action;
+ struct sigaction ignore_sigprof_action;
+ memset(&ignore_sigprof_action, 0, sizeof(ignore_sigprof_action));
+ sigemptyset(&ignore_sigprof_action.sa_mask);
+ ignore_sigprof_action.sa_handler = SIG_IGN;
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(sigaction(
+ SIGPROF, &ignore_sigprof_action, &saved_sigprof_action));
+# endif // GTEST_OS_LINUX
+
+# if GTEST_HAS_CLONE
+ const bool use_fork = GTEST_FLAG(death_test_use_fork);
+
+ if (!use_fork) {
+ static const bool stack_grows_down = StackGrowsDown();
+ const size_t stack_size = getpagesize();
+ // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead.
+ void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+ GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED);
+
+ // Maximum stack alignment in bytes: For a downward-growing stack, this
+ // amount is subtracted from size of the stack space to get an address
+ // that is within the stack space and is aligned on all systems we care
+ // about. As far as I know there is no ABI with stack alignment greater
+ // than 64. We assume stack and stack_size already have alignment of
+ // kMaxStackAlignment.
+ const size_t kMaxStackAlignment = 64;
+ void* const stack_top =
+ static_cast<char*>(stack) +
+ (stack_grows_down ? stack_size - kMaxStackAlignment : 0);
+ GTEST_DEATH_TEST_CHECK_(stack_size > kMaxStackAlignment &&
+ reinterpret_cast<intptr_t>(stack_top) % kMaxStackAlignment == 0);
+
+ child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args);
+
+ GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1);
+ }
+# else
+ const bool use_fork = true;
+# endif // GTEST_HAS_CLONE
+
+ if (use_fork && (child_pid = fork()) == 0) {
+ ExecDeathTestChildMain(&args);
+ _exit(0);
+ }
+# endif // GTEST_OS_QNX
+# if GTEST_OS_LINUX
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(
+ sigaction(SIGPROF, &saved_sigprof_action, NULL));
+# endif // GTEST_OS_LINUX
+
+ GTEST_DEATH_TEST_CHECK_(child_pid != -1);
+ return child_pid;
+}
+
+// The AssumeRole process for a fork-and-exec death test. It re-executes the
+// main program from the beginning, setting the --gtest_filter
+// and --gtest_internal_run_death_test flags to cause only the current
+// death test to be re-run.
+DeathTest::TestRole ExecDeathTest::AssumeRole() {
+ const UnitTestImpl* const impl = GetUnitTestImpl();
+ const InternalRunDeathTestFlag* const flag =
+ impl->internal_run_death_test_flag();
+ const TestInfo* const info = impl->current_test_info();
+ const int death_test_index = info->result()->death_test_count();
+
+ if (flag != NULL) {
+ set_write_fd(flag->write_fd());
+ return EXECUTE_TEST;
+ }
+
+ int pipe_fd[2];
+ GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1);
+ // Clear the close-on-exec flag on the write end of the pipe, lest
+ // it be closed when the child process does an exec:
+ GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1);
+
+ const std::string filter_flag =
+ std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "="
+ + info->test_case_name() + "." + info->name();
+ const std::string internal_flag =
+ std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "="
+ + file_ + "|" + StreamableToString(line_) + "|"
+ + StreamableToString(death_test_index) + "|"
+ + StreamableToString(pipe_fd[1]);
+ Arguments args;
+ args.AddArguments(GetArgvsForDeathTestChildProcess());
+ args.AddArgument(filter_flag.c_str());
+ args.AddArgument(internal_flag.c_str());
+
+ DeathTest::set_last_death_test_message("");
+
+ CaptureStderr();
+ // See the comment in NoExecDeathTest::AssumeRole for why the next line
+ // is necessary.
+ FlushInfoLog();
+
+ const pid_t child_pid = ExecDeathTestSpawnChild(args.Argv(), pipe_fd[0]);
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1]));
+ set_child_pid(child_pid);
+ set_read_fd(pipe_fd[0]);
+ set_spawned(true);
+ return OVERSEE_TEST;
+}
+
+# endif // !GTEST_OS_WINDOWS
+
+// Creates a concrete DeathTest-derived class that depends on the
+// --gtest_death_test_style flag, and sets the pointer pointed to
+// by the "test" argument to its address. If the test should be
+// skipped, sets that pointer to NULL. Returns true, unless the
+// flag is set to an invalid value.
+bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex,
+ const char* file, int line,
+ DeathTest** test) {
+ UnitTestImpl* const impl = GetUnitTestImpl();
+ const InternalRunDeathTestFlag* const flag =
+ impl->internal_run_death_test_flag();
+ const int death_test_index = impl->current_test_info()
+ ->increment_death_test_count();
+
+ if (flag != NULL) {
+ if (death_test_index > flag->index()) {
+ DeathTest::set_last_death_test_message(
+ "Death test count (" + StreamableToString(death_test_index)
+ + ") somehow exceeded expected maximum ("
+ + StreamableToString(flag->index()) + ")");
+ return false;
+ }
+
+ if (!(flag->file() == file && flag->line() == line &&
+ flag->index() == death_test_index)) {
+ *test = NULL;
+ return true;
+ }
+ }
+
+# if GTEST_OS_WINDOWS
+
+ if (GTEST_FLAG(death_test_style) == "threadsafe" ||
+ GTEST_FLAG(death_test_style) == "fast") {
+ *test = new WindowsDeathTest(statement, regex, file, line);
+ }
+
+# else
+
+ if (GTEST_FLAG(death_test_style) == "threadsafe") {
+ *test = new ExecDeathTest(statement, regex, file, line);
+ } else if (GTEST_FLAG(death_test_style) == "fast") {
+ *test = new NoExecDeathTest(statement, regex);
+ }
+
+# endif // GTEST_OS_WINDOWS
+
+ else { // NOLINT - this is more readable than unbalanced brackets inside #if.
+ DeathTest::set_last_death_test_message(
+ "Unknown death test style \"" + GTEST_FLAG(death_test_style)
+ + "\" encountered");
+ return false;
+ }
+
+ return true;
+}
+
+// Splits a given string on a given delimiter, populating a given
+// vector with the fields. GTEST_HAS_DEATH_TEST implies that we have
+// ::std::string, so we can use it here.
+static void SplitString(const ::std::string& str, char delimiter,
+ ::std::vector< ::std::string>* dest) {
+ ::std::vector< ::std::string> parsed;
+ ::std::string::size_type pos = 0;
+ while (::testing::internal::AlwaysTrue()) {
+ const ::std::string::size_type colon = str.find(delimiter, pos);
+ if (colon == ::std::string::npos) {
+ parsed.push_back(str.substr(pos));
+ break;
+ } else {
+ parsed.push_back(str.substr(pos, colon - pos));
+ pos = colon + 1;
+ }
+ }
+ dest->swap(parsed);
+}
+
+# if GTEST_OS_WINDOWS
+// Recreates the pipe and event handles from the provided parameters,
+// signals the event, and returns a file descriptor wrapped around the pipe
+// handle. This function is called in the child process only.
+int GetStatusFileDescriptor(unsigned int parent_process_id,
+ size_t write_handle_as_size_t,
+ size_t event_handle_as_size_t) {
+ AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE,
+ FALSE, // Non-inheritable.
+ parent_process_id));
+ if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) {
+ DeathTestAbort("Unable to open parent process " +
+ StreamableToString(parent_process_id));
+ }
+
+ // TODO(vladl at google.com): Replace the following check with a
+ // compile-time assertion when available.
+ GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t));
+
+ const HANDLE write_handle =
+ reinterpret_cast<HANDLE>(write_handle_as_size_t);
+ HANDLE dup_write_handle;
+
+ // The newly initialized handle is accessible only in in the parent
+ // process. To obtain one accessible within the child, we need to use
+ // DuplicateHandle.
+ if (!::DuplicateHandle(parent_process_handle.Get(), write_handle,
+ ::GetCurrentProcess(), &dup_write_handle,
+ 0x0, // Requested privileges ignored since
+ // DUPLICATE_SAME_ACCESS is used.
+ FALSE, // Request non-inheritable handler.
+ DUPLICATE_SAME_ACCESS)) {
+ DeathTestAbort("Unable to duplicate the pipe handle " +
+ StreamableToString(write_handle_as_size_t) +
+ " from the parent process " +
+ StreamableToString(parent_process_id));
+ }
+
+ const HANDLE event_handle = reinterpret_cast<HANDLE>(event_handle_as_size_t);
+ HANDLE dup_event_handle;
+
+ if (!::DuplicateHandle(parent_process_handle.Get(), event_handle,
+ ::GetCurrentProcess(), &dup_event_handle,
+ 0x0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS)) {
+ DeathTestAbort("Unable to duplicate the event handle " +
+ StreamableToString(event_handle_as_size_t) +
+ " from the parent process " +
+ StreamableToString(parent_process_id));
+ }
+
+ const int write_fd =
+ ::_open_osfhandle(reinterpret_cast<intptr_t>(dup_write_handle), O_APPEND);
+ if (write_fd == -1) {
+ DeathTestAbort("Unable to convert pipe handle " +
+ StreamableToString(write_handle_as_size_t) +
+ " to a file descriptor");
+ }
+
+ // Signals the parent that the write end of the pipe has been acquired
+ // so the parent can release its own write end.
+ ::SetEvent(dup_event_handle);
+
+ return write_fd;
+}
+# endif // GTEST_OS_WINDOWS
+
+// Returns a newly created InternalRunDeathTestFlag object with fields
+// initialized from the GTEST_FLAG(internal_run_death_test) flag if
+// the flag is specified; otherwise returns NULL.
+InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() {
+ if (GTEST_FLAG(internal_run_death_test) == "") return NULL;
+
+ // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we
+ // can use it here.
+ int line = -1;
+ int index = -1;
+ ::std::vector< ::std::string> fields;
+ SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields);
+ int write_fd = -1;
+
+# if GTEST_OS_WINDOWS
+
+ unsigned int parent_process_id = 0;
+ size_t write_handle_as_size_t = 0;
+ size_t event_handle_as_size_t = 0;
+
+ if (fields.size() != 6
+ || !ParseNaturalNumber(fields[1], &line)
+ || !ParseNaturalNumber(fields[2], &index)
+ || !ParseNaturalNumber(fields[3], &parent_process_id)
+ || !ParseNaturalNumber(fields[4], &write_handle_as_size_t)
+ || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) {
+ DeathTestAbort("Bad --gtest_internal_run_death_test flag: " +
+ GTEST_FLAG(internal_run_death_test));
+ }
+ write_fd = GetStatusFileDescriptor(parent_process_id,
+ write_handle_as_size_t,
+ event_handle_as_size_t);
+# else
+
+ if (fields.size() != 4
+ || !ParseNaturalNumber(fields[1], &line)
+ || !ParseNaturalNumber(fields[2], &index)
+ || !ParseNaturalNumber(fields[3], &write_fd)) {
+ DeathTestAbort("Bad --gtest_internal_run_death_test flag: "
+ + GTEST_FLAG(internal_run_death_test));
+ }
+
+# endif // GTEST_OS_WINDOWS
+
+ return new InternalRunDeathTestFlag(fields[0], line, index, write_fd);
+}
+
+} // namespace internal
+
+#endif // GTEST_HAS_DEATH_TEST
+
+} // namespace testing
diff --git a/test/gtest/src/gtest-filepath.cc b/test/gtest/src/gtest-filepath.cc
new file mode 100644
index 0000000..6be58b6
--- /dev/null
+++ b/test/gtest/src/gtest-filepath.cc
@@ -0,0 +1,382 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: keith.ray at gmail.com (Keith Ray)
+
+#include "gtest/gtest-message.h"
+#include "gtest/internal/gtest-filepath.h"
+#include "gtest/internal/gtest-port.h"
+
+#include <stdlib.h>
+
+#if GTEST_OS_WINDOWS_MOBILE
+# include <windows.h>
+#elif GTEST_OS_WINDOWS
+# include <direct.h>
+# include <io.h>
+#elif GTEST_OS_SYMBIAN
+// Symbian OpenC has PATH_MAX in sys/syslimits.h
+# include <sys/syslimits.h>
+#else
+# include <limits.h>
+# include <climits> // Some Linux distributions define PATH_MAX here.
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+#if GTEST_OS_WINDOWS
+# define GTEST_PATH_MAX_ _MAX_PATH
+#elif defined(PATH_MAX)
+# define GTEST_PATH_MAX_ PATH_MAX
+#elif defined(_XOPEN_PATH_MAX)
+# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX
+#else
+# define GTEST_PATH_MAX_ _POSIX_PATH_MAX
+#endif // GTEST_OS_WINDOWS
+
+#include "gtest/internal/gtest-string.h"
+
+namespace testing {
+namespace internal {
+
+#if GTEST_OS_WINDOWS
+// On Windows, '\\' is the standard path separator, but many tools and the
+// Windows API also accept '/' as an alternate path separator. Unless otherwise
+// noted, a file path can contain either kind of path separators, or a mixture
+// of them.
+const char kPathSeparator = '\\';
+const char kAlternatePathSeparator = '/';
+const char kPathSeparatorString[] = "\\";
+const char kAlternatePathSeparatorString[] = "/";
+# if GTEST_OS_WINDOWS_MOBILE
+// Windows CE doesn't have a current directory. You should not use
+// the current directory in tests on Windows CE, but this at least
+// provides a reasonable fallback.
+const char kCurrentDirectoryString[] = "\\";
+// Windows CE doesn't define INVALID_FILE_ATTRIBUTES
+const DWORD kInvalidFileAttributes = 0xffffffff;
+# else
+const char kCurrentDirectoryString[] = ".\\";
+# endif // GTEST_OS_WINDOWS_MOBILE
+#else
+const char kPathSeparator = '/';
+const char kPathSeparatorString[] = "/";
+const char kCurrentDirectoryString[] = "./";
+#endif // GTEST_OS_WINDOWS
+
+// Returns whether the given character is a valid path separator.
+static bool IsPathSeparator(char c) {
+#if GTEST_HAS_ALT_PATH_SEP_
+ return (c == kPathSeparator) || (c == kAlternatePathSeparator);
+#else
+ return c == kPathSeparator;
+#endif
+}
+
+// Returns the current working directory, or "" if unsuccessful.
+FilePath FilePath::GetCurrentDir() {
+#if GTEST_OS_WINDOWS_MOBILE
+ // Windows CE doesn't have a current directory, so we just return
+ // something reasonable.
+ return FilePath(kCurrentDirectoryString);
+#elif GTEST_OS_WINDOWS
+ char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
+ return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
+#else
+ char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
+ return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
+#endif // GTEST_OS_WINDOWS_MOBILE
+}
+
+// Returns a copy of the FilePath with the case-insensitive extension removed.
+// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
+// FilePath("dir/file"). If a case-insensitive extension is not
+// found, returns a copy of the original FilePath.
+FilePath FilePath::RemoveExtension(const char* extension) const {
+ const std::string dot_extension = std::string(".") + extension;
+ if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) {
+ return FilePath(pathname_.substr(
+ 0, pathname_.length() - dot_extension.length()));
+ }
+ return *this;
+}
+
+// Returns a pointer to the last occurence of a valid path separator in
+// the FilePath. On Windows, for example, both '/' and '\' are valid path
+// separators. Returns NULL if no path separator was found.
+const char* FilePath::FindLastPathSeparator() const {
+ const char* const last_sep = strrchr(c_str(), kPathSeparator);
+#if GTEST_HAS_ALT_PATH_SEP_
+ const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator);
+ // Comparing two pointers of which only one is NULL is undefined.
+ if (last_alt_sep != NULL &&
+ (last_sep == NULL || last_alt_sep > last_sep)) {
+ return last_alt_sep;
+ }
+#endif
+ return last_sep;
+}
+
+// Returns a copy of the FilePath with the directory part removed.
+// Example: FilePath("path/to/file").RemoveDirectoryName() returns
+// FilePath("file"). If there is no directory part ("just_a_file"), it returns
+// the FilePath unmodified. If there is no file part ("just_a_dir/") it
+// returns an empty FilePath ("").
+// On Windows platform, '\' is the path separator, otherwise it is '/'.
+FilePath FilePath::RemoveDirectoryName() const {
+ const char* const last_sep = FindLastPathSeparator();
+ return last_sep ? FilePath(last_sep + 1) : *this;
+}
+
+// RemoveFileName returns the directory path with the filename removed.
+// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
+// If the FilePath is "a_file" or "/a_file", RemoveFileName returns
+// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
+// not have a file, like "just/a/dir/", it returns the FilePath unmodified.
+// On Windows platform, '\' is the path separator, otherwise it is '/'.
+FilePath FilePath::RemoveFileName() const {
+ const char* const last_sep = FindLastPathSeparator();
+ std::string dir;
+ if (last_sep) {
+ dir = std::string(c_str(), last_sep + 1 - c_str());
+ } else {
+ dir = kCurrentDirectoryString;
+ }
+ return FilePath(dir);
+}
+
+// Helper functions for naming files in a directory for xml output.
+
+// Given directory = "dir", base_name = "test", number = 0,
+// extension = "xml", returns "dir/test.xml". If number is greater
+// than zero (e.g., 12), returns "dir/test_12.xml".
+// On Windows platform, uses \ as the separator rather than /.
+FilePath FilePath::MakeFileName(const FilePath& directory,
+ const FilePath& base_name,
+ int number,
+ const char* extension) {
+ std::string file;
+ if (number == 0) {
+ file = base_name.string() + "." + extension;
+ } else {
+ file = base_name.string() + "_" + StreamableToString(number)
+ + "." + extension;
+ }
+ return ConcatPaths(directory, FilePath(file));
+}
+
+// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml".
+// On Windows, uses \ as the separator rather than /.
+FilePath FilePath::ConcatPaths(const FilePath& directory,
+ const FilePath& relative_path) {
+ if (directory.IsEmpty())
+ return relative_path;
+ const FilePath dir(directory.RemoveTrailingPathSeparator());
+ return FilePath(dir.string() + kPathSeparator + relative_path.string());
+}
+
+// Returns true if pathname describes something findable in the file-system,
+// either a file, directory, or whatever.
+bool FilePath::FileOrDirectoryExists() const {
+#if GTEST_OS_WINDOWS_MOBILE
+ LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str());
+ const DWORD attributes = GetFileAttributes(unicode);
+ delete [] unicode;
+ return attributes != kInvalidFileAttributes;
+#else
+ posix::StatStruct file_stat;
+ return posix::Stat(pathname_.c_str(), &file_stat) == 0;
+#endif // GTEST_OS_WINDOWS_MOBILE
+}
+
+// Returns true if pathname describes a directory in the file-system
+// that exists.
+bool FilePath::DirectoryExists() const {
+ bool result = false;
+#if GTEST_OS_WINDOWS
+ // Don't strip off trailing separator if path is a root directory on
+ // Windows (like "C:\\").
+ const FilePath& path(IsRootDirectory() ? *this :
+ RemoveTrailingPathSeparator());
+#else
+ const FilePath& path(*this);
+#endif
+
+#if GTEST_OS_WINDOWS_MOBILE
+ LPCWSTR unicode = String::AnsiToUtf16(path.c_str());
+ const DWORD attributes = GetFileAttributes(unicode);
+ delete [] unicode;
+ if ((attributes != kInvalidFileAttributes) &&
+ (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ result = true;
+ }
+#else
+ posix::StatStruct file_stat;
+ result = posix::Stat(path.c_str(), &file_stat) == 0 &&
+ posix::IsDir(file_stat);
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+ return result;
+}
+
+// Returns true if pathname describes a root directory. (Windows has one
+// root directory per disk drive.)
+bool FilePath::IsRootDirectory() const {
+#if GTEST_OS_WINDOWS
+ // TODO(wan at google.com): on Windows a network share like
+ // \\server\share can be a root directory, although it cannot be the
+ // current directory. Handle this properly.
+ return pathname_.length() == 3 && IsAbsolutePath();
+#else
+ return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]);
+#endif
+}
+
+// Returns true if pathname describes an absolute path.
+bool FilePath::IsAbsolutePath() const {
+ const char* const name = pathname_.c_str();
+#if GTEST_OS_WINDOWS
+ return pathname_.length() >= 3 &&
+ ((name[0] >= 'a' && name[0] <= 'z') ||
+ (name[0] >= 'A' && name[0] <= 'Z')) &&
+ name[1] == ':' &&
+ IsPathSeparator(name[2]);
+#else
+ return IsPathSeparator(name[0]);
+#endif
+}
+
+// Returns a pathname for a file that does not currently exist. The pathname
+// will be directory/base_name.extension or
+// directory/base_name_<number>.extension if directory/base_name.extension
+// already exists. The number will be incremented until a pathname is found
+// that does not already exist.
+// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
+// There could be a race condition if two or more processes are calling this
+// function at the same time -- they could both pick the same filename.
+FilePath FilePath::GenerateUniqueFileName(const FilePath& directory,
+ const FilePath& base_name,
+ const char* extension) {
+ FilePath full_pathname;
+ int number = 0;
+ do {
+ full_pathname.Set(MakeFileName(directory, base_name, number++, extension));
+ } while (full_pathname.FileOrDirectoryExists());
+ return full_pathname;
+}
+
+// Returns true if FilePath ends with a path separator, which indicates that
+// it is intended to represent a directory. Returns false otherwise.
+// This does NOT check that a directory (or file) actually exists.
+bool FilePath::IsDirectory() const {
+ return !pathname_.empty() &&
+ IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]);
+}
+
+// Create directories so that path exists. Returns true if successful or if
+// the directories already exist; returns false if unable to create directories
+// for any reason.
+bool FilePath::CreateDirectoriesRecursively() const {
+ if (!this->IsDirectory()) {
+ return false;
+ }
+
+ if (pathname_.length() == 0 || this->DirectoryExists()) {
+ return true;
+ }
+
+ const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName());
+ return parent.CreateDirectoriesRecursively() && this->CreateFolder();
+}
+
+// Create the directory so that path exists. Returns true if successful or
+// if the directory already exists; returns false if unable to create the
+// directory for any reason, including if the parent directory does not
+// exist. Not named "CreateDirectory" because that's a macro on Windows.
+bool FilePath::CreateFolder() const {
+#if GTEST_OS_WINDOWS_MOBILE
+ FilePath removed_sep(this->RemoveTrailingPathSeparator());
+ LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str());
+ int result = CreateDirectory(unicode, NULL) ? 0 : -1;
+ delete [] unicode;
+#elif GTEST_OS_WINDOWS
+ int result = _mkdir(pathname_.c_str());
+#else
+ int result = mkdir(pathname_.c_str(), 0777);
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+ if (result == -1) {
+ return this->DirectoryExists(); // An error is OK if the directory exists.
+ }
+ return true; // No error.
+}
+
+// If input name has a trailing separator character, remove it and return the
+// name, otherwise return the name string unmodified.
+// On Windows platform, uses \ as the separator, other platforms use /.
+FilePath FilePath::RemoveTrailingPathSeparator() const {
+ return IsDirectory()
+ ? FilePath(pathname_.substr(0, pathname_.length() - 1))
+ : *this;
+}
+
+// Removes any redundant separators that might be in the pathname.
+// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
+// redundancies that might be in a pathname involving "." or "..".
+// TODO(wan at google.com): handle Windows network shares (e.g. \\server\share).
+void FilePath::Normalize() {
+ if (pathname_.c_str() == NULL) {
+ pathname_ = "";
+ return;
+ }
+ const char* src = pathname_.c_str();
+ char* const dest = new char[pathname_.length() + 1];
+ char* dest_ptr = dest;
+ memset(dest_ptr, 0, pathname_.length() + 1);
+
+ while (*src != '\0') {
+ *dest_ptr = *src;
+ if (!IsPathSeparator(*src)) {
+ src++;
+ } else {
+#if GTEST_HAS_ALT_PATH_SEP_
+ if (*dest_ptr == kAlternatePathSeparator) {
+ *dest_ptr = kPathSeparator;
+ }
+#endif
+ while (IsPathSeparator(*src))
+ src++;
+ }
+ dest_ptr++;
+ }
+ *dest_ptr = '\0';
+ pathname_ = dest;
+ delete[] dest;
+}
+
+} // namespace internal
+} // namespace testing
diff --git a/test/gtest/src/gtest-internal-inl.h b/test/gtest/src/gtest-internal-inl.h
new file mode 100644
index 0000000..35df303
--- /dev/null
+++ b/test/gtest/src/gtest-internal-inl.h
@@ -0,0 +1,1218 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Utility functions and classes used by the Google C++ testing framework.
+//
+// Author: wan at google.com (Zhanyong Wan)
+//
+// This file contains purely Google Test's internal implementation. Please
+// DO NOT #INCLUDE IT IN A USER PROGRAM.
+
+#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_
+#define GTEST_SRC_GTEST_INTERNAL_INL_H_
+
+// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is
+// part of Google Test's implementation; otherwise it's undefined.
+#if !GTEST_IMPLEMENTATION_
+// A user is trying to include this from his code - just say no.
+# error "gtest-internal-inl.h is part of Google Test's internal implementation."
+# error "It must not be included except by Google Test itself."
+#endif // GTEST_IMPLEMENTATION_
+
+#ifndef _WIN32_WCE
+# include <errno.h>
+#endif // !_WIN32_WCE
+#include <stddef.h>
+#include <stdlib.h> // For strtoll/_strtoul64/malloc/free.
+#include <string.h> // For memmove.
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "gtest/internal/gtest-port.h"
+
+#if GTEST_CAN_STREAM_RESULTS_
+# include <arpa/inet.h> // NOLINT
+# include <netdb.h> // NOLINT
+#endif
+
+#if GTEST_OS_WINDOWS
+# include <windows.h> // NOLINT
+#endif // GTEST_OS_WINDOWS
+
+#include "gtest/gtest.h" // NOLINT
+#include "gtest/gtest-spi.h"
+
+namespace testing {
+
+// Declares the flags.
+//
+// We don't want the users to modify this flag in the code, but want
+// Google Test's own unit tests to be able to access it. Therefore we
+// declare it here as opposed to in gtest.h.
+GTEST_DECLARE_bool_(death_test_use_fork);
+
+namespace internal {
+
+// The value of GetTestTypeId() as seen from within the Google Test
+// library. This is solely for testing GetTestTypeId().
+GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest;
+
+// Names of the flags (needed for parsing Google Test flags).
+const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests";
+const char kBreakOnFailureFlag[] = "break_on_failure";
+const char kCatchExceptionsFlag[] = "catch_exceptions";
+const char kColorFlag[] = "color";
+const char kFilterFlag[] = "filter";
+const char kListTestsFlag[] = "list_tests";
+const char kOutputFlag[] = "output";
+const char kPrintTimeFlag[] = "print_time";
+const char kRandomSeedFlag[] = "random_seed";
+const char kRepeatFlag[] = "repeat";
+const char kShuffleFlag[] = "shuffle";
+const char kStackTraceDepthFlag[] = "stack_trace_depth";
+const char kStreamResultToFlag[] = "stream_result_to";
+const char kThrowOnFailureFlag[] = "throw_on_failure";
+
+// A valid random seed must be in [1, kMaxRandomSeed].
+const int kMaxRandomSeed = 99999;
+
+// g_help_flag is true iff the --help flag or an equivalent form is
+// specified on the command line.
+GTEST_API_ extern bool g_help_flag;
+
+// Returns the current time in milliseconds.
+GTEST_API_ TimeInMillis GetTimeInMillis();
+
+// Returns true iff Google Test should use colors in the output.
+GTEST_API_ bool ShouldUseColor(bool stdout_is_tty);
+
+// Formats the given time in milliseconds as seconds.
+GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms);
+
+// Converts the given time in milliseconds to a date string in the ISO 8601
+// format, without the timezone information. N.B.: due to the use the
+// non-reentrant localtime() function, this function is not thread safe. Do
+// not use it in any code that can be called from multiple threads.
+GTEST_API_ std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms);
+
+// Parses a string for an Int32 flag, in the form of "--flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+GTEST_API_ bool ParseInt32Flag(
+ const char* str, const char* flag, Int32* value);
+
+// Returns a random seed in range [1, kMaxRandomSeed] based on the
+// given --gtest_random_seed flag value.
+inline int GetRandomSeedFromFlag(Int32 random_seed_flag) {
+ const unsigned int raw_seed = (random_seed_flag == 0) ?
+ static_cast<unsigned int>(GetTimeInMillis()) :
+ static_cast<unsigned int>(random_seed_flag);
+
+ // Normalizes the actual seed to range [1, kMaxRandomSeed] such that
+ // it's easy to type.
+ const int normalized_seed =
+ static_cast<int>((raw_seed - 1U) %
+ static_cast<unsigned int>(kMaxRandomSeed)) + 1;
+ return normalized_seed;
+}
+
+// Returns the first valid random seed after 'seed'. The behavior is
+// undefined if 'seed' is invalid. The seed after kMaxRandomSeed is
+// considered to be 1.
+inline int GetNextRandomSeed(int seed) {
+ GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed)
+ << "Invalid random seed " << seed << " - must be in [1, "
+ << kMaxRandomSeed << "].";
+ const int next_seed = seed + 1;
+ return (next_seed > kMaxRandomSeed) ? 1 : next_seed;
+}
+
+// This class saves the values of all Google Test flags in its c'tor, and
+// restores them in its d'tor.
+class GTestFlagSaver {
+ public:
+ // The c'tor.
+ GTestFlagSaver() {
+ also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests);
+ break_on_failure_ = GTEST_FLAG(break_on_failure);
+ catch_exceptions_ = GTEST_FLAG(catch_exceptions);
+ color_ = GTEST_FLAG(color);
+ death_test_style_ = GTEST_FLAG(death_test_style);
+ death_test_use_fork_ = GTEST_FLAG(death_test_use_fork);
+ filter_ = GTEST_FLAG(filter);
+ internal_run_death_test_ = GTEST_FLAG(internal_run_death_test);
+ list_tests_ = GTEST_FLAG(list_tests);
+ output_ = GTEST_FLAG(output);
+ print_time_ = GTEST_FLAG(print_time);
+ random_seed_ = GTEST_FLAG(random_seed);
+ repeat_ = GTEST_FLAG(repeat);
+ shuffle_ = GTEST_FLAG(shuffle);
+ stack_trace_depth_ = GTEST_FLAG(stack_trace_depth);
+ stream_result_to_ = GTEST_FLAG(stream_result_to);
+ throw_on_failure_ = GTEST_FLAG(throw_on_failure);
+ }
+
+ // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS.
+ ~GTestFlagSaver() {
+ GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_;
+ GTEST_FLAG(break_on_failure) = break_on_failure_;
+ GTEST_FLAG(catch_exceptions) = catch_exceptions_;
+ GTEST_FLAG(color) = color_;
+ GTEST_FLAG(death_test_style) = death_test_style_;
+ GTEST_FLAG(death_test_use_fork) = death_test_use_fork_;
+ GTEST_FLAG(filter) = filter_;
+ GTEST_FLAG(internal_run_death_test) = internal_run_death_test_;
+ GTEST_FLAG(list_tests) = list_tests_;
+ GTEST_FLAG(output) = output_;
+ GTEST_FLAG(print_time) = print_time_;
+ GTEST_FLAG(random_seed) = random_seed_;
+ GTEST_FLAG(repeat) = repeat_;
+ GTEST_FLAG(shuffle) = shuffle_;
+ GTEST_FLAG(stack_trace_depth) = stack_trace_depth_;
+ GTEST_FLAG(stream_result_to) = stream_result_to_;
+ GTEST_FLAG(throw_on_failure) = throw_on_failure_;
+ }
+
+ private:
+ // Fields for saving the original values of flags.
+ bool also_run_disabled_tests_;
+ bool break_on_failure_;
+ bool catch_exceptions_;
+ std::string color_;
+ std::string death_test_style_;
+ bool death_test_use_fork_;
+ std::string filter_;
+ std::string internal_run_death_test_;
+ bool list_tests_;
+ std::string output_;
+ bool print_time_;
+ internal::Int32 random_seed_;
+ internal::Int32 repeat_;
+ bool shuffle_;
+ internal::Int32 stack_trace_depth_;
+ std::string stream_result_to_;
+ bool throw_on_failure_;
+} GTEST_ATTRIBUTE_UNUSED_;
+
+// Converts a Unicode code point to a narrow string in UTF-8 encoding.
+// code_point parameter is of type UInt32 because wchar_t may not be
+// wide enough to contain a code point.
+// If the code_point is not a valid Unicode code point
+// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted
+// to "(Invalid Unicode 0xXXXXXXXX)".
+GTEST_API_ std::string CodePointToUtf8(UInt32 code_point);
+
+// Converts a wide string to a narrow string in UTF-8 encoding.
+// The wide string is assumed to have the following encoding:
+// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS)
+// UTF-32 if sizeof(wchar_t) == 4 (on Linux)
+// Parameter str points to a null-terminated wide string.
+// Parameter num_chars may additionally limit the number
+// of wchar_t characters processed. -1 is used when the entire string
+// should be processed.
+// If the string contains code points that are not valid Unicode code points
+// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output
+// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding
+// and contains invalid UTF-16 surrogate pairs, values in those pairs
+// will be encoded as individual Unicode characters from Basic Normal Plane.
+GTEST_API_ std::string WideStringToUtf8(const wchar_t* str, int num_chars);
+
+// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file
+// if the variable is present. If a file already exists at this location, this
+// function will write over it. If the variable is present, but the file cannot
+// be created, prints an error and exits.
+void WriteToShardStatusFileIfNeeded();
+
+// Checks whether sharding is enabled by examining the relevant
+// environment variable values. If the variables are present,
+// but inconsistent (e.g., shard_index >= total_shards), prints
+// an error and exits. If in_subprocess_for_death_test, sharding is
+// disabled because it must only be applied to the original test
+// process. Otherwise, we could filter out death tests we intended to execute.
+GTEST_API_ bool ShouldShard(const char* total_shards_str,
+ const char* shard_index_str,
+ bool in_subprocess_for_death_test);
+
+// Parses the environment variable var as an Int32. If it is unset,
+// returns default_val. If it is not an Int32, prints an error and
+// and aborts.
+GTEST_API_ Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val);
+
+// Given the total number of shards, the shard index, and the test id,
+// returns true iff the test should be run on this shard. The test id is
+// some arbitrary but unique non-negative integer assigned to each test
+// method. Assumes that 0 <= shard_index < total_shards.
+GTEST_API_ bool ShouldRunTestOnShard(
+ int total_shards, int shard_index, int test_id);
+
+// STL container utilities.
+
+// Returns the number of elements in the given container that satisfy
+// the given predicate.
+template <class Container, typename Predicate>
+inline int CountIf(const Container& c, Predicate predicate) {
+ // Implemented as an explicit loop since std::count_if() in libCstd on
+ // Solaris has a non-standard signature.
+ int count = 0;
+ for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) {
+ if (predicate(*it))
+ ++count;
+ }
+ return count;
+}
+
+// Applies a function/functor to each element in the container.
+template <class Container, typename Functor>
+void ForEach(const Container& c, Functor functor) {
+ std::for_each(c.begin(), c.end(), functor);
+}
+
+// Returns the i-th element of the vector, or default_value if i is not
+// in range [0, v.size()).
+template <typename E>
+inline E GetElementOr(const std::vector<E>& v, int i, E default_value) {
+ return (i < 0 || i >= static_cast<int>(v.size())) ? default_value : v[i];
+}
+
+// Performs an in-place shuffle of a range of the vector's elements.
+// 'begin' and 'end' are element indices as an STL-style range;
+// i.e. [begin, end) are shuffled, where 'end' == size() means to
+// shuffle to the end of the vector.
+template <typename E>
+void ShuffleRange(internal::Random* random, int begin, int end,
+ std::vector<E>* v) {
+ const int size = static_cast<int>(v->size());
+ GTEST_CHECK_(0 <= begin && begin <= size)
+ << "Invalid shuffle range start " << begin << ": must be in range [0, "
+ << size << "].";
+ GTEST_CHECK_(begin <= end && end <= size)
+ << "Invalid shuffle range finish " << end << ": must be in range ["
+ << begin << ", " << size << "].";
+
+ // Fisher-Yates shuffle, from
+ // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
+ for (int range_width = end - begin; range_width >= 2; range_width--) {
+ const int last_in_range = begin + range_width - 1;
+ const int selected = begin + random->Generate(range_width);
+ std::swap((*v)[selected], (*v)[last_in_range]);
+ }
+}
+
+// Performs an in-place shuffle of the vector's elements.
+template <typename E>
+inline void Shuffle(internal::Random* random, std::vector<E>* v) {
+ ShuffleRange(random, 0, static_cast<int>(v->size()), v);
+}
+
+// A function for deleting an object. Handy for being used as a
+// functor.
+template <typename T>
+static void Delete(T* x) {
+ delete x;
+}
+
+// A predicate that checks the key of a TestProperty against a known key.
+//
+// TestPropertyKeyIs is copyable.
+class TestPropertyKeyIs {
+ public:
+ // Constructor.
+ //
+ // TestPropertyKeyIs has NO default constructor.
+ explicit TestPropertyKeyIs(const std::string& key) : key_(key) {}
+
+ // Returns true iff the test name of test property matches on key_.
+ bool operator()(const TestProperty& test_property) const {
+ return test_property.key() == key_;
+ }
+
+ private:
+ std::string key_;
+};
+
+// Class UnitTestOptions.
+//
+// This class contains functions for processing options the user
+// specifies when running the tests. It has only static members.
+//
+// In most cases, the user can specify an option using either an
+// environment variable or a command line flag. E.g. you can set the
+// test filter using either GTEST_FILTER or --gtest_filter. If both
+// the variable and the flag are present, the latter overrides the
+// former.
+class GTEST_API_ UnitTestOptions {
+ public:
+ // Functions for processing the gtest_output flag.
+
+ // Returns the output format, or "" for normal printed output.
+ static std::string GetOutputFormat();
+
+ // Returns the absolute path of the requested output file, or the
+ // default (test_detail.xml in the original working directory) if
+ // none was explicitly specified.
+ static std::string GetAbsolutePathToOutputFile();
+
+ // Functions for processing the gtest_filter flag.
+
+ // Returns true iff the wildcard pattern matches the string. The
+ // first ':' or '\0' character in pattern marks the end of it.
+ //
+ // This recursive algorithm isn't very efficient, but is clear and
+ // works well enough for matching test names, which are short.
+ static bool PatternMatchesString(const char *pattern, const char *str);
+
+ // Returns true iff the user-specified filter matches the test case
+ // name and the test name.
+ static bool FilterMatchesTest(const std::string &test_case_name,
+ const std::string &test_name);
+
+#if GTEST_OS_WINDOWS
+ // Function for supporting the gtest_catch_exception flag.
+
+ // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the
+ // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise.
+ // This function is useful as an __except condition.
+ static int GTestShouldProcessSEH(DWORD exception_code);
+#endif // GTEST_OS_WINDOWS
+
+ // Returns true if "name" matches the ':' separated list of glob-style
+ // filters in "filter".
+ static bool MatchesFilter(const std::string& name, const char* filter);
+};
+
+// Returns the current application's name, removing directory path if that
+// is present. Used by UnitTestOptions::GetOutputFile.
+GTEST_API_ FilePath GetCurrentExecutableName();
+
+// The role interface for getting the OS stack trace as a string.
+class OsStackTraceGetterInterface {
+ public:
+ OsStackTraceGetterInterface() {}
+ virtual ~OsStackTraceGetterInterface() {}
+
+ // Returns the current OS stack trace as an std::string. Parameters:
+ //
+ // max_depth - the maximum number of stack frames to be included
+ // in the trace.
+ // skip_count - the number of top frames to be skipped; doesn't count
+ // against max_depth.
+ virtual string CurrentStackTrace(int max_depth, int skip_count) = 0;
+
+ // UponLeavingGTest() should be called immediately before Google Test calls
+ // user code. It saves some information about the current stack that
+ // CurrentStackTrace() will use to find and hide Google Test stack frames.
+ virtual void UponLeavingGTest() = 0;
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface);
+};
+
+// A working implementation of the OsStackTraceGetterInterface interface.
+class OsStackTraceGetter : public OsStackTraceGetterInterface {
+ public:
+ OsStackTraceGetter() : caller_frame_(NULL) {}
+
+ virtual string CurrentStackTrace(int max_depth, int skip_count)
+ GTEST_LOCK_EXCLUDED_(mutex_);
+
+ virtual void UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_);
+
+ // This string is inserted in place of stack frames that are part of
+ // Google Test's implementation.
+ static const char* const kElidedFramesMarker;
+
+ private:
+ Mutex mutex_; // protects all internal state
+
+ // We save the stack frame below the frame that calls user code.
+ // We do this because the address of the frame immediately below
+ // the user code changes between the call to UponLeavingGTest()
+ // and any calls to CurrentStackTrace() from within the user code.
+ void* caller_frame_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter);
+};
+
+// Information about a Google Test trace point.
+struct TraceInfo {
+ const char* file;
+ int line;
+ std::string message;
+};
+
+// This is the default global test part result reporter used in UnitTestImpl.
+// This class should only be used by UnitTestImpl.
+class DefaultGlobalTestPartResultReporter
+ : public TestPartResultReporterInterface {
+ public:
+ explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test);
+ // Implements the TestPartResultReporterInterface. Reports the test part
+ // result in the current test.
+ virtual void ReportTestPartResult(const TestPartResult& result);
+
+ private:
+ UnitTestImpl* const unit_test_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter);
+};
+
+// This is the default per thread test part result reporter used in
+// UnitTestImpl. This class should only be used by UnitTestImpl.
+class DefaultPerThreadTestPartResultReporter
+ : public TestPartResultReporterInterface {
+ public:
+ explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test);
+ // Implements the TestPartResultReporterInterface. The implementation just
+ // delegates to the current global test part result reporter of *unit_test_.
+ virtual void ReportTestPartResult(const TestPartResult& result);
+
+ private:
+ UnitTestImpl* const unit_test_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter);
+};
+
+// The private implementation of the UnitTest class. We don't protect
+// the methods under a mutex, as this class is not accessible by a
+// user and the UnitTest class that delegates work to this class does
+// proper locking.
+class GTEST_API_ UnitTestImpl {
+ public:
+ explicit UnitTestImpl(UnitTest* parent);
+ virtual ~UnitTestImpl();
+
+ // There are two different ways to register your own TestPartResultReporter.
+ // You can register your own repoter to listen either only for test results
+ // from the current thread or for results from all threads.
+ // By default, each per-thread test result repoter just passes a new
+ // TestPartResult to the global test result reporter, which registers the
+ // test part result for the currently running test.
+
+ // Returns the global test part result reporter.
+ TestPartResultReporterInterface* GetGlobalTestPartResultReporter();
+
+ // Sets the global test part result reporter.
+ void SetGlobalTestPartResultReporter(
+ TestPartResultReporterInterface* reporter);
+
+ // Returns the test part result reporter for the current thread.
+ TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread();
+
+ // Sets the test part result reporter for the current thread.
+ void SetTestPartResultReporterForCurrentThread(
+ TestPartResultReporterInterface* reporter);
+
+ // Gets the number of successful test cases.
+ int successful_test_case_count() const;
+
+ // Gets the number of failed test cases.
+ int failed_test_case_count() const;
+
+ // Gets the number of all test cases.
+ int total_test_case_count() const;
+
+ // Gets the number of all test cases that contain at least one test
+ // that should run.
+ int test_case_to_run_count() const;
+
+ // Gets the number of successful tests.
+ int successful_test_count() const;
+
+ // Gets the number of failed tests.
+ int failed_test_count() const;
+
+ // Gets the number of disabled tests that will be reported in the XML report.
+ int reportable_disabled_test_count() const;
+
+ // Gets the number of disabled tests.
+ int disabled_test_count() const;
+
+ // Gets the number of tests to be printed in the XML report.
+ int reportable_test_count() const;
+
+ // Gets the number of all tests.
+ int total_test_count() const;
+
+ // Gets the number of tests that should run.
+ int test_to_run_count() const;
+
+ // Gets the time of the test program start, in ms from the start of the
+ // UNIX epoch.
+ TimeInMillis start_timestamp() const { return start_timestamp_; }
+
+ // Gets the elapsed time, in milliseconds.
+ TimeInMillis elapsed_time() const { return elapsed_time_; }
+
+ // Returns true iff the unit test passed (i.e. all test cases passed).
+ bool Passed() const { return !Failed(); }
+
+ // Returns true iff the unit test failed (i.e. some test case failed
+ // or something outside of all tests failed).
+ bool Failed() const {
+ return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed();
+ }
+
+ // Gets the i-th test case among all the test cases. i can range from 0 to
+ // total_test_case_count() - 1. If i is not in that range, returns NULL.
+ const TestCase* GetTestCase(int i) const {
+ const int index = GetElementOr(test_case_indices_, i, -1);
+ return index < 0 ? NULL : test_cases_[i];
+ }
+
+ // Gets the i-th test case among all the test cases. i can range from 0 to
+ // total_test_case_count() - 1. If i is not in that range, returns NULL.
+ TestCase* GetMutableTestCase(int i) {
+ const int index = GetElementOr(test_case_indices_, i, -1);
+ return index < 0 ? NULL : test_cases_[index];
+ }
+
+ // Provides access to the event listener list.
+ TestEventListeners* listeners() { return &listeners_; }
+
+ // Returns the TestResult for the test that's currently running, or
+ // the TestResult for the ad hoc test if no test is running.
+ TestResult* current_test_result();
+
+ // Returns the TestResult for the ad hoc test.
+ const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; }
+
+ // Sets the OS stack trace getter.
+ //
+ // Does nothing if the input and the current OS stack trace getter
+ // are the same; otherwise, deletes the old getter and makes the
+ // input the current getter.
+ void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter);
+
+ // Returns the current OS stack trace getter if it is not NULL;
+ // otherwise, creates an OsStackTraceGetter, makes it the current
+ // getter, and returns it.
+ OsStackTraceGetterInterface* os_stack_trace_getter();
+
+ // Returns the current OS stack trace as an std::string.
+ //
+ // The maximum number of stack frames to be included is specified by
+ // the gtest_stack_trace_depth flag. The skip_count parameter
+ // specifies the number of top frames to be skipped, which doesn't
+ // count against the number of frames to be included.
+ //
+ // For example, if Foo() calls Bar(), which in turn calls
+ // CurrentOsStackTraceExceptTop(1), Foo() will be included in the
+ // trace but Bar() and CurrentOsStackTraceExceptTop() won't.
+ std::string CurrentOsStackTraceExceptTop(int skip_count) GTEST_NO_INLINE_;
+
+ // Finds and returns a TestCase with the given name. If one doesn't
+ // exist, creates one and returns it.
+ //
+ // Arguments:
+ //
+ // test_case_name: name of the test case
+ // type_param: the name of the test's type parameter, or NULL if
+ // this is not a typed or a type-parameterized test.
+ // set_up_tc: pointer to the function that sets up the test case
+ // tear_down_tc: pointer to the function that tears down the test case
+ TestCase* GetTestCase(const char* test_case_name,
+ const char* type_param,
+ Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc);
+
+ // Adds a TestInfo to the unit test.
+ //
+ // Arguments:
+ //
+ // set_up_tc: pointer to the function that sets up the test case
+ // tear_down_tc: pointer to the function that tears down the test case
+ // test_info: the TestInfo object
+ void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc,
+ TestInfo* test_info) {
+ // In order to support thread-safe death tests, we need to
+ // remember the original working directory when the test program
+ // was first invoked. We cannot do this in RUN_ALL_TESTS(), as
+ // the user may have changed the current directory before calling
+ // RUN_ALL_TESTS(). Therefore we capture the current directory in
+ // AddTestInfo(), which is called to register a TEST or TEST_F
+ // before main() is reached.
+ if (original_working_dir_.IsEmpty()) {
+ original_working_dir_.Set(FilePath::GetCurrentDir());
+ GTEST_CHECK_(!original_working_dir_.IsEmpty())
+ << "Failed to get the current working directory.";
+ }
+
+ GetTestCase(test_info->test_case_name(),
+ test_info->type_param(),
+ set_up_tc,
+ tear_down_tc)->AddTestInfo(test_info);
+ }
+
+#if GTEST_HAS_PARAM_TEST
+ // Returns ParameterizedTestCaseRegistry object used to keep track of
+ // value-parameterized tests and instantiate and register them.
+ internal::ParameterizedTestCaseRegistry& parameterized_test_registry() {
+ return parameterized_test_registry_;
+ }
+#endif // GTEST_HAS_PARAM_TEST
+
+ // Sets the TestCase object for the test that's currently running.
+ void set_current_test_case(TestCase* a_current_test_case) {
+ current_test_case_ = a_current_test_case;
+ }
+
+ // Sets the TestInfo object for the test that's currently running. If
+ // current_test_info is NULL, the assertion results will be stored in
+ // ad_hoc_test_result_.
+ void set_current_test_info(TestInfo* a_current_test_info) {
+ current_test_info_ = a_current_test_info;
+ }
+
+ // Registers all parameterized tests defined using TEST_P and
+ // INSTANTIATE_TEST_CASE_P, creating regular tests for each test/parameter
+ // combination. This method can be called more then once; it has guards
+ // protecting from registering the tests more then once. If
+ // value-parameterized tests are disabled, RegisterParameterizedTests is
+ // present but does nothing.
+ void RegisterParameterizedTests();
+
+ // Runs all tests in this UnitTest object, prints the result, and
+ // returns true if all tests are successful. If any exception is
+ // thrown during a test, this test is considered to be failed, but
+ // the rest of the tests will still be run.
+ bool RunAllTests();
+
+ // Clears the results of all tests, except the ad hoc tests.
+ void ClearNonAdHocTestResult() {
+ ForEach(test_cases_, TestCase::ClearTestCaseResult);
+ }
+
+ // Clears the results of ad-hoc test assertions.
+ void ClearAdHocTestResult() {
+ ad_hoc_test_result_.Clear();
+ }
+
+ // Adds a TestProperty to the current TestResult object when invoked in a
+ // context of a test or a test case, or to the global property set. If the
+ // result already contains a property with the same key, the value will be
+ // updated.
+ void RecordProperty(const TestProperty& test_property);
+
+ enum ReactionToSharding {
+ HONOR_SHARDING_PROTOCOL,
+ IGNORE_SHARDING_PROTOCOL
+ };
+
+ // Matches the full name of each test against the user-specified
+ // filter to decide whether the test should run, then records the
+ // result in each TestCase and TestInfo object.
+ // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests
+ // based on sharding variables in the environment.
+ // Returns the number of tests that should run.
+ int FilterTests(ReactionToSharding shard_tests);
+
+ // Prints the names of the tests matching the user-specified filter flag.
+ void ListTestsMatchingFilter();
+
+ const TestCase* current_test_case() const { return current_test_case_; }
+ TestInfo* current_test_info() { return current_test_info_; }
+ const TestInfo* current_test_info() const { return current_test_info_; }
+
+ // Returns the vector of environments that need to be set-up/torn-down
+ // before/after the tests are run.
+ std::vector<Environment*>& environments() { return environments_; }
+
+ // Getters for the per-thread Google Test trace stack.
+ std::vector<TraceInfo>& gtest_trace_stack() {
+ return *(gtest_trace_stack_.pointer());
+ }
+ const std::vector<TraceInfo>& gtest_trace_stack() const {
+ return gtest_trace_stack_.get();
+ }
+
+#if GTEST_HAS_DEATH_TEST
+ void InitDeathTestSubprocessControlInfo() {
+ internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag());
+ }
+ // Returns a pointer to the parsed --gtest_internal_run_death_test
+ // flag, or NULL if that flag was not specified.
+ // This information is useful only in a death test child process.
+ // Must not be called before a call to InitGoogleTest.
+ const InternalRunDeathTestFlag* internal_run_death_test_flag() const {
+ return internal_run_death_test_flag_.get();
+ }
+
+ // Returns a pointer to the current death test factory.
+ internal::DeathTestFactory* death_test_factory() {
+ return death_test_factory_.get();
+ }
+
+ void SuppressTestEventsIfInSubprocess();
+
+ friend class ReplaceDeathTestFactory;
+#endif // GTEST_HAS_DEATH_TEST
+
+ // Initializes the event listener performing XML output as specified by
+ // UnitTestOptions. Must not be called before InitGoogleTest.
+ void ConfigureXmlOutput();
+
+#if GTEST_CAN_STREAM_RESULTS_
+ // Initializes the event listener for streaming test results to a socket.
+ // Must not be called before InitGoogleTest.
+ void ConfigureStreamingOutput();
+#endif
+
+ // Performs initialization dependent upon flag values obtained in
+ // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to
+ // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest
+ // this function is also called from RunAllTests. Since this function can be
+ // called more than once, it has to be idempotent.
+ void PostFlagParsingInit();
+
+ // Gets the random seed used at the start of the current test iteration.
+ int random_seed() const { return random_seed_; }
+
+ // Gets the random number generator.
+ internal::Random* random() { return &random_; }
+
+ // Shuffles all test cases, and the tests within each test case,
+ // making sure that death tests are still run first.
+ void ShuffleTests();
+
+ // Restores the test cases and tests to their order before the first shuffle.
+ void UnshuffleTests();
+
+ // Returns the value of GTEST_FLAG(catch_exceptions) at the moment
+ // UnitTest::Run() starts.
+ bool catch_exceptions() const { return catch_exceptions_; }
+
+ private:
+ friend class ::testing::UnitTest;
+
+ // Used by UnitTest::Run() to capture the state of
+ // GTEST_FLAG(catch_exceptions) at the moment it starts.
+ void set_catch_exceptions(bool value) { catch_exceptions_ = value; }
+
+ // The UnitTest object that owns this implementation object.
+ UnitTest* const parent_;
+
+ // The working directory when the first TEST() or TEST_F() was
+ // executed.
+ internal::FilePath original_working_dir_;
+
+ // The default test part result reporters.
+ DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_;
+ DefaultPerThreadTestPartResultReporter
+ default_per_thread_test_part_result_reporter_;
+
+ // Points to (but doesn't own) the global test part result reporter.
+ TestPartResultReporterInterface* global_test_part_result_repoter_;
+
+ // Protects read and write access to global_test_part_result_reporter_.
+ internal::Mutex global_test_part_result_reporter_mutex_;
+
+ // Points to (but doesn't own) the per-thread test part result reporter.
+ internal::ThreadLocal<TestPartResultReporterInterface*>
+ per_thread_test_part_result_reporter_;
+
+ // The vector of environments that need to be set-up/torn-down
+ // before/after the tests are run.
+ std::vector<Environment*> environments_;
+
+ // The vector of TestCases in their original order. It owns the
+ // elements in the vector.
+ std::vector<TestCase*> test_cases_;
+
+ // Provides a level of indirection for the test case list to allow
+ // easy shuffling and restoring the test case order. The i-th
+ // element of this vector is the index of the i-th test case in the
+ // shuffled order.
+ std::vector<int> test_case_indices_;
+
+#if GTEST_HAS_PARAM_TEST
+ // ParameterizedTestRegistry object used to register value-parameterized
+ // tests.
+ internal::ParameterizedTestCaseRegistry parameterized_test_registry_;
+
+ // Indicates whether RegisterParameterizedTests() has been called already.
+ bool parameterized_tests_registered_;
+#endif // GTEST_HAS_PARAM_TEST
+
+ // Index of the last death test case registered. Initially -1.
+ int last_death_test_case_;
+
+ // This points to the TestCase for the currently running test. It
+ // changes as Google Test goes through one test case after another.
+ // When no test is running, this is set to NULL and Google Test
+ // stores assertion results in ad_hoc_test_result_. Initially NULL.
+ TestCase* current_test_case_;
+
+ // This points to the TestInfo for the currently running test. It
+ // changes as Google Test goes through one test after another. When
+ // no test is running, this is set to NULL and Google Test stores
+ // assertion results in ad_hoc_test_result_. Initially NULL.
+ TestInfo* current_test_info_;
+
+ // Normally, a user only writes assertions inside a TEST or TEST_F,
+ // or inside a function called by a TEST or TEST_F. Since Google
+ // Test keeps track of which test is current running, it can
+ // associate such an assertion with the test it belongs to.
+ //
+ // If an assertion is encountered when no TEST or TEST_F is running,
+ // Google Test attributes the assertion result to an imaginary "ad hoc"
+ // test, and records the result in ad_hoc_test_result_.
+ TestResult ad_hoc_test_result_;
+
+ // The list of event listeners that can be used to track events inside
+ // Google Test.
+ TestEventListeners listeners_;
+
+ // The OS stack trace getter. Will be deleted when the UnitTest
+ // object is destructed. By default, an OsStackTraceGetter is used,
+ // but the user can set this field to use a custom getter if that is
+ // desired.
+ OsStackTraceGetterInterface* os_stack_trace_getter_;
+
+ // True iff PostFlagParsingInit() has been called.
+ bool post_flag_parse_init_performed_;
+
+ // The random number seed used at the beginning of the test run.
+ int random_seed_;
+
+ // Our random number generator.
+ internal::Random random_;
+
+ // The time of the test program start, in ms from the start of the
+ // UNIX epoch.
+ TimeInMillis start_timestamp_;
+
+ // How long the test took to run, in milliseconds.
+ TimeInMillis elapsed_time_;
+
+#if GTEST_HAS_DEATH_TEST
+ // The decomposed components of the gtest_internal_run_death_test flag,
+ // parsed when RUN_ALL_TESTS is called.
+ internal::scoped_ptr<InternalRunDeathTestFlag> internal_run_death_test_flag_;
+ internal::scoped_ptr<internal::DeathTestFactory> death_test_factory_;
+#endif // GTEST_HAS_DEATH_TEST
+
+ // A per-thread stack of traces created by the SCOPED_TRACE() macro.
+ internal::ThreadLocal<std::vector<TraceInfo> > gtest_trace_stack_;
+
+ // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests()
+ // starts.
+ bool catch_exceptions_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl);
+}; // class UnitTestImpl
+
+// Convenience function for accessing the global UnitTest
+// implementation object.
+inline UnitTestImpl* GetUnitTestImpl() {
+ return UnitTest::GetInstance()->impl();
+}
+
+#if GTEST_USES_SIMPLE_RE
+
+// Internal helper functions for implementing the simple regular
+// expression matcher.
+GTEST_API_ bool IsInSet(char ch, const char* str);
+GTEST_API_ bool IsAsciiDigit(char ch);
+GTEST_API_ bool IsAsciiPunct(char ch);
+GTEST_API_ bool IsRepeat(char ch);
+GTEST_API_ bool IsAsciiWhiteSpace(char ch);
+GTEST_API_ bool IsAsciiWordChar(char ch);
+GTEST_API_ bool IsValidEscape(char ch);
+GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch);
+GTEST_API_ bool ValidateRegex(const char* regex);
+GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str);
+GTEST_API_ bool MatchRepetitionAndRegexAtHead(
+ bool escaped, char ch, char repeat, const char* regex, const char* str);
+GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str);
+
+#endif // GTEST_USES_SIMPLE_RE
+
+// Parses the command line for Google Test flags, without initializing
+// other parts of Google Test.
+GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv);
+GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv);
+
+#if GTEST_HAS_DEATH_TEST
+
+// Returns the message describing the last system error, regardless of the
+// platform.
+GTEST_API_ std::string GetLastErrnoDescription();
+
+# if GTEST_OS_WINDOWS
+// Provides leak-safe Windows kernel handle ownership.
+class AutoHandle {
+ public:
+ AutoHandle() : handle_(INVALID_HANDLE_VALUE) {}
+ explicit AutoHandle(HANDLE handle) : handle_(handle) {}
+
+ ~AutoHandle() { Reset(); }
+
+ HANDLE Get() const { return handle_; }
+ void Reset() { Reset(INVALID_HANDLE_VALUE); }
+ void Reset(HANDLE handle) {
+ if (handle != handle_) {
+ if (handle_ != INVALID_HANDLE_VALUE)
+ ::CloseHandle(handle_);
+ handle_ = handle;
+ }
+ }
+
+ private:
+ HANDLE handle_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle);
+};
+# endif // GTEST_OS_WINDOWS
+
+// Attempts to parse a string into a positive integer pointed to by the
+// number parameter. Returns true if that is possible.
+// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use
+// it here.
+template <typename Integer>
+bool ParseNaturalNumber(const ::std::string& str, Integer* number) {
+ // Fail fast if the given string does not begin with a digit;
+ // this bypasses strtoXXX's "optional leading whitespace and plus
+ // or minus sign" semantics, which are undesirable here.
+ if (str.empty() || !IsDigit(str[0])) {
+ return false;
+ }
+ errno = 0;
+
+ char* end;
+ // BiggestConvertible is the largest integer type that system-provided
+ // string-to-number conversion routines can return.
+
+# if GTEST_OS_WINDOWS && !defined(__GNUC__)
+
+ // MSVC and C++ Builder define __int64 instead of the standard long long.
+ typedef unsigned __int64 BiggestConvertible;
+ const BiggestConvertible parsed = _strtoui64(str.c_str(), &end, 10);
+
+# else
+
+ typedef unsigned long long BiggestConvertible; // NOLINT
+ const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10);
+
+# endif // GTEST_OS_WINDOWS && !defined(__GNUC__)
+
+ const bool parse_success = *end == '\0' && errno == 0;
+
+ // TODO(vladl at google.com): Convert this to compile time assertion when it is
+ // available.
+ GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed));
+
+ const Integer result = static_cast<Integer>(parsed);
+ if (parse_success && static_cast<BiggestConvertible>(result) == parsed) {
+ *number = result;
+ return true;
+ }
+ return false;
+}
+#endif // GTEST_HAS_DEATH_TEST
+
+// TestResult contains some private methods that should be hidden from
+// Google Test user but are required for testing. This class allow our tests
+// to access them.
+//
+// This class is supplied only for the purpose of testing Google Test's own
+// constructs. Do not use it in user tests, either directly or indirectly.
+class TestResultAccessor {
+ public:
+ static void RecordProperty(TestResult* test_result,
+ const std::string& xml_element,
+ const TestProperty& property) {
+ test_result->RecordProperty(xml_element, property);
+ }
+
+ static void ClearTestPartResults(TestResult* test_result) {
+ test_result->ClearTestPartResults();
+ }
+
+ static const std::vector<testing::TestPartResult>& test_part_results(
+ const TestResult& test_result) {
+ return test_result.test_part_results();
+ }
+};
+
+#if GTEST_CAN_STREAM_RESULTS_
+
+// Streams test results to the given port on the given host machine.
+class StreamingListener : public EmptyTestEventListener {
+ public:
+ // Abstract base class for writing strings to a socket.
+ class AbstractSocketWriter {
+ public:
+ virtual ~AbstractSocketWriter() {}
+
+ // Sends a string to the socket.
+ virtual void Send(const string& message) = 0;
+
+ // Closes the socket.
+ virtual void CloseConnection() {}
+
+ // Sends a string and a newline to the socket.
+ void SendLn(const string& message) {
+ Send(message + "\n");
+ }
+ };
+
+ // Concrete class for actually writing strings to a socket.
+ class SocketWriter : public AbstractSocketWriter {
+ public:
+ SocketWriter(const string& host, const string& port)
+ : sockfd_(-1), host_name_(host), port_num_(port) {
+ MakeConnection();
+ }
+
+ virtual ~SocketWriter() {
+ if (sockfd_ != -1)
+ CloseConnection();
+ }
+
+ // Sends a string to the socket.
+ virtual void Send(const string& message) {
+ GTEST_CHECK_(sockfd_ != -1)
+ << "Send() can be called only when there is a connection.";
+
+ const int len = static_cast<int>(message.length());
+ if (write(sockfd_, message.c_str(), len) != len) {
+ GTEST_LOG_(WARNING)
+ << "stream_result_to: failed to stream to "
+ << host_name_ << ":" << port_num_;
+ }
+ }
+
+ private:
+ // Creates a client socket and connects to the server.
+ void MakeConnection();
+
+ // Closes the socket.
+ void CloseConnection() {
+ GTEST_CHECK_(sockfd_ != -1)
+ << "CloseConnection() can be called only when there is a connection.";
+
+ close(sockfd_);
+ sockfd_ = -1;
+ }
+
+ int sockfd_; // socket file descriptor
+ const string host_name_;
+ const string port_num_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter);
+ }; // class SocketWriter
+
+ // Escapes '=', '&', '%', and '\n' characters in str as "%xx".
+ static string UrlEncode(const char* str);
+
+ StreamingListener(const string& host, const string& port)
+ : socket_writer_(new SocketWriter(host, port)) { Start(); }
+
+ explicit StreamingListener(AbstractSocketWriter* socket_writer)
+ : socket_writer_(socket_writer) { Start(); }
+
+ void OnTestProgramStart(const UnitTest& /* unit_test */) {
+ SendLn("event=TestProgramStart");
+ }
+
+ void OnTestProgramEnd(const UnitTest& unit_test) {
+ // Note that Google Test current only report elapsed time for each
+ // test iteration, not for the entire test program.
+ SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed()));
+
+ // Notify the streaming server to stop.
+ socket_writer_->CloseConnection();
+ }
+
+ void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) {
+ SendLn("event=TestIterationStart&iteration=" +
+ StreamableToString(iteration));
+ }
+
+ void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) {
+ SendLn("event=TestIterationEnd&passed=" +
+ FormatBool(unit_test.Passed()) + "&elapsed_time=" +
+ StreamableToString(unit_test.elapsed_time()) + "ms");
+ }
+
+ void OnTestCaseStart(const TestCase& test_case) {
+ SendLn(std::string("event=TestCaseStart&name=") + test_case.name());
+ }
+
+ void OnTestCaseEnd(const TestCase& test_case) {
+ SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed())
+ + "&elapsed_time=" + StreamableToString(test_case.elapsed_time())
+ + "ms");
+ }
+
+ void OnTestStart(const TestInfo& test_info) {
+ SendLn(std::string("event=TestStart&name=") + test_info.name());
+ }
+
+ void OnTestEnd(const TestInfo& test_info) {
+ SendLn("event=TestEnd&passed=" +
+ FormatBool((test_info.result())->Passed()) +
+ "&elapsed_time=" +
+ StreamableToString((test_info.result())->elapsed_time()) + "ms");
+ }
+
+ void OnTestPartResult(const TestPartResult& test_part_result) {
+ const char* file_name = test_part_result.file_name();
+ if (file_name == NULL)
+ file_name = "";
+ SendLn("event=TestPartResult&file=" + UrlEncode(file_name) +
+ "&line=" + StreamableToString(test_part_result.line_number()) +
+ "&message=" + UrlEncode(test_part_result.message()));
+ }
+
+ private:
+ // Sends the given message and a newline to the socket.
+ void SendLn(const string& message) { socket_writer_->SendLn(message); }
+
+ // Called at the start of streaming to notify the receiver what
+ // protocol we are using.
+ void Start() { SendLn("gtest_streaming_protocol_version=1.0"); }
+
+ string FormatBool(bool value) { return value ? "1" : "0"; }
+
+ const scoped_ptr<AbstractSocketWriter> socket_writer_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener);
+}; // class StreamingListener
+
+#endif // GTEST_CAN_STREAM_RESULTS_
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_
diff --git a/test/gtest/src/gtest-port.cc b/test/gtest/src/gtest-port.cc
new file mode 100644
index 0000000..0c4df5f
--- /dev/null
+++ b/test/gtest/src/gtest-port.cc
@@ -0,0 +1,805 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+#include "gtest/internal/gtest-port.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#if GTEST_OS_WINDOWS_MOBILE
+# include <windows.h> // For TerminateProcess()
+#elif GTEST_OS_WINDOWS
+# include <io.h>
+# include <sys/stat.h>
+#else
+# include <unistd.h>
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+#if GTEST_OS_MAC
+# include <mach/mach_init.h>
+# include <mach/task.h>
+# include <mach/vm_map.h>
+#endif // GTEST_OS_MAC
+
+#if GTEST_OS_QNX
+# include <devctl.h>
+# include <sys/procfs.h>
+#endif // GTEST_OS_QNX
+
+#include "gtest/gtest-spi.h"
+#include "gtest/gtest-message.h"
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-string.h"
+
+// Indicates that this translation unit is part of Google Test's
+// implementation. It must come before gtest-internal-inl.h is
+// included, or there will be a compiler error. This trick is to
+// prevent a user from accidentally including gtest-internal-inl.h in
+// his code.
+#define GTEST_IMPLEMENTATION_ 1
+#include "src/gtest-internal-inl.h"
+#undef GTEST_IMPLEMENTATION_
+
+namespace testing {
+namespace internal {
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+// MSVC and C++Builder do not provide a definition of STDERR_FILENO.
+const int kStdOutFileno = 1;
+const int kStdErrFileno = 2;
+#else
+const int kStdOutFileno = STDOUT_FILENO;
+const int kStdErrFileno = STDERR_FILENO;
+#endif // _MSC_VER
+
+#if GTEST_OS_MAC
+
+// Returns the number of threads running in the process, or 0 to indicate that
+// we cannot detect it.
+size_t GetThreadCount() {
+ const task_t task = mach_task_self();
+ mach_msg_type_number_t thread_count;
+ thread_act_array_t thread_list;
+ const kern_return_t status = task_threads(task, &thread_list, &thread_count);
+ if (status == KERN_SUCCESS) {
+ // task_threads allocates resources in thread_list and we need to free them
+ // to avoid leaks.
+ vm_deallocate(task,
+ reinterpret_cast<vm_address_t>(thread_list),
+ sizeof(thread_t) * thread_count);
+ return static_cast<size_t>(thread_count);
+ } else {
+ return 0;
+ }
+}
+
+#elif GTEST_OS_QNX
+
+// Returns the number of threads running in the process, or 0 to indicate that
+// we cannot detect it.
+size_t GetThreadCount() {
+ const int fd = open("/proc/self/as", O_RDONLY);
+ if (fd < 0) {
+ return 0;
+ }
+ procfs_info process_info;
+ const int status =
+ devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), NULL);
+ close(fd);
+ if (status == EOK) {
+ return static_cast<size_t>(process_info.num_threads);
+ } else {
+ return 0;
+ }
+}
+
+#else
+
+size_t GetThreadCount() {
+ // There's no portable way to detect the number of threads, so we just
+ // return 0 to indicate that we cannot detect it.
+ return 0;
+}
+
+#endif // GTEST_OS_MAC
+
+#if GTEST_USES_POSIX_RE
+
+// Implements RE. Currently only needed for death tests.
+
+RE::~RE() {
+ if (is_valid_) {
+ // regfree'ing an invalid regex might crash because the content
+ // of the regex is undefined. Since the regex's are essentially
+ // the same, one cannot be valid (or invalid) without the other
+ // being so too.
+ regfree(&partial_regex_);
+ regfree(&full_regex_);
+ }
+ free(const_cast<char*>(pattern_));
+}
+
+// Returns true iff regular expression re matches the entire str.
+bool RE::FullMatch(const char* str, const RE& re) {
+ if (!re.is_valid_) return false;
+
+ regmatch_t match;
+ return regexec(&re.full_regex_, str, 1, &match, 0) == 0;
+}
+
+// Returns true iff regular expression re matches a substring of str
+// (including str itself).
+bool RE::PartialMatch(const char* str, const RE& re) {
+ if (!re.is_valid_) return false;
+
+ regmatch_t match;
+ return regexec(&re.partial_regex_, str, 1, &match, 0) == 0;
+}
+
+// Initializes an RE from its string representation.
+void RE::Init(const char* regex) {
+ pattern_ = posix::StrDup(regex);
+
+ // Reserves enough bytes to hold the regular expression used for a
+ // full match.
+ const size_t full_regex_len = strlen(regex) + 10;
+ char* const full_pattern = new char[full_regex_len];
+
+ snprintf(full_pattern, full_regex_len, "^(%s)$", regex);
+ is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0;
+ // We want to call regcomp(&partial_regex_, ...) even if the
+ // previous expression returns false. Otherwise partial_regex_ may
+ // not be properly initialized can may cause trouble when it's
+ // freed.
+ //
+ // Some implementation of POSIX regex (e.g. on at least some
+ // versions of Cygwin) doesn't accept the empty string as a valid
+ // regex. We change it to an equivalent form "()" to be safe.
+ if (is_valid_) {
+ const char* const partial_regex = (*regex == '\0') ? "()" : regex;
+ is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0;
+ }
+ EXPECT_TRUE(is_valid_)
+ << "Regular expression \"" << regex
+ << "\" is not a valid POSIX Extended regular expression.";
+
+ delete[] full_pattern;
+}
+
+#elif GTEST_USES_SIMPLE_RE
+
+// Returns true iff ch appears anywhere in str (excluding the
+// terminating '\0' character).
+bool IsInSet(char ch, const char* str) {
+ return ch != '\0' && strchr(str, ch) != NULL;
+}
+
+// Returns true iff ch belongs to the given classification. Unlike
+// similar functions in <ctype.h>, these aren't affected by the
+// current locale.
+bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; }
+bool IsAsciiPunct(char ch) {
+ return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~");
+}
+bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); }
+bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); }
+bool IsAsciiWordChar(char ch) {
+ return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') ||
+ ('0' <= ch && ch <= '9') || ch == '_';
+}
+
+// Returns true iff "\\c" is a supported escape sequence.
+bool IsValidEscape(char c) {
+ return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW"));
+}
+
+// Returns true iff the given atom (specified by escaped and pattern)
+// matches ch. The result is undefined if the atom is invalid.
+bool AtomMatchesChar(bool escaped, char pattern_char, char ch) {
+ if (escaped) { // "\\p" where p is pattern_char.
+ switch (pattern_char) {
+ case 'd': return IsAsciiDigit(ch);
+ case 'D': return !IsAsciiDigit(ch);
+ case 'f': return ch == '\f';
+ case 'n': return ch == '\n';
+ case 'r': return ch == '\r';
+ case 's': return IsAsciiWhiteSpace(ch);
+ case 'S': return !IsAsciiWhiteSpace(ch);
+ case 't': return ch == '\t';
+ case 'v': return ch == '\v';
+ case 'w': return IsAsciiWordChar(ch);
+ case 'W': return !IsAsciiWordChar(ch);
+ }
+ return IsAsciiPunct(pattern_char) && pattern_char == ch;
+ }
+
+ return (pattern_char == '.' && ch != '\n') || pattern_char == ch;
+}
+
+// Helper function used by ValidateRegex() to format error messages.
+std::string FormatRegexSyntaxError(const char* regex, int index) {
+ return (Message() << "Syntax error at index " << index
+ << " in simple regular expression \"" << regex << "\": ").GetString();
+}
+
+// Generates non-fatal failures and returns false if regex is invalid;
+// otherwise returns true.
+bool ValidateRegex(const char* regex) {
+ if (regex == NULL) {
+ // TODO(wan at google.com): fix the source file location in the
+ // assertion failures to match where the regex is used in user
+ // code.
+ ADD_FAILURE() << "NULL is not a valid simple regular expression.";
+ return false;
+ }
+
+ bool is_valid = true;
+
+ // True iff ?, *, or + can follow the previous atom.
+ bool prev_repeatable = false;
+ for (int i = 0; regex[i]; i++) {
+ if (regex[i] == '\\') { // An escape sequence
+ i++;
+ if (regex[i] == '\0') {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1)
+ << "'\\' cannot appear at the end.";
+ return false;
+ }
+
+ if (!IsValidEscape(regex[i])) {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1)
+ << "invalid escape sequence \"\\" << regex[i] << "\".";
+ is_valid = false;
+ }
+ prev_repeatable = true;
+ } else { // Not an escape sequence.
+ const char ch = regex[i];
+
+ if (ch == '^' && i > 0) {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
+ << "'^' can only appear at the beginning.";
+ is_valid = false;
+ } else if (ch == '$' && regex[i + 1] != '\0') {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
+ << "'$' can only appear at the end.";
+ is_valid = false;
+ } else if (IsInSet(ch, "()[]{}|")) {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
+ << "'" << ch << "' is unsupported.";
+ is_valid = false;
+ } else if (IsRepeat(ch) && !prev_repeatable) {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
+ << "'" << ch << "' can only follow a repeatable token.";
+ is_valid = false;
+ }
+
+ prev_repeatable = !IsInSet(ch, "^$?*+");
+ }
+ }
+
+ return is_valid;
+}
+
+// Matches a repeated regex atom followed by a valid simple regular
+// expression. The regex atom is defined as c if escaped is false,
+// or \c otherwise. repeat is the repetition meta character (?, *,
+// or +). The behavior is undefined if str contains too many
+// characters to be indexable by size_t, in which case the test will
+// probably time out anyway. We are fine with this limitation as
+// std::string has it too.
+bool MatchRepetitionAndRegexAtHead(
+ bool escaped, char c, char repeat, const char* regex,
+ const char* str) {
+ const size_t min_count = (repeat == '+') ? 1 : 0;
+ const size_t max_count = (repeat == '?') ? 1 :
+ static_cast<size_t>(-1) - 1;
+ // We cannot call numeric_limits::max() as it conflicts with the
+ // max() macro on Windows.
+
+ for (size_t i = 0; i <= max_count; ++i) {
+ // We know that the atom matches each of the first i characters in str.
+ if (i >= min_count && MatchRegexAtHead(regex, str + i)) {
+ // We have enough matches at the head, and the tail matches too.
+ // Since we only care about *whether* the pattern matches str
+ // (as opposed to *how* it matches), there is no need to find a
+ // greedy match.
+ return true;
+ }
+ if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i]))
+ return false;
+ }
+ return false;
+}
+
+// Returns true iff regex matches a prefix of str. regex must be a
+// valid simple regular expression and not start with "^", or the
+// result is undefined.
+bool MatchRegexAtHead(const char* regex, const char* str) {
+ if (*regex == '\0') // An empty regex matches a prefix of anything.
+ return true;
+
+ // "$" only matches the end of a string. Note that regex being
+ // valid guarantees that there's nothing after "$" in it.
+ if (*regex == '$')
+ return *str == '\0';
+
+ // Is the first thing in regex an escape sequence?
+ const bool escaped = *regex == '\\';
+ if (escaped)
+ ++regex;
+ if (IsRepeat(regex[1])) {
+ // MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so
+ // here's an indirect recursion. It terminates as the regex gets
+ // shorter in each recursion.
+ return MatchRepetitionAndRegexAtHead(
+ escaped, regex[0], regex[1], regex + 2, str);
+ } else {
+ // regex isn't empty, isn't "$", and doesn't start with a
+ // repetition. We match the first atom of regex with the first
+ // character of str and recurse.
+ return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) &&
+ MatchRegexAtHead(regex + 1, str + 1);
+ }
+}
+
+// Returns true iff regex matches any substring of str. regex must be
+// a valid simple regular expression, or the result is undefined.
+//
+// The algorithm is recursive, but the recursion depth doesn't exceed
+// the regex length, so we won't need to worry about running out of
+// stack space normally. In rare cases the time complexity can be
+// exponential with respect to the regex length + the string length,
+// but usually it's must faster (often close to linear).
+bool MatchRegexAnywhere(const char* regex, const char* str) {
+ if (regex == NULL || str == NULL)
+ return false;
+
+ if (*regex == '^')
+ return MatchRegexAtHead(regex + 1, str);
+
+ // A successful match can be anywhere in str.
+ do {
+ if (MatchRegexAtHead(regex, str))
+ return true;
+ } while (*str++ != '\0');
+ return false;
+}
+
+// Implements the RE class.
+
+RE::~RE() {
+ free(const_cast<char*>(pattern_));
+ free(const_cast<char*>(full_pattern_));
+}
+
+// Returns true iff regular expression re matches the entire str.
+bool RE::FullMatch(const char* str, const RE& re) {
+ return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str);
+}
+
+// Returns true iff regular expression re matches a substring of str
+// (including str itself).
+bool RE::PartialMatch(const char* str, const RE& re) {
+ return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str);
+}
+
+// Initializes an RE from its string representation.
+void RE::Init(const char* regex) {
+ pattern_ = full_pattern_ = NULL;
+ if (regex != NULL) {
+ pattern_ = posix::StrDup(regex);
+ }
+
+ is_valid_ = ValidateRegex(regex);
+ if (!is_valid_) {
+ // No need to calculate the full pattern when the regex is invalid.
+ return;
+ }
+
+ const size_t len = strlen(regex);
+ // Reserves enough bytes to hold the regular expression used for a
+ // full match: we need space to prepend a '^', append a '$', and
+ // terminate the string with '\0'.
+ char* buffer = static_cast<char*>(malloc(len + 3));
+ full_pattern_ = buffer;
+
+ if (*regex != '^')
+ *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'.
+
+ // We don't use snprintf or strncpy, as they trigger a warning when
+ // compiled with VC++ 8.0.
+ memcpy(buffer, regex, len);
+ buffer += len;
+
+ if (len == 0 || regex[len - 1] != '$')
+ *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'.
+
+ *buffer = '\0';
+}
+
+#endif // GTEST_USES_POSIX_RE
+
+const char kUnknownFile[] = "unknown file";
+
+// Formats a source file path and a line number as they would appear
+// in an error message from the compiler used to compile this code.
+GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) {
+ const std::string file_name(file == NULL ? kUnknownFile : file);
+
+ if (line < 0) {
+ return file_name + ":";
+ }
+#ifdef _MSC_VER
+ return file_name + "(" + StreamableToString(line) + "):";
+#else
+ return file_name + ":" + StreamableToString(line) + ":";
+#endif // _MSC_VER
+}
+
+// Formats a file location for compiler-independent XML output.
+// Although this function is not platform dependent, we put it next to
+// FormatFileLocation in order to contrast the two functions.
+// Note that FormatCompilerIndependentFileLocation() does NOT append colon
+// to the file location it produces, unlike FormatFileLocation().
+GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(
+ const char* file, int line) {
+ const std::string file_name(file == NULL ? kUnknownFile : file);
+
+ if (line < 0)
+ return file_name;
+ else
+ return file_name + ":" + StreamableToString(line);
+}
+
+
+GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line)
+ : severity_(severity) {
+ const char* const marker =
+ severity == GTEST_INFO ? "[ INFO ]" :
+ severity == GTEST_WARNING ? "[WARNING]" :
+ severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]";
+ GetStream() << ::std::endl << marker << " "
+ << FormatFileLocation(file, line).c_str() << ": ";
+}
+
+// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program.
+GTestLog::~GTestLog() {
+ GetStream() << ::std::endl;
+ if (severity_ == GTEST_FATAL) {
+ fflush(stderr);
+ posix::Abort();
+ }
+}
+// Disable Microsoft deprecation warnings for POSIX functions called from
+// this class (creat, dup, dup2, and close)
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable: 4996)
+#endif // _MSC_VER
+
+#if GTEST_HAS_STREAM_REDIRECTION
+
+// Object that captures an output stream (stdout/stderr).
+class CapturedStream {
+ public:
+ // The ctor redirects the stream to a temporary file.
+ explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) {
+# if GTEST_OS_WINDOWS
+ char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT
+ char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT
+
+ ::GetTempPathA(sizeof(temp_dir_path), temp_dir_path);
+ const UINT success = ::GetTempFileNameA(temp_dir_path,
+ "gtest_redir",
+ 0, // Generate unique file name.
+ temp_file_path);
+ GTEST_CHECK_(success != 0)
+ << "Unable to create a temporary file in " << temp_dir_path;
+ const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE);
+ GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file "
+ << temp_file_path;
+ filename_ = temp_file_path;
+# else
+ // There's no guarantee that a test has write access to the current
+ // directory, so we create the temporary file in the /tmp directory
+ // instead. We use /tmp on most systems, and /sdcard on Android.
+ // That's because Android doesn't have /tmp.
+# if GTEST_OS_LINUX_ANDROID
+ // Note: Android applications are expected to call the framework's
+ // Context.getExternalStorageDirectory() method through JNI to get
+ // the location of the world-writable SD Card directory. However,
+ // this requires a Context handle, which cannot be retrieved
+ // globally from native code. Doing so also precludes running the
+ // code as part of a regular standalone executable, which doesn't
+ // run in a Dalvik process (e.g. when running it through 'adb shell').
+ //
+ // The location /sdcard is directly accessible from native code
+ // and is the only location (unofficially) supported by the Android
+ // team. It's generally a symlink to the real SD Card mount point
+ // which can be /mnt/sdcard, /mnt/sdcard0, /system/media/sdcard, or
+ // other OEM-customized locations. Never rely on these, and always
+ // use /sdcard.
+ char name_template[] = "/sdcard/gtest_captured_stream.XXXXXX";
+# else
+ char name_template[] = "/tmp/captured_stream.XXXXXX";
+# endif // GTEST_OS_LINUX_ANDROID
+ const int captured_fd = mkstemp(name_template);
+ filename_ = name_template;
+# endif // GTEST_OS_WINDOWS
+ fflush(NULL);
+ dup2(captured_fd, fd_);
+ close(captured_fd);
+ }
+
+ ~CapturedStream() {
+ remove(filename_.c_str());
+ }
+
+ std::string GetCapturedString() {
+ if (uncaptured_fd_ != -1) {
+ // Restores the original stream.
+ fflush(NULL);
+ dup2(uncaptured_fd_, fd_);
+ close(uncaptured_fd_);
+ uncaptured_fd_ = -1;
+ }
+
+ FILE* const file = posix::FOpen(filename_.c_str(), "r");
+ const std::string content = ReadEntireFile(file);
+ posix::FClose(file);
+ return content;
+ }
+
+ private:
+ // Reads the entire content of a file as an std::string.
+ static std::string ReadEntireFile(FILE* file);
+
+ // Returns the size (in bytes) of a file.
+ static size_t GetFileSize(FILE* file);
+
+ const int fd_; // A stream to capture.
+ int uncaptured_fd_;
+ // Name of the temporary file holding the stderr output.
+ ::std::string filename_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream);
+};
+
+// Returns the size (in bytes) of a file.
+size_t CapturedStream::GetFileSize(FILE* file) {
+ fseek(file, 0, SEEK_END);
+ return static_cast<size_t>(ftell(file));
+}
+
+// Reads the entire content of a file as a string.
+std::string CapturedStream::ReadEntireFile(FILE* file) {
+ const size_t file_size = GetFileSize(file);
+ char* const buffer = new char[file_size];
+
+ size_t bytes_last_read = 0; // # of bytes read in the last fread()
+ size_t bytes_read = 0; // # of bytes read so far
+
+ fseek(file, 0, SEEK_SET);
+
+ // Keeps reading the file until we cannot read further or the
+ // pre-determined file size is reached.
+ do {
+ bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file);
+ bytes_read += bytes_last_read;
+ } while (bytes_last_read > 0 && bytes_read < file_size);
+
+ const std::string content(buffer, bytes_read);
+ delete[] buffer;
+
+ return content;
+}
+
+# ifdef _MSC_VER
+# pragma warning(pop)
+# endif // _MSC_VER
+
+static CapturedStream* g_captured_stderr = NULL;
+static CapturedStream* g_captured_stdout = NULL;
+
+// Starts capturing an output stream (stdout/stderr).
+void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) {
+ if (*stream != NULL) {
+ GTEST_LOG_(FATAL) << "Only one " << stream_name
+ << " capturer can exist at a time.";
+ }
+ *stream = new CapturedStream(fd);
+}
+
+// Stops capturing the output stream and returns the captured string.
+std::string GetCapturedStream(CapturedStream** captured_stream) {
+ const std::string content = (*captured_stream)->GetCapturedString();
+
+ delete *captured_stream;
+ *captured_stream = NULL;
+
+ return content;
+}
+
+// Starts capturing stdout.
+void CaptureStdout() {
+ CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout);
+}
+
+// Starts capturing stderr.
+void CaptureStderr() {
+ CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr);
+}
+
+// Stops capturing stdout and returns the captured string.
+std::string GetCapturedStdout() {
+ return GetCapturedStream(&g_captured_stdout);
+}
+
+// Stops capturing stderr and returns the captured string.
+std::string GetCapturedStderr() {
+ return GetCapturedStream(&g_captured_stderr);
+}
+
+#endif // GTEST_HAS_STREAM_REDIRECTION
+
+#if GTEST_HAS_DEATH_TEST
+
+// A copy of all command line arguments. Set by InitGoogleTest().
+::std::vector<testing::internal::string> g_argvs;
+
+static const ::std::vector<testing::internal::string>* g_injected_test_argvs =
+ NULL; // Owned.
+
+void SetInjectableArgvs(const ::std::vector<testing::internal::string>* argvs) {
+ if (g_injected_test_argvs != argvs)
+ delete g_injected_test_argvs;
+ g_injected_test_argvs = argvs;
+}
+
+const ::std::vector<testing::internal::string>& GetInjectableArgvs() {
+ if (g_injected_test_argvs != NULL) {
+ return *g_injected_test_argvs;
+ }
+ return g_argvs;
+}
+#endif // GTEST_HAS_DEATH_TEST
+
+#if GTEST_OS_WINDOWS_MOBILE
+namespace posix {
+void Abort() {
+ DebugBreak();
+ TerminateProcess(GetCurrentProcess(), 1);
+}
+} // namespace posix
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+// Returns the name of the environment variable corresponding to the
+// given flag. For example, FlagToEnvVar("foo") will return
+// "GTEST_FOO" in the open-source version.
+static std::string FlagToEnvVar(const char* flag) {
+ const std::string full_flag =
+ (Message() << GTEST_FLAG_PREFIX_ << flag).GetString();
+
+ Message env_var;
+ for (size_t i = 0; i != full_flag.length(); i++) {
+ env_var << ToUpper(full_flag.c_str()[i]);
+ }
+
+ return env_var.GetString();
+}
+
+// Parses 'str' for a 32-bit signed integer. If successful, writes
+// the result to *value and returns true; otherwise leaves *value
+// unchanged and returns false.
+bool ParseInt32(const Message& src_text, const char* str, Int32* value) {
+ // Parses the environment variable as a decimal integer.
+ char* end = NULL;
+ const long long_value = strtol(str, &end, 10); // NOLINT
+
+ // Has strtol() consumed all characters in the string?
+ if (*end != '\0') {
+ // No - an invalid character was encountered.
+ Message msg;
+ msg << "WARNING: " << src_text
+ << " is expected to be a 32-bit integer, but actually"
+ << " has value \"" << str << "\".\n";
+ printf("%s", msg.GetString().c_str());
+ fflush(stdout);
+ return false;
+ }
+
+ // Is the parsed value in the range of an Int32?
+ const Int32 result = static_cast<Int32>(long_value);
+ if (long_value == LONG_MAX || long_value == LONG_MIN ||
+ // The parsed value overflows as a long. (strtol() returns
+ // LONG_MAX or LONG_MIN when the input overflows.)
+ result != long_value
+ // The parsed value overflows as an Int32.
+ ) {
+ Message msg;
+ msg << "WARNING: " << src_text
+ << " is expected to be a 32-bit integer, but actually"
+ << " has value " << str << ", which overflows.\n";
+ printf("%s", msg.GetString().c_str());
+ fflush(stdout);
+ return false;
+ }
+
+ *value = result;
+ return true;
+}
+
+// Reads and returns the Boolean environment variable corresponding to
+// the given flag; if it's not set, returns default_value.
+//
+// The value is considered true iff it's not "0".
+bool BoolFromGTestEnv(const char* flag, bool default_value) {
+ const std::string env_var = FlagToEnvVar(flag);
+ const char* const string_value = posix::GetEnv(env_var.c_str());
+ return string_value == NULL ?
+ default_value : strcmp(string_value, "0") != 0;
+}
+
+// Reads and returns a 32-bit integer stored in the environment
+// variable corresponding to the given flag; if it isn't set or
+// doesn't represent a valid 32-bit integer, returns default_value.
+Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) {
+ const std::string env_var = FlagToEnvVar(flag);
+ const char* const string_value = posix::GetEnv(env_var.c_str());
+ if (string_value == NULL) {
+ // The environment variable is not set.
+ return default_value;
+ }
+
+ Int32 result = default_value;
+ if (!ParseInt32(Message() << "Environment variable " << env_var,
+ string_value, &result)) {
+ printf("The default value %s is used.\n",
+ (Message() << default_value).GetString().c_str());
+ fflush(stdout);
+ return default_value;
+ }
+
+ return result;
+}
+
+// Reads and returns the string environment variable corresponding to
+// the given flag; if it's not set, returns default_value.
+const char* StringFromGTestEnv(const char* flag, const char* default_value) {
+ const std::string env_var = FlagToEnvVar(flag);
+ const char* const value = posix::GetEnv(env_var.c_str());
+ return value == NULL ? default_value : value;
+}
+
+} // namespace internal
+} // namespace testing
diff --git a/test/gtest/src/gtest-printers.cc b/test/gtest/src/gtest-printers.cc
new file mode 100644
index 0000000..75fa408
--- /dev/null
+++ b/test/gtest/src/gtest-printers.cc
@@ -0,0 +1,363 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+// Google Test - The Google C++ Testing Framework
+//
+// This file implements a universal value printer that can print a
+// value of any type T:
+//
+// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
+//
+// It uses the << operator when possible, and prints the bytes in the
+// object otherwise. A user can override its behavior for a class
+// type Foo by defining either operator<<(::std::ostream&, const Foo&)
+// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that
+// defines Foo.
+
+#include "gtest/gtest-printers.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <ostream> // NOLINT
+#include <string>
+#include "gtest/internal/gtest-port.h"
+
+namespace testing {
+
+namespace {
+
+using ::std::ostream;
+
+// Prints a segment of bytes in the given object.
+void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start,
+ size_t count, ostream* os) {
+ char text[5] = "";
+ for (size_t i = 0; i != count; i++) {
+ const size_t j = start + i;
+ if (i != 0) {
+ // Organizes the bytes into groups of 2 for easy parsing by
+ // human.
+ if ((j % 2) == 0)
+ *os << ' ';
+ else
+ *os << '-';
+ }
+ GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]);
+ *os << text;
+ }
+}
+
+// Prints the bytes in the given value to the given ostream.
+void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count,
+ ostream* os) {
+ // Tells the user how big the object is.
+ *os << count << "-byte object <";
+
+ const size_t kThreshold = 132;
+ const size_t kChunkSize = 64;
+ // If the object size is bigger than kThreshold, we'll have to omit
+ // some details by printing only the first and the last kChunkSize
+ // bytes.
+ // TODO(wan): let the user control the threshold using a flag.
+ if (count < kThreshold) {
+ PrintByteSegmentInObjectTo(obj_bytes, 0, count, os);
+ } else {
+ PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os);
+ *os << " ... ";
+ // Rounds up to 2-byte boundary.
+ const size_t resume_pos = (count - kChunkSize + 1)/2*2;
+ PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os);
+ }
+ *os << ">";
+}
+
+} // namespace
+
+namespace internal2 {
+
+// Delegates to PrintBytesInObjectToImpl() to print the bytes in the
+// given object. The delegation simplifies the implementation, which
+// uses the << operator and thus is easier done outside of the
+// ::testing::internal namespace, which contains a << operator that
+// sometimes conflicts with the one in STL.
+void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count,
+ ostream* os) {
+ PrintBytesInObjectToImpl(obj_bytes, count, os);
+}
+
+} // namespace internal2
+
+namespace internal {
+
+// Depending on the value of a char (or wchar_t), we print it in one
+// of three formats:
+// - as is if it's a printable ASCII (e.g. 'a', '2', ' '),
+// - as a hexidecimal escape sequence (e.g. '\x7F'), or
+// - as a special escape sequence (e.g. '\r', '\n').
+enum CharFormat {
+ kAsIs,
+ kHexEscape,
+ kSpecialEscape
+};
+
+// Returns true if c is a printable ASCII character. We test the
+// value of c directly instead of calling isprint(), which is buggy on
+// Windows Mobile.
+inline bool IsPrintableAscii(wchar_t c) {
+ return 0x20 <= c && c <= 0x7E;
+}
+
+// Prints a wide or narrow char c as a character literal without the
+// quotes, escaping it when necessary; returns how c was formatted.
+// The template argument UnsignedChar is the unsigned version of Char,
+// which is the type of c.
+template <typename UnsignedChar, typename Char>
+static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) {
+ switch (static_cast<wchar_t>(c)) {
+ case L'\0':
+ *os << "\\0";
+ break;
+ case L'\'':
+ *os << "\\'";
+ break;
+ case L'\\':
+ *os << "\\\\";
+ break;
+ case L'\a':
+ *os << "\\a";
+ break;
+ case L'\b':
+ *os << "\\b";
+ break;
+ case L'\f':
+ *os << "\\f";
+ break;
+ case L'\n':
+ *os << "\\n";
+ break;
+ case L'\r':
+ *os << "\\r";
+ break;
+ case L'\t':
+ *os << "\\t";
+ break;
+ case L'\v':
+ *os << "\\v";
+ break;
+ default:
+ if (IsPrintableAscii(c)) {
+ *os << static_cast<char>(c);
+ return kAsIs;
+ } else {
+ *os << "\\x" + String::FormatHexInt(static_cast<UnsignedChar>(c));
+ return kHexEscape;
+ }
+ }
+ return kSpecialEscape;
+}
+
+// Prints a wchar_t c as if it's part of a string literal, escaping it when
+// necessary; returns how c was formatted.
+static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) {
+ switch (c) {
+ case L'\'':
+ *os << "'";
+ return kAsIs;
+ case L'"':
+ *os << "\\\"";
+ return kSpecialEscape;
+ default:
+ return PrintAsCharLiteralTo<wchar_t>(c, os);
+ }
+}
+
+// Prints a char c as if it's part of a string literal, escaping it when
+// necessary; returns how c was formatted.
+static CharFormat PrintAsStringLiteralTo(char c, ostream* os) {
+ return PrintAsStringLiteralTo(
+ static_cast<wchar_t>(static_cast<unsigned char>(c)), os);
+}
+
+// Prints a wide or narrow character c and its code. '\0' is printed
+// as "'\\0'", other unprintable characters are also properly escaped
+// using the standard C++ escape sequence. The template argument
+// UnsignedChar is the unsigned version of Char, which is the type of c.
+template <typename UnsignedChar, typename Char>
+void PrintCharAndCodeTo(Char c, ostream* os) {
+ // First, print c as a literal in the most readable form we can find.
+ *os << ((sizeof(c) > 1) ? "L'" : "'");
+ const CharFormat format = PrintAsCharLiteralTo<UnsignedChar>(c, os);
+ *os << "'";
+
+ // To aid user debugging, we also print c's code in decimal, unless
+ // it's 0 (in which case c was printed as '\\0', making the code
+ // obvious).
+ if (c == 0)
+ return;
+ *os << " (" << static_cast<int>(c);
+
+ // For more convenience, we print c's code again in hexidecimal,
+ // unless c was already printed in the form '\x##' or the code is in
+ // [1, 9].
+ if (format == kHexEscape || (1 <= c && c <= 9)) {
+ // Do nothing.
+ } else {
+ *os << ", 0x" << String::FormatHexInt(static_cast<UnsignedChar>(c));
+ }
+ *os << ")";
+}
+
+void PrintTo(unsigned char c, ::std::ostream* os) {
+ PrintCharAndCodeTo<unsigned char>(c, os);
+}
+void PrintTo(signed char c, ::std::ostream* os) {
+ PrintCharAndCodeTo<unsigned char>(c, os);
+}
+
+// Prints a wchar_t as a symbol if it is printable or as its internal
+// code otherwise and also as its code. L'\0' is printed as "L'\\0'".
+void PrintTo(wchar_t wc, ostream* os) {
+ PrintCharAndCodeTo<wchar_t>(wc, os);
+}
+
+// Prints the given array of characters to the ostream. CharType must be either
+// char or wchar_t.
+// The array starts at begin, the length is len, it may include '\0' characters
+// and may not be NUL-terminated.
+template <typename CharType>
+static void PrintCharsAsStringTo(
+ const CharType* begin, size_t len, ostream* os) {
+ const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\"";
+ *os << kQuoteBegin;
+ bool is_previous_hex = false;
+ for (size_t index = 0; index < len; ++index) {
+ const CharType cur = begin[index];
+ if (is_previous_hex && IsXDigit(cur)) {
+ // Previous character is of '\x..' form and this character can be
+ // interpreted as another hexadecimal digit in its number. Break string to
+ // disambiguate.
+ *os << "\" " << kQuoteBegin;
+ }
+ is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape;
+ }
+ *os << "\"";
+}
+
+// Prints a (const) char/wchar_t array of 'len' elements, starting at address
+// 'begin'. CharType must be either char or wchar_t.
+template <typename CharType>
+static void UniversalPrintCharArray(
+ const CharType* begin, size_t len, ostream* os) {
+ // The code
+ // const char kFoo[] = "foo";
+ // generates an array of 4, not 3, elements, with the last one being '\0'.
+ //
+ // Therefore when printing a char array, we don't print the last element if
+ // it's '\0', such that the output matches the string literal as it's
+ // written in the source code.
+ if (len > 0 && begin[len - 1] == '\0') {
+ PrintCharsAsStringTo(begin, len - 1, os);
+ return;
+ }
+
+ // If, however, the last element in the array is not '\0', e.g.
+ // const char kFoo[] = { 'f', 'o', 'o' };
+ // we must print the entire array. We also print a message to indicate
+ // that the array is not NUL-terminated.
+ PrintCharsAsStringTo(begin, len, os);
+ *os << " (no terminating NUL)";
+}
+
+// Prints a (const) char array of 'len' elements, starting at address 'begin'.
+void UniversalPrintArray(const char* begin, size_t len, ostream* os) {
+ UniversalPrintCharArray(begin, len, os);
+}
+
+// Prints a (const) wchar_t array of 'len' elements, starting at address
+// 'begin'.
+void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) {
+ UniversalPrintCharArray(begin, len, os);
+}
+
+// Prints the given C string to the ostream.
+void PrintTo(const char* s, ostream* os) {
+ if (s == NULL) {
+ *os << "NULL";
+ } else {
+ *os << ImplicitCast_<const void*>(s) << " pointing to ";
+ PrintCharsAsStringTo(s, strlen(s), os);
+ }
+}
+
+// MSVC compiler can be configured to define whar_t as a typedef
+// of unsigned short. Defining an overload for const wchar_t* in that case
+// would cause pointers to unsigned shorts be printed as wide strings,
+// possibly accessing more memory than intended and causing invalid
+// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when
+// wchar_t is implemented as a native type.
+#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
+// Prints the given wide C string to the ostream.
+void PrintTo(const wchar_t* s, ostream* os) {
+ if (s == NULL) {
+ *os << "NULL";
+ } else {
+ *os << ImplicitCast_<const void*>(s) << " pointing to ";
+ PrintCharsAsStringTo(s, wcslen(s), os);
+ }
+}
+#endif // wchar_t is native
+
+// Prints a ::string object.
+#if GTEST_HAS_GLOBAL_STRING
+void PrintStringTo(const ::string& s, ostream* os) {
+ PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+#endif // GTEST_HAS_GLOBAL_STRING
+
+void PrintStringTo(const ::std::string& s, ostream* os) {
+ PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+
+// Prints a ::wstring object.
+#if GTEST_HAS_GLOBAL_WSTRING
+void PrintWideStringTo(const ::wstring& s, ostream* os) {
+ PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+#if GTEST_HAS_STD_WSTRING
+void PrintWideStringTo(const ::std::wstring& s, ostream* os) {
+ PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+#endif // GTEST_HAS_STD_WSTRING
+
+} // namespace internal
+
+} // namespace testing
diff --git a/test/gtest/src/gtest-test-part.cc b/test/gtest/src/gtest-test-part.cc
new file mode 100644
index 0000000..c60eef3
--- /dev/null
+++ b/test/gtest/src/gtest-test-part.cc
@@ -0,0 +1,110 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: mheule at google.com (Markus Heule)
+//
+// The Google C++ Testing Framework (Google Test)
+
+#include "gtest/gtest-test-part.h"
+
+// Indicates that this translation unit is part of Google Test's
+// implementation. It must come before gtest-internal-inl.h is
+// included, or there will be a compiler error. This trick is to
+// prevent a user from accidentally including gtest-internal-inl.h in
+// his code.
+#define GTEST_IMPLEMENTATION_ 1
+#include "src/gtest-internal-inl.h"
+#undef GTEST_IMPLEMENTATION_
+
+namespace testing {
+
+using internal::GetUnitTestImpl;
+
+// Gets the summary of the failure message by omitting the stack trace
+// in it.
+std::string TestPartResult::ExtractSummary(const char* message) {
+ const char* const stack_trace = strstr(message, internal::kStackTraceMarker);
+ return stack_trace == NULL ? message :
+ std::string(message, stack_trace);
+}
+
+// Prints a TestPartResult object.
+std::ostream& operator<<(std::ostream& os, const TestPartResult& result) {
+ return os
+ << result.file_name() << ":" << result.line_number() << ": "
+ << (result.type() == TestPartResult::kSuccess ? "Success" :
+ result.type() == TestPartResult::kFatalFailure ? "Fatal failure" :
+ "Non-fatal failure") << ":\n"
+ << result.message() << std::endl;
+}
+
+// Appends a TestPartResult to the array.
+void TestPartResultArray::Append(const TestPartResult& result) {
+ array_.push_back(result);
+}
+
+// Returns the TestPartResult at the given index (0-based).
+const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const {
+ if (index < 0 || index >= size()) {
+ printf("\nInvalid index (%d) into TestPartResultArray.\n", index);
+ internal::posix::Abort();
+ }
+
+ return array_[index];
+}
+
+// Returns the number of TestPartResult objects in the array.
+int TestPartResultArray::size() const {
+ return static_cast<int>(array_.size());
+}
+
+namespace internal {
+
+HasNewFatalFailureHelper::HasNewFatalFailureHelper()
+ : has_new_fatal_failure_(false),
+ original_reporter_(GetUnitTestImpl()->
+ GetTestPartResultReporterForCurrentThread()) {
+ GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this);
+}
+
+HasNewFatalFailureHelper::~HasNewFatalFailureHelper() {
+ GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(
+ original_reporter_);
+}
+
+void HasNewFatalFailureHelper::ReportTestPartResult(
+ const TestPartResult& result) {
+ if (result.fatally_failed())
+ has_new_fatal_failure_ = true;
+ original_reporter_->ReportTestPartResult(result);
+}
+
+} // namespace internal
+
+} // namespace testing
diff --git a/test/gtest/src/gtest-typed-test.cc b/test/gtest/src/gtest-typed-test.cc
new file mode 100644
index 0000000..f0079f4
--- /dev/null
+++ b/test/gtest/src/gtest-typed-test.cc
@@ -0,0 +1,110 @@
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+#include "gtest/gtest-typed-test.h"
+#include "gtest/gtest.h"
+
+namespace testing {
+namespace internal {
+
+#if GTEST_HAS_TYPED_TEST_P
+
+// Skips to the first non-space char in str. Returns an empty string if str
+// contains only whitespace characters.
+static const char* SkipSpaces(const char* str) {
+ while (IsSpace(*str))
+ str++;
+ return str;
+}
+
+// Verifies that registered_tests match the test names in
+// defined_test_names_; returns registered_tests if successful, or
+// aborts the program otherwise.
+const char* TypedTestCasePState::VerifyRegisteredTestNames(
+ const char* file, int line, const char* registered_tests) {
+ typedef ::std::set<const char*>::const_iterator DefinedTestIter;
+ registered_ = true;
+
+ // Skip initial whitespace in registered_tests since some
+ // preprocessors prefix stringizied literals with whitespace.
+ registered_tests = SkipSpaces(registered_tests);
+
+ Message errors;
+ ::std::set<std::string> tests;
+ for (const char* names = registered_tests; names != NULL;
+ names = SkipComma(names)) {
+ const std::string name = GetPrefixUntilComma(names);
+ if (tests.count(name) != 0) {
+ errors << "Test " << name << " is listed more than once.\n";
+ continue;
+ }
+
+ bool found = false;
+ for (DefinedTestIter it = defined_test_names_.begin();
+ it != defined_test_names_.end();
+ ++it) {
+ if (name == *it) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ tests.insert(name);
+ } else {
+ errors << "No test named " << name
+ << " can be found in this test case.\n";
+ }
+ }
+
+ for (DefinedTestIter it = defined_test_names_.begin();
+ it != defined_test_names_.end();
+ ++it) {
+ if (tests.count(*it) == 0) {
+ errors << "You forgot to list test " << *it << ".\n";
+ }
+ }
+
+ const std::string& errors_str = errors.GetString();
+ if (errors_str != "") {
+ fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(),
+ errors_str.c_str());
+ fflush(stderr);
+ posix::Abort();
+ }
+
+ return registered_tests;
+}
+
+#endif // GTEST_HAS_TYPED_TEST_P
+
+} // namespace internal
+} // namespace testing
diff --git a/test/gtest/src/gtest.cc b/test/gtest/src/gtest.cc
new file mode 100644
index 0000000..2ab80e2
--- /dev/null
+++ b/test/gtest/src/gtest.cc
@@ -0,0 +1,5019 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+//
+// The Google C++ Testing Framework (Google Test)
+
+#include "gtest/gtest.h"
+#include "gtest/gtest-spi.h"
+
+#ifndef _WIN32
+#pragma GCC system_header
+#endif
+
+#include <ctype.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <algorithm>
+#include <iomanip>
+#include <limits>
+#include <ostream> // NOLINT
+#include <sstream>
+#include <vector>
+
+#if GTEST_OS_LINUX
+
+// TODO(kenton at google.com): Use autoconf to detect availability of
+// gettimeofday().
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+
+# include <fcntl.h> // NOLINT
+# include <limits.h> // NOLINT
+# include <sched.h> // NOLINT
+// Declares vsnprintf(). This header is not available on Windows.
+# include <strings.h> // NOLINT
+# include <sys/mman.h> // NOLINT
+# include <sys/time.h> // NOLINT
+# include <unistd.h> // NOLINT
+# include <string>
+
+#elif GTEST_OS_SYMBIAN
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+# include <sys/time.h> // NOLINT
+
+#elif GTEST_OS_ZOS
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+# include <sys/time.h> // NOLINT
+
+// On z/OS we additionally need strings.h for strcasecmp.
+# include <strings.h> // NOLINT
+
+#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE.
+
+# include <windows.h> // NOLINT
+
+#elif GTEST_OS_WINDOWS // We are on Windows proper.
+
+# include <io.h> // NOLINT
+# include <sys/timeb.h> // NOLINT
+# include <sys/types.h> // NOLINT
+# include <sys/stat.h> // NOLINT
+
+# if GTEST_OS_WINDOWS_MINGW
+// MinGW has gettimeofday() but not _ftime64().
+// TODO(kenton at google.com): Use autoconf to detect availability of
+// gettimeofday().
+// TODO(kenton at google.com): There are other ways to get the time on
+// Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW
+// supports these. consider using them instead.
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+# include <sys/time.h> // NOLINT
+# endif // GTEST_OS_WINDOWS_MINGW
+
+// cpplint thinks that the header is already included, so we want to
+// silence it.
+# include <windows.h> // NOLINT
+
+#else
+
+// Assume other platforms have gettimeofday().
+// TODO(kenton at google.com): Use autoconf to detect availability of
+// gettimeofday().
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+
+// cpplint thinks that the header is already included, so we want to
+// silence it.
+# include <sys/time.h> // NOLINT
+# include <unistd.h> // NOLINT
+
+#endif // GTEST_OS_LINUX
+
+#if GTEST_HAS_EXCEPTIONS
+# include <stdexcept>
+#endif
+
+#if GTEST_CAN_STREAM_RESULTS_
+# include <arpa/inet.h> // NOLINT
+# include <netdb.h> // NOLINT
+#endif
+
+// Indicates that this translation unit is part of Google Test's
+// implementation. It must come before gtest-internal-inl.h is
+// included, or there will be a compiler error. This trick is to
+// prevent a user from accidentally including gtest-internal-inl.h in
+// his code.
+#define GTEST_IMPLEMENTATION_ 1
+#include "src/gtest-internal-inl.h"
+#undef GTEST_IMPLEMENTATION_
+
+#if GTEST_OS_WINDOWS
+# define vsnprintf _vsnprintf
+#endif // GTEST_OS_WINDOWS
+
+namespace testing {
+
+using internal::CountIf;
+using internal::ForEach;
+using internal::GetElementOr;
+using internal::Shuffle;
+
+// Constants.
+
+// A test whose test case name or test name matches this filter is
+// disabled and not run.
+static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*";
+
+// A test case whose name matches this filter is considered a death
+// test case and will be run before test cases whose name doesn't
+// match this filter.
+static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*";
+
+// A test filter that matches everything.
+static const char kUniversalFilter[] = "*";
+
+// The default output file for XML output.
+static const char kDefaultOutputFile[] = "test_detail.xml";
+
+// The environment variable name for the test shard index.
+static const char kTestShardIndex[] = "GTEST_SHARD_INDEX";
+// The environment variable name for the total number of test shards.
+static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS";
+// The environment variable name for the test shard status file.
+static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE";
+
+namespace internal {
+
+// The text used in failure messages to indicate the start of the
+// stack trace.
+const char kStackTraceMarker[] = "\nStack trace:\n";
+
+// g_help_flag is true iff the --help flag or an equivalent form is
+// specified on the command line.
+bool g_help_flag = false;
+
+} // namespace internal
+
+static const char* GetDefaultFilter() {
+ return kUniversalFilter;
+}
+
+GTEST_DEFINE_bool_(
+ also_run_disabled_tests,
+ internal::BoolFromGTestEnv("also_run_disabled_tests", false),
+ "Run disabled tests too, in addition to the tests normally being run.");
+
+GTEST_DEFINE_bool_(
+ break_on_failure,
+ internal::BoolFromGTestEnv("break_on_failure", false),
+ "True iff a failed assertion should be a debugger break-point.");
+
+GTEST_DEFINE_bool_(
+ catch_exceptions,
+ internal::BoolFromGTestEnv("catch_exceptions", true),
+ "True iff " GTEST_NAME_
+ " should catch exceptions and treat them as test failures.");
+
+GTEST_DEFINE_string_(
+ color,
+ internal::StringFromGTestEnv("color", "auto"),
+ "Whether to use colors in the output. Valid values: yes, no, "
+ "and auto. 'auto' means to use colors if the output is "
+ "being sent to a terminal and the TERM environment variable "
+ "is set to a terminal type that supports colors.");
+
+GTEST_DEFINE_string_(
+ filter,
+ internal::StringFromGTestEnv("filter", GetDefaultFilter()),
+ "A colon-separated list of glob (not regex) patterns "
+ "for filtering the tests to run, optionally followed by a "
+ "'-' and a : separated list of negative patterns (tests to "
+ "exclude). A test is run if it matches one of the positive "
+ "patterns and does not match any of the negative patterns.");
+
+GTEST_DEFINE_bool_(list_tests, false,
+ "List all tests without running them.");
+
+GTEST_DEFINE_string_(
+ output,
+ internal::StringFromGTestEnv("output", ""),
+ "A format (currently must be \"xml\"), optionally followed "
+ "by a colon and an output file name or directory. A directory "
+ "is indicated by a trailing pathname separator. "
+ "Examples: \"xml:filename.xml\", \"xml::directoryname/\". "
+ "If a directory is specified, output files will be created "
+ "within that directory, with file-names based on the test "
+ "executable's name and, if necessary, made unique by adding "
+ "digits.");
+
+GTEST_DEFINE_bool_(
+ print_time,
+ internal::BoolFromGTestEnv("print_time", true),
+ "True iff " GTEST_NAME_
+ " should display elapsed time in text output.");
+
+GTEST_DEFINE_int32_(
+ random_seed,
+ internal::Int32FromGTestEnv("random_seed", 0),
+ "Random number seed to use when shuffling test orders. Must be in range "
+ "[1, 99999], or 0 to use a seed based on the current time.");
+
+GTEST_DEFINE_int32_(
+ repeat,
+ internal::Int32FromGTestEnv("repeat", 1),
+ "How many times to repeat each test. Specify a negative number "
+ "for repeating forever. Useful for shaking out flaky tests.");
+
+GTEST_DEFINE_bool_(
+ show_internal_stack_frames, false,
+ "True iff " GTEST_NAME_ " should include internal stack frames when "
+ "printing test failure stack traces.");
+
+GTEST_DEFINE_bool_(
+ shuffle,
+ internal::BoolFromGTestEnv("shuffle", false),
+ "True iff " GTEST_NAME_
+ " should randomize tests' order on every run.");
+
+GTEST_DEFINE_int32_(
+ stack_trace_depth,
+ internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth),
+ "The maximum number of stack frames to print when an "
+ "assertion fails. The valid range is 0 through 100, inclusive.");
+
+GTEST_DEFINE_string_(
+ stream_result_to,
+ internal::StringFromGTestEnv("stream_result_to", ""),
+ "This flag specifies the host name and the port number on which to stream "
+ "test results. Example: \"localhost:555\". The flag is effective only on "
+ "Linux.");
+
+GTEST_DEFINE_bool_(
+ throw_on_failure,
+ internal::BoolFromGTestEnv("throw_on_failure", false),
+ "When this flag is specified, a failed assertion will throw an exception "
+ "if exceptions are enabled or exit the program with a non-zero code "
+ "otherwise.");
+
+namespace internal {
+
+// Generates a random number from [0, range), using a Linear
+// Congruential Generator (LCG). Crashes if 'range' is 0 or greater
+// than kMaxRange.
+UInt32 Random::Generate(UInt32 range) {
+ // These constants are the same as are used in glibc's rand(3).
+ state_ = (1103515245U*state_ + 12345U) % kMaxRange;
+
+ GTEST_CHECK_(range > 0)
+ << "Cannot generate a number in the range [0, 0).";
+ GTEST_CHECK_(range <= kMaxRange)
+ << "Generation of a number in [0, " << range << ") was requested, "
+ << "but this can only generate numbers in [0, " << kMaxRange << ").";
+
+ // Converting via modulus introduces a bit of downward bias, but
+ // it's simple, and a linear congruential generator isn't too good
+ // to begin with.
+ return state_ % range;
+}
+
+// GTestIsInitialized() returns true iff the user has initialized
+// Google Test. Useful for catching the user mistake of not initializing
+// Google Test before calling RUN_ALL_TESTS().
+//
+// A user must call testing::InitGoogleTest() to initialize Google
+// Test. g_init_gtest_count is set to the number of times
+// InitGoogleTest() has been called. We don't protect this variable
+// under a mutex as it is only accessed in the main thread.
+GTEST_API_ int g_init_gtest_count = 0;
+static bool GTestIsInitialized() { return g_init_gtest_count != 0; }
+
+// Iterates over a vector of TestCases, keeping a running sum of the
+// results of calling a given int-returning method on each.
+// Returns the sum.
+static int SumOverTestCaseList(const std::vector<TestCase*>& case_list,
+ int (TestCase::*method)() const) {
+ int sum = 0;
+ for (size_t i = 0; i < case_list.size(); i++) {
+ sum += (case_list[i]->*method)();
+ }
+ return sum;
+}
+
+// Returns true iff the test case passed.
+static bool TestCasePassed(const TestCase* test_case) {
+ return test_case->should_run() && test_case->Passed();
+}
+
+// Returns true iff the test case failed.
+static bool TestCaseFailed(const TestCase* test_case) {
+ return test_case->should_run() && test_case->Failed();
+}
+
+// Returns true iff test_case contains at least one test that should
+// run.
+static bool ShouldRunTestCase(const TestCase* test_case) {
+ return test_case->should_run();
+}
+
+// AssertHelper constructor.
+AssertHelper::AssertHelper(TestPartResult::Type type,
+ const char* file,
+ int line,
+ const char* message)
+ : data_(new AssertHelperData(type, file, line, message)) {
+}
+
+AssertHelper::~AssertHelper() {
+ delete data_;
+}
+
+// Message assignment, for assertion streaming support.
+void AssertHelper::operator=(const Message& message) const {
+ UnitTest::GetInstance()->
+ AddTestPartResult(data_->type, data_->file, data_->line,
+ AppendUserMessage(data_->message, message),
+ UnitTest::GetInstance()->impl()
+ ->CurrentOsStackTraceExceptTop(1)
+ // Skips the stack frame for this function itself.
+ ); // NOLINT
+}
+
+// Mutex for linked pointers.
+GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex);
+
+// Application pathname gotten in InitGoogleTest.
+std::string g_executable_path;
+
+// Returns the current application's name, removing directory path if that
+// is present.
+FilePath GetCurrentExecutableName() {
+ FilePath result;
+
+#if GTEST_OS_WINDOWS
+ result.Set(FilePath(g_executable_path).RemoveExtension("exe"));
+#else
+ result.Set(FilePath(g_executable_path));
+#endif // GTEST_OS_WINDOWS
+
+ return result.RemoveDirectoryName();
+}
+
+// Functions for processing the gtest_output flag.
+
+// Returns the output format, or "" for normal printed output.
+std::string UnitTestOptions::GetOutputFormat() {
+ const char* const gtest_output_flag = GTEST_FLAG(output).c_str();
+ if (gtest_output_flag == NULL) return std::string("");
+
+ const char* const colon = strchr(gtest_output_flag, ':');
+ return (colon == NULL) ?
+ std::string(gtest_output_flag) :
+ std::string(gtest_output_flag, colon - gtest_output_flag);
+}
+
+// Returns the name of the requested output file, or the default if none
+// was explicitly specified.
+std::string UnitTestOptions::GetAbsolutePathToOutputFile() {
+ const char* const gtest_output_flag = GTEST_FLAG(output).c_str();
+ if (gtest_output_flag == NULL)
+ return "";
+
+ const char* const colon = strchr(gtest_output_flag, ':');
+ if (colon == NULL)
+ return internal::FilePath::ConcatPaths(
+ internal::FilePath(
+ UnitTest::GetInstance()->original_working_dir()),
+ internal::FilePath(kDefaultOutputFile)).string();
+
+ internal::FilePath output_name(colon + 1);
+ if (!output_name.IsAbsolutePath())
+ // TODO(wan at google.com): on Windows \some\path is not an absolute
+ // path (as its meaning depends on the current drive), yet the
+ // following logic for turning it into an absolute path is wrong.
+ // Fix it.
+ output_name = internal::FilePath::ConcatPaths(
+ internal::FilePath(UnitTest::GetInstance()->original_working_dir()),
+ internal::FilePath(colon + 1));
+
+ if (!output_name.IsDirectory())
+ return output_name.string();
+
+ internal::FilePath result(internal::FilePath::GenerateUniqueFileName(
+ output_name, internal::GetCurrentExecutableName(),
+ GetOutputFormat().c_str()));
+ return result.string();
+}
+
+// Returns true iff the wildcard pattern matches the string. The
+// first ':' or '\0' character in pattern marks the end of it.
+//
+// This recursive algorithm isn't very efficient, but is clear and
+// works well enough for matching test names, which are short.
+bool UnitTestOptions::PatternMatchesString(const char *pattern,
+ const char *str) {
+ switch (*pattern) {
+ case '\0':
+ case ':': // Either ':' or '\0' marks the end of the pattern.
+ return *str == '\0';
+ case '?': // Matches any single character.
+ return *str != '\0' && PatternMatchesString(pattern + 1, str + 1);
+ case '*': // Matches any string (possibly empty) of characters.
+ return (*str != '\0' && PatternMatchesString(pattern, str + 1)) ||
+ PatternMatchesString(pattern + 1, str);
+ default: // Non-special character. Matches itself.
+ return *pattern == *str &&
+ PatternMatchesString(pattern + 1, str + 1);
+ }
+}
+
+bool UnitTestOptions::MatchesFilter(
+ const std::string& name, const char* filter) {
+ const char *cur_pattern = filter;
+ for (;;) {
+ if (PatternMatchesString(cur_pattern, name.c_str())) {
+ return true;
+ }
+
+ // Finds the next pattern in the filter.
+ cur_pattern = strchr(cur_pattern, ':');
+
+ // Returns if no more pattern can be found.
+ if (cur_pattern == NULL) {
+ return false;
+ }
+
+ // Skips the pattern separater (the ':' character).
+ cur_pattern++;
+ }
+}
+
+// Returns true iff the user-specified filter matches the test case
+// name and the test name.
+bool UnitTestOptions::FilterMatchesTest(const std::string &test_case_name,
+ const std::string &test_name) {
+ const std::string& full_name = test_case_name + "." + test_name.c_str();
+
+ // Split --gtest_filter at '-', if there is one, to separate into
+ // positive filter and negative filter portions
+ const char* const p = GTEST_FLAG(filter).c_str();
+ const char* const dash = strchr(p, '-');
+ std::string positive;
+ std::string negative;
+ if (dash == NULL) {
+ positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter
+ negative = "";
+ } else {
+ positive = std::string(p, dash); // Everything up to the dash
+ negative = std::string(dash + 1); // Everything after the dash
+ if (positive.empty()) {
+ // Treat '-test1' as the same as '*-test1'
+ positive = kUniversalFilter;
+ }
+ }
+
+ // A filter is a colon-separated list of patterns. It matches a
+ // test if any pattern in it matches the test.
+ return (MatchesFilter(full_name, positive.c_str()) &&
+ !MatchesFilter(full_name, negative.c_str()));
+}
+
+#if GTEST_HAS_SEH
+// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the
+// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise.
+// This function is useful as an __except condition.
+int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) {
+ // Google Test should handle a SEH exception if:
+ // 1. the user wants it to, AND
+ // 2. this is not a breakpoint exception, AND
+ // 3. this is not a C++ exception (VC++ implements them via SEH,
+ // apparently).
+ //
+ // SEH exception code for C++ exceptions.
+ // (see http://support.microsoft.com/kb/185294 for more information).
+ const DWORD kCxxExceptionCode = 0xe06d7363;
+
+ bool should_handle = true;
+
+ if (!GTEST_FLAG(catch_exceptions))
+ should_handle = false;
+ else if (exception_code == EXCEPTION_BREAKPOINT)
+ should_handle = false;
+ else if (exception_code == kCxxExceptionCode)
+ should_handle = false;
+
+ return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
+}
+#endif // GTEST_HAS_SEH
+
+} // namespace internal
+
+// The c'tor sets this object as the test part result reporter used by
+// Google Test. The 'result' parameter specifies where to report the
+// results. Intercepts only failures from the current thread.
+ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter(
+ TestPartResultArray* result)
+ : intercept_mode_(INTERCEPT_ONLY_CURRENT_THREAD),
+ result_(result) {
+ Init();
+}
+
+// The c'tor sets this object as the test part result reporter used by
+// Google Test. The 'result' parameter specifies where to report the
+// results.
+ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter(
+ InterceptMode intercept_mode, TestPartResultArray* result)
+ : intercept_mode_(intercept_mode),
+ result_(result) {
+ Init();
+}
+
+void ScopedFakeTestPartResultReporter::Init() {
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ if (intercept_mode_ == INTERCEPT_ALL_THREADS) {
+ old_reporter_ = impl->GetGlobalTestPartResultReporter();
+ impl->SetGlobalTestPartResultReporter(this);
+ } else {
+ old_reporter_ = impl->GetTestPartResultReporterForCurrentThread();
+ impl->SetTestPartResultReporterForCurrentThread(this);
+ }
+}
+
+// The d'tor restores the test part result reporter used by Google Test
+// before.
+ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() {
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ if (intercept_mode_ == INTERCEPT_ALL_THREADS) {
+ impl->SetGlobalTestPartResultReporter(old_reporter_);
+ } else {
+ impl->SetTestPartResultReporterForCurrentThread(old_reporter_);
+ }
+}
+
+// Increments the test part result count and remembers the result.
+// This method is from the TestPartResultReporterInterface interface.
+void ScopedFakeTestPartResultReporter::ReportTestPartResult(
+ const TestPartResult& result) {
+ result_->Append(result);
+}
+
+namespace internal {
+
+// Returns the type ID of ::testing::Test. We should always call this
+// instead of GetTypeId< ::testing::Test>() to get the type ID of
+// testing::Test. This is to work around a suspected linker bug when
+// using Google Test as a framework on Mac OS X. The bug causes
+// GetTypeId< ::testing::Test>() to return different values depending
+// on whether the call is from the Google Test framework itself or
+// from user test code. GetTestTypeId() is guaranteed to always
+// return the same value, as it always calls GetTypeId<>() from the
+// gtest.cc, which is within the Google Test framework.
+TypeId GetTestTypeId() {
+ return GetTypeId<Test>();
+}
+
+// The value of GetTestTypeId() as seen from within the Google Test
+// library. This is solely for testing GetTestTypeId().
+extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId();
+
+// This predicate-formatter checks that 'results' contains a test part
+// failure of the given type and that the failure message contains the
+// given substring.
+AssertionResult HasOneFailure(const char* /* results_expr */,
+ const char* /* type_expr */,
+ const char* /* substr_expr */,
+ const TestPartResultArray& results,
+ TestPartResult::Type type,
+ const string& substr) {
+ const std::string expected(type == TestPartResult::kFatalFailure ?
+ "1 fatal failure" :
+ "1 non-fatal failure");
+ Message msg;
+ if (results.size() != 1) {
+ msg << "Expected: " << expected << "\n"
+ << " Actual: " << results.size() << " failures";
+ for (int i = 0; i < results.size(); i++) {
+ msg << "\n" << results.GetTestPartResult(i);
+ }
+ return AssertionFailure() << msg;
+ }
+
+ const TestPartResult& r = results.GetTestPartResult(0);
+ if (r.type() != type) {
+ return AssertionFailure() << "Expected: " << expected << "\n"
+ << " Actual:\n"
+ << r;
+ }
+
+ if (strstr(r.message(), substr.c_str()) == NULL) {
+ return AssertionFailure() << "Expected: " << expected << " containing \""
+ << substr << "\"\n"
+ << " Actual:\n"
+ << r;
+ }
+
+ return AssertionSuccess();
+}
+
+// The constructor of SingleFailureChecker remembers where to look up
+// test part results, what type of failure we expect, and what
+// substring the failure message should contain.
+SingleFailureChecker:: SingleFailureChecker(
+ const TestPartResultArray* results,
+ TestPartResult::Type type,
+ const string& substr)
+ : results_(results),
+ type_(type),
+ substr_(substr) {}
+
+// The destructor of SingleFailureChecker verifies that the given
+// TestPartResultArray contains exactly one failure that has the given
+// type and contains the given substring. If that's not the case, a
+// non-fatal failure will be generated.
+SingleFailureChecker::~SingleFailureChecker() {
+ EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_);
+}
+
+DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter(
+ UnitTestImpl* unit_test) : unit_test_(unit_test) {}
+
+void DefaultGlobalTestPartResultReporter::ReportTestPartResult(
+ const TestPartResult& result) {
+ unit_test_->current_test_result()->AddTestPartResult(result);
+ unit_test_->listeners()->repeater()->OnTestPartResult(result);
+}
+
+DefaultPerThreadTestPartResultReporter::DefaultPerThreadTestPartResultReporter(
+ UnitTestImpl* unit_test) : unit_test_(unit_test) {}
+
+void DefaultPerThreadTestPartResultReporter::ReportTestPartResult(
+ const TestPartResult& result) {
+ unit_test_->GetGlobalTestPartResultReporter()->ReportTestPartResult(result);
+}
+
+// Returns the global test part result reporter.
+TestPartResultReporterInterface*
+UnitTestImpl::GetGlobalTestPartResultReporter() {
+ internal::MutexLock lock(&global_test_part_result_reporter_mutex_);
+ return global_test_part_result_repoter_;
+}
+
+// Sets the global test part result reporter.
+void UnitTestImpl::SetGlobalTestPartResultReporter(
+ TestPartResultReporterInterface* reporter) {
+ internal::MutexLock lock(&global_test_part_result_reporter_mutex_);
+ global_test_part_result_repoter_ = reporter;
+}
+
+// Returns the test part result reporter for the current thread.
+TestPartResultReporterInterface*
+UnitTestImpl::GetTestPartResultReporterForCurrentThread() {
+ return per_thread_test_part_result_reporter_.get();
+}
+
+// Sets the test part result reporter for the current thread.
+void UnitTestImpl::SetTestPartResultReporterForCurrentThread(
+ TestPartResultReporterInterface* reporter) {
+ per_thread_test_part_result_reporter_.set(reporter);
+}
+
+// Gets the number of successful test cases.
+int UnitTestImpl::successful_test_case_count() const {
+ return CountIf(test_cases_, TestCasePassed);
+}
+
+// Gets the number of failed test cases.
+int UnitTestImpl::failed_test_case_count() const {
+ return CountIf(test_cases_, TestCaseFailed);
+}
+
+// Gets the number of all test cases.
+int UnitTestImpl::total_test_case_count() const {
+ return static_cast<int>(test_cases_.size());
+}
+
+// Gets the number of all test cases that contain at least one test
+// that should run.
+int UnitTestImpl::test_case_to_run_count() const {
+ return CountIf(test_cases_, ShouldRunTestCase);
+}
+
+// Gets the number of successful tests.
+int UnitTestImpl::successful_test_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count);
+}
+
+// Gets the number of failed tests.
+int UnitTestImpl::failed_test_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count);
+}
+
+// Gets the number of disabled tests that will be reported in the XML report.
+int UnitTestImpl::reportable_disabled_test_count() const {
+ return SumOverTestCaseList(test_cases_,
+ &TestCase::reportable_disabled_test_count);
+}
+
+// Gets the number of disabled tests.
+int UnitTestImpl::disabled_test_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count);
+}
+
+// Gets the number of tests to be printed in the XML report.
+int UnitTestImpl::reportable_test_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::reportable_test_count);
+}
+
+// Gets the number of all tests.
+int UnitTestImpl::total_test_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::total_test_count);
+}
+
+// Gets the number of tests that should run.
+int UnitTestImpl::test_to_run_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count);
+}
+
+// Returns the current OS stack trace as an std::string.
+//
+// The maximum number of stack frames to be included is specified by
+// the gtest_stack_trace_depth flag. The skip_count parameter
+// specifies the number of top frames to be skipped, which doesn't
+// count against the number of frames to be included.
+//
+// For example, if Foo() calls Bar(), which in turn calls
+// CurrentOsStackTraceExceptTop(1), Foo() will be included in the
+// trace but Bar() and CurrentOsStackTraceExceptTop() won't.
+std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) {
+ (void)skip_count;
+ return "";
+}
+
+// Returns the current time in milliseconds.
+TimeInMillis GetTimeInMillis() {
+#if GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__)
+ // Difference between 1970-01-01 and 1601-01-01 in milliseconds.
+ // http://analogous.blogspot.com/2005/04/epoch.html
+ const TimeInMillis kJavaEpochToWinFileTimeDelta =
+ static_cast<TimeInMillis>(116444736UL) * 100000UL;
+ const DWORD kTenthMicrosInMilliSecond = 10000;
+
+ SYSTEMTIME now_systime;
+ FILETIME now_filetime;
+ ULARGE_INTEGER now_int64;
+ // TODO(kenton at google.com): Shouldn't this just use
+ // GetSystemTimeAsFileTime()?
+ GetSystemTime(&now_systime);
+ if (SystemTimeToFileTime(&now_systime, &now_filetime)) {
+ now_int64.LowPart = now_filetime.dwLowDateTime;
+ now_int64.HighPart = now_filetime.dwHighDateTime;
+ now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) -
+ kJavaEpochToWinFileTimeDelta;
+ return now_int64.QuadPart;
+ }
+ return 0;
+#elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_
+ __timeb64 now;
+
+# ifdef _MSC_VER
+
+ // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996
+ // (deprecated function) there.
+ // TODO(kenton at google.com): Use GetTickCount()? Or use
+ // SystemTimeToFileTime()
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4996) // Temporarily disables warning 4996.
+ _ftime64(&now);
+# pragma warning(pop) // Restores the warning state.
+# else
+
+ _ftime64(&now);
+
+# endif // _MSC_VER
+
+ return static_cast<TimeInMillis>(now.time) * 1000 + now.millitm;
+#elif GTEST_HAS_GETTIMEOFDAY_
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return static_cast<TimeInMillis>(now.tv_sec) * 1000 + now.tv_usec / 1000;
+#else
+# error "Don't know how to get the current time on your system."
+#endif
+}
+
+// Utilities
+
+// class String.
+
+#if GTEST_OS_WINDOWS_MOBILE
+// Creates a UTF-16 wide string from the given ANSI string, allocating
+// memory using new. The caller is responsible for deleting the return
+// value using delete[]. Returns the wide string, or NULL if the
+// input is NULL.
+LPCWSTR String::AnsiToUtf16(const char* ansi) {
+ if (!ansi) return NULL;
+ const int length = strlen(ansi);
+ const int unicode_length =
+ MultiByteToWideChar(CP_ACP, 0, ansi, length,
+ NULL, 0);
+ WCHAR* unicode = new WCHAR[unicode_length + 1];
+ MultiByteToWideChar(CP_ACP, 0, ansi, length,
+ unicode, unicode_length);
+ unicode[unicode_length] = 0;
+ return unicode;
+}
+
+// Creates an ANSI string from the given wide string, allocating
+// memory using new. The caller is responsible for deleting the return
+// value using delete[]. Returns the ANSI string, or NULL if the
+// input is NULL.
+const char* String::Utf16ToAnsi(LPCWSTR utf16_str) {
+ if (!utf16_str) return NULL;
+ const int ansi_length =
+ WideCharToMultiByte(CP_ACP, 0, utf16_str, -1,
+ NULL, 0, NULL, NULL);
+ char* ansi = new char[ansi_length + 1];
+ WideCharToMultiByte(CP_ACP, 0, utf16_str, -1,
+ ansi, ansi_length, NULL, NULL);
+ ansi[ansi_length] = 0;
+ return ansi;
+}
+
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+// Compares two C strings. Returns true iff they have the same content.
+//
+// Unlike strcmp(), this function can handle NULL argument(s). A NULL
+// C string is considered different to any non-NULL C string,
+// including the empty string.
+bool String::CStringEquals(const char * lhs, const char * rhs) {
+ if ( lhs == NULL ) return rhs == NULL;
+
+ if ( rhs == NULL ) return false;
+
+ return strcmp(lhs, rhs) == 0;
+}
+
+#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING
+
+// Converts an array of wide chars to a narrow string using the UTF-8
+// encoding, and streams the result to the given Message object.
+static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length,
+ Message* msg) {
+ for (size_t i = 0; i != length; ) { // NOLINT
+ if (wstr[i] != L'\0') {
+ *msg << WideStringToUtf8(wstr + i, static_cast<int>(length - i));
+ while (i != length && wstr[i] != L'\0')
+ i++;
+ } else {
+ *msg << '\0';
+ i++;
+ }
+ }
+}
+
+#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING
+
+} // namespace internal
+
+// Constructs an empty Message.
+// We allocate the stringstream separately because otherwise each use of
+// ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's
+// stack frame leading to huge stack frames in some cases; gcc does not reuse
+// the stack space.
+Message::Message() : ss_(new ::std::stringstream) {
+ // By default, we want there to be enough precision when printing
+ // a double to a Message.
+ *ss_ << std::setprecision(std::numeric_limits<double>::digits10 + 2);
+}
+
+// These two overloads allow streaming a wide C string to a Message
+// using the UTF-8 encoding.
+Message& Message::operator <<(const wchar_t* wide_c_str) {
+ return *this << internal::String::ShowWideCString(wide_c_str);
+}
+Message& Message::operator <<(wchar_t* wide_c_str) {
+ return *this << internal::String::ShowWideCString(wide_c_str);
+}
+
+#if GTEST_HAS_STD_WSTRING
+// Converts the given wide string to a narrow string using the UTF-8
+// encoding, and streams the result to this Message object.
+Message& Message::operator <<(const ::std::wstring& wstr) {
+ internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this);
+ return *this;
+}
+#endif // GTEST_HAS_STD_WSTRING
+
+#if GTEST_HAS_GLOBAL_WSTRING
+// Converts the given wide string to a narrow string using the UTF-8
+// encoding, and streams the result to this Message object.
+Message& Message::operator <<(const ::wstring& wstr) {
+ internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this);
+ return *this;
+}
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+// Gets the text streamed to this object so far as an std::string.
+// Each '\0' character in the buffer is replaced with "\\0".
+std::string Message::GetString() const {
+ return internal::StringStreamToString(ss_.get());
+}
+
+// AssertionResult constructors.
+// Used in EXPECT_TRUE/FALSE(assertion_result).
+AssertionResult::AssertionResult(const AssertionResult& other)
+ : success_(other.success_),
+ message_(other.message_.get() != NULL ?
+ new ::std::string(*other.message_) :
+ static_cast< ::std::string*>(NULL)) {
+}
+
+// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
+AssertionResult AssertionResult::operator!() const {
+ AssertionResult negation(!success_);
+ if (message_.get() != NULL)
+ negation << *message_;
+ return negation;
+}
+
+// Makes a successful assertion result.
+AssertionResult AssertionSuccess() {
+ return AssertionResult(true);
+}
+
+// Makes a failed assertion result.
+AssertionResult AssertionFailure() {
+ return AssertionResult(false);
+}
+
+// Makes a failed assertion result with the given failure message.
+// Deprecated; use AssertionFailure() << message.
+AssertionResult AssertionFailure(const Message& message) {
+ return AssertionFailure() << message;
+}
+
+namespace internal {
+
+// Constructs and returns the message for an equality assertion
+// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure.
+//
+// The first four parameters are the expressions used in the assertion
+// and their values, as strings. For example, for ASSERT_EQ(foo, bar)
+// where foo is 5 and bar is 6, we have:
+//
+// expected_expression: "foo"
+// actual_expression: "bar"
+// expected_value: "5"
+// actual_value: "6"
+//
+// The ignoring_case parameter is true iff the assertion is a
+// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will
+// be inserted into the message.
+AssertionResult EqFailure(const char* expected_expression,
+ const char* actual_expression,
+ const std::string& expected_value,
+ const std::string& actual_value,
+ bool ignoring_case) {
+ Message msg;
+ msg << "Value of: " << actual_expression;
+ if (actual_value != actual_expression) {
+ msg << "\n Actual: " << actual_value;
+ }
+
+ msg << "\nExpected: " << expected_expression;
+ if (ignoring_case) {
+ msg << " (ignoring case)";
+ }
+ if (expected_value != expected_expression) {
+ msg << "\nWhich is: " << expected_value;
+ }
+
+ return AssertionFailure() << msg;
+}
+
+// Constructs a failure message for Boolean assertions such as EXPECT_TRUE.
+std::string GetBoolAssertionFailureMessage(
+ const AssertionResult& assertion_result,
+ const char* expression_text,
+ const char* actual_predicate_value,
+ const char* expected_predicate_value) {
+ const char* actual_message = assertion_result.message();
+ Message msg;
+ msg << "Value of: " << expression_text
+ << "\n Actual: " << actual_predicate_value;
+ if (actual_message[0] != '\0')
+ msg << " (" << actual_message << ")";
+ msg << "\nExpected: " << expected_predicate_value;
+ return msg.GetString();
+}
+
+// Helper function for implementing ASSERT_NEAR.
+AssertionResult DoubleNearPredFormat(const char* expr1,
+ const char* expr2,
+ const char* abs_error_expr,
+ double val1,
+ double val2,
+ double abs_error) {
+ const double diff = fabs(val1 - val2);
+ if (diff <= abs_error) return AssertionSuccess();
+
+ // TODO(wan): do not print the value of an expression if it's
+ // already a literal.
+ return AssertionFailure()
+ << "The difference between " << expr1 << " and " << expr2
+ << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n"
+ << expr1 << " evaluates to " << val1 << ",\n"
+ << expr2 << " evaluates to " << val2 << ", and\n"
+ << abs_error_expr << " evaluates to " << abs_error << ".";
+}
+
+
+// Helper template for implementing FloatLE() and DoubleLE().
+template <typename RawType>
+AssertionResult FloatingPointLE(const char* expr1,
+ const char* expr2,
+ RawType val1,
+ RawType val2) {
+ // Returns success if val1 is less than val2,
+ if (val1 < val2) {
+ return AssertionSuccess();
+ }
+
+ // or if val1 is almost equal to val2.
+ const FloatingPoint<RawType> lhs(val1), rhs(val2);
+ if (lhs.AlmostEquals(rhs)) {
+ return AssertionSuccess();
+ }
+
+ // Note that the above two checks will both fail if either val1 or
+ // val2 is NaN, as the IEEE floating-point standard requires that
+ // any predicate involving a NaN must return false.
+
+ ::std::stringstream val1_ss;
+ val1_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+ << val1;
+
+ ::std::stringstream val2_ss;
+ val2_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+ << val2;
+
+ return AssertionFailure()
+ << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n"
+ << " Actual: " << StringStreamToString(&val1_ss) << " vs "
+ << StringStreamToString(&val2_ss);
+}
+
+} // namespace internal
+
+// Asserts that val1 is less than, or almost equal to, val2. Fails
+// otherwise. In particular, it fails if either val1 or val2 is NaN.
+AssertionResult FloatLE(const char* expr1, const char* expr2,
+ float val1, float val2) {
+ return internal::FloatingPointLE<float>(expr1, expr2, val1, val2);
+}
+
+// Asserts that val1 is less than, or almost equal to, val2. Fails
+// otherwise. In particular, it fails if either val1 or val2 is NaN.
+AssertionResult DoubleLE(const char* expr1, const char* expr2,
+ double val1, double val2) {
+ return internal::FloatingPointLE<double>(expr1, expr2, val1, val2);
+}
+
+namespace internal {
+
+// The helper function for {ASSERT|EXPECT}_EQ with int or enum
+// arguments.
+AssertionResult CmpHelperEQ(const char* expected_expression,
+ const char* actual_expression,
+ BiggestInt expected,
+ BiggestInt actual) {
+ if (expected == actual) {
+ return AssertionSuccess();
+ }
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ FormatForComparisonFailureMessage(expected, actual),
+ FormatForComparisonFailureMessage(actual, expected),
+ false);
+}
+
+// A macro for implementing the helper functions needed to implement
+// ASSERT_?? and EXPECT_?? with integer or enum arguments. It is here
+// just to avoid copy-and-paste of similar code.
+#define GTEST_IMPL_CMP_HELPER_(op_name, op)\
+AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \
+ BiggestInt val1, BiggestInt val2) {\
+ if (val1 op val2) {\
+ return AssertionSuccess();\
+ } else {\
+ return AssertionFailure() \
+ << "Expected: (" << expr1 << ") " #op " (" << expr2\
+ << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\
+ << " vs " << FormatForComparisonFailureMessage(val2, val1);\
+ }\
+}
+
+// Implements the helper function for {ASSERT|EXPECT}_NE with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(NE, !=)
+// Implements the helper function for {ASSERT|EXPECT}_LE with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(LE, <=)
+// Implements the helper function for {ASSERT|EXPECT}_LT with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(LT, < )
+// Implements the helper function for {ASSERT|EXPECT}_GE with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(GE, >=)
+// Implements the helper function for {ASSERT|EXPECT}_GT with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(GT, > )
+
+#undef GTEST_IMPL_CMP_HELPER_
+
+// The helper function for {ASSERT|EXPECT}_STREQ.
+AssertionResult CmpHelperSTREQ(const char* expected_expression,
+ const char* actual_expression,
+ const char* expected,
+ const char* actual) {
+ if (String::CStringEquals(expected, actual)) {
+ return AssertionSuccess();
+ }
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ PrintToString(expected),
+ PrintToString(actual),
+ false);
+}
+
+// The helper function for {ASSERT|EXPECT}_STRCASEEQ.
+AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression,
+ const char* actual_expression,
+ const char* expected,
+ const char* actual) {
+ if (String::CaseInsensitiveCStringEquals(expected, actual)) {
+ return AssertionSuccess();
+ }
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ PrintToString(expected),
+ PrintToString(actual),
+ true);
+}
+
+// The helper function for {ASSERT|EXPECT}_STRNE.
+AssertionResult CmpHelperSTRNE(const char* s1_expression,
+ const char* s2_expression,
+ const char* s1,
+ const char* s2) {
+ if (!String::CStringEquals(s1, s2)) {
+ return AssertionSuccess();
+ } else {
+ return AssertionFailure() << "Expected: (" << s1_expression << ") != ("
+ << s2_expression << "), actual: \""
+ << s1 << "\" vs \"" << s2 << "\"";
+ }
+}
+
+// The helper function for {ASSERT|EXPECT}_STRCASENE.
+AssertionResult CmpHelperSTRCASENE(const char* s1_expression,
+ const char* s2_expression,
+ const char* s1,
+ const char* s2) {
+ if (!String::CaseInsensitiveCStringEquals(s1, s2)) {
+ return AssertionSuccess();
+ } else {
+ return AssertionFailure()
+ << "Expected: (" << s1_expression << ") != ("
+ << s2_expression << ") (ignoring case), actual: \""
+ << s1 << "\" vs \"" << s2 << "\"";
+ }
+}
+
+} // namespace internal
+
+namespace {
+
+// Helper functions for implementing IsSubString() and IsNotSubstring().
+
+// This group of overloaded functions return true iff needle is a
+// substring of haystack. NULL is considered a substring of itself
+// only.
+
+bool IsSubstringPred(const char* needle, const char* haystack) {
+ if (needle == NULL || haystack == NULL)
+ return needle == haystack;
+
+ return strstr(haystack, needle) != NULL;
+}
+
+bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) {
+ if (needle == NULL || haystack == NULL)
+ return needle == haystack;
+
+ return wcsstr(haystack, needle) != NULL;
+}
+
+// StringType here can be either ::std::string or ::std::wstring.
+template <typename StringType>
+bool IsSubstringPred(const StringType& needle,
+ const StringType& haystack) {
+ return haystack.find(needle) != StringType::npos;
+}
+
+// This function implements either IsSubstring() or IsNotSubstring(),
+// depending on the value of the expected_to_be_substring parameter.
+// StringType here can be const char*, const wchar_t*, ::std::string,
+// or ::std::wstring.
+template <typename StringType>
+AssertionResult IsSubstringImpl(
+ bool expected_to_be_substring,
+ const char* needle_expr, const char* haystack_expr,
+ const StringType& needle, const StringType& haystack) {
+ if (IsSubstringPred(needle, haystack) == expected_to_be_substring)
+ return AssertionSuccess();
+
+ const bool is_wide_string = sizeof(needle[0]) > 1;
+ const char* const begin_string_quote = is_wide_string ? "L\"" : "\"";
+ return AssertionFailure()
+ << "Value of: " << needle_expr << "\n"
+ << " Actual: " << begin_string_quote << needle << "\"\n"
+ << "Expected: " << (expected_to_be_substring ? "" : "not ")
+ << "a substring of " << haystack_expr << "\n"
+ << "Which is: " << begin_string_quote << haystack << "\"";
+}
+
+} // namespace
+
+// IsSubstring() and IsNotSubstring() check whether needle is a
+// substring of haystack (NULL is considered a substring of itself
+// only), and return an appropriate error message when they fail.
+
+AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const char* needle, const char* haystack) {
+ return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const wchar_t* needle, const wchar_t* haystack) {
+ return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const char* needle, const char* haystack) {
+ return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const wchar_t* needle, const wchar_t* haystack) {
+ return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::string& needle, const ::std::string& haystack) {
+ return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::string& needle, const ::std::string& haystack) {
+ return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack);
+}
+
+#if GTEST_HAS_STD_WSTRING
+AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::wstring& needle, const ::std::wstring& haystack) {
+ return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::wstring& needle, const ::std::wstring& haystack) {
+ return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack);
+}
+#endif // GTEST_HAS_STD_WSTRING
+
+namespace internal {
+
+#if GTEST_OS_WINDOWS
+
+namespace {
+
+// Helper function for IsHRESULT{SuccessFailure} predicates
+AssertionResult HRESULTFailureHelper(const char* expr,
+ const char* expected,
+ long hr) { // NOLINT
+# if GTEST_OS_WINDOWS_MOBILE
+
+ // Windows CE doesn't support FormatMessage.
+ const char error_text[] = "";
+
+# else
+
+ // Looks up the human-readable system message for the HRESULT code
+ // and since we're not passing any params to FormatMessage, we don't
+ // want inserts expanded.
+ const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS;
+ const DWORD kBufSize = 4096;
+ // Gets the system's human readable message string for this HRESULT.
+ char error_text[kBufSize] = { '\0' };
+ DWORD message_length = ::FormatMessageA(kFlags,
+ 0, // no source, we're asking system
+ hr, // the error
+ 0, // no line width restrictions
+ error_text, // output buffer
+ kBufSize, // buf size
+ NULL); // no arguments for inserts
+ // Trims tailing white space (FormatMessage leaves a trailing CR-LF)
+ for (; message_length && IsSpace(error_text[message_length - 1]);
+ --message_length) {
+ error_text[message_length - 1] = '\0';
+ }
+
+# endif // GTEST_OS_WINDOWS_MOBILE
+
+ const std::string error_hex("0x" + String::FormatHexInt(hr));
+ return ::testing::AssertionFailure()
+ << "Expected: " << expr << " " << expected << ".\n"
+ << " Actual: " << error_hex << " " << error_text << "\n";
+}
+
+} // namespace
+
+AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT
+ if (SUCCEEDED(hr)) {
+ return AssertionSuccess();
+ }
+ return HRESULTFailureHelper(expr, "succeeds", hr);
+}
+
+AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT
+ if (FAILED(hr)) {
+ return AssertionSuccess();
+ }
+ return HRESULTFailureHelper(expr, "fails", hr);
+}
+
+#endif // GTEST_OS_WINDOWS
+
+// Utility functions for encoding Unicode text (wide strings) in
+// UTF-8.
+
+// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8
+// like this:
+//
+// Code-point length Encoding
+// 0 - 7 bits 0xxxxxxx
+// 8 - 11 bits 110xxxxx 10xxxxxx
+// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx
+// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+
+// The maximum code-point a one-byte UTF-8 sequence can represent.
+const UInt32 kMaxCodePoint1 = (static_cast<UInt32>(1) << 7) - 1;
+
+// The maximum code-point a two-byte UTF-8 sequence can represent.
+const UInt32 kMaxCodePoint2 = (static_cast<UInt32>(1) << (5 + 6)) - 1;
+
+// The maximum code-point a three-byte UTF-8 sequence can represent.
+const UInt32 kMaxCodePoint3 = (static_cast<UInt32>(1) << (4 + 2*6)) - 1;
+
+// The maximum code-point a four-byte UTF-8 sequence can represent.
+const UInt32 kMaxCodePoint4 = (static_cast<UInt32>(1) << (3 + 3*6)) - 1;
+
+// Chops off the n lowest bits from a bit pattern. Returns the n
+// lowest bits. As a side effect, the original bit pattern will be
+// shifted to the right by n bits.
+inline UInt32 ChopLowBits(UInt32* bits, int n) {
+ const UInt32 low_bits = *bits & ((static_cast<UInt32>(1) << n) - 1);
+ *bits >>= n;
+ return low_bits;
+}
+
+// Converts a Unicode code point to a narrow string in UTF-8 encoding.
+// code_point parameter is of type UInt32 because wchar_t may not be
+// wide enough to contain a code point.
+// If the code_point is not a valid Unicode code point
+// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted
+// to "(Invalid Unicode 0xXXXXXXXX)".
+std::string CodePointToUtf8(UInt32 code_point) {
+ if (code_point > kMaxCodePoint4) {
+ return "(Invalid Unicode 0x" + String::FormatHexInt(code_point) + ")";
+ }
+
+ char str[5]; // Big enough for the largest valid code point.
+ if (code_point <= kMaxCodePoint1) {
+ str[1] = '\0';
+ str[0] = static_cast<char>(code_point); // 0xxxxxxx
+ } else if (code_point <= kMaxCodePoint2) {
+ str[2] = '\0';
+ str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[0] = static_cast<char>(0xC0 | code_point); // 110xxxxx
+ } else if (code_point <= kMaxCodePoint3) {
+ str[3] = '\0';
+ str[2] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[0] = static_cast<char>(0xE0 | code_point); // 1110xxxx
+ } else { // code_point <= kMaxCodePoint4
+ str[4] = '\0';
+ str[3] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[2] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[0] = static_cast<char>(0xF0 | code_point); // 11110xxx
+ }
+ return str;
+}
+
+// The following two functions only make sense if the the system
+// uses UTF-16 for wide string encoding. All supported systems
+// with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16.
+
+// Determines if the arguments constitute UTF-16 surrogate pair
+// and thus should be combined into a single Unicode code point
+// using CreateCodePointFromUtf16SurrogatePair.
+inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) {
+ return sizeof(wchar_t) == 2 &&
+ (first & 0xFC00) == 0xD800 && (second & 0xFC00) == 0xDC00;
+}
+
+// Creates a Unicode code point from UTF16 surrogate pair.
+inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first,
+ wchar_t second) {
+ const UInt32 mask = (1 << 10) - 1;
+ return (sizeof(wchar_t) == 2) ?
+ (((first & mask) << 10) | (second & mask)) + 0x10000 :
+ // This function should not be called when the condition is
+ // false, but we provide a sensible default in case it is.
+ static_cast<UInt32>(first);
+}
+
+// Converts a wide string to a narrow string in UTF-8 encoding.
+// The wide string is assumed to have the following encoding:
+// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS)
+// UTF-32 if sizeof(wchar_t) == 4 (on Linux)
+// Parameter str points to a null-terminated wide string.
+// Parameter num_chars may additionally limit the number
+// of wchar_t characters processed. -1 is used when the entire string
+// should be processed.
+// If the string contains code points that are not valid Unicode code points
+// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output
+// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding
+// and contains invalid UTF-16 surrogate pairs, values in those pairs
+// will be encoded as individual Unicode characters from Basic Normal Plane.
+std::string WideStringToUtf8(const wchar_t* str, int num_chars) {
+ if (num_chars == -1)
+ num_chars = static_cast<int>(wcslen(str));
+
+ ::std::stringstream stream;
+ for (int i = 0; i < num_chars; ++i) {
+ UInt32 unicode_code_point;
+
+ if (str[i] == L'\0') {
+ break;
+ } else if (i + 1 < num_chars && IsUtf16SurrogatePair(str[i], str[i + 1])) {
+ unicode_code_point = CreateCodePointFromUtf16SurrogatePair(str[i],
+ str[i + 1]);
+ i++;
+ } else {
+ unicode_code_point = static_cast<UInt32>(str[i]);
+ }
+
+ stream << CodePointToUtf8(unicode_code_point);
+ }
+ return StringStreamToString(&stream);
+}
+
+// Converts a wide C string to an std::string using the UTF-8 encoding.
+// NULL will be converted to "(null)".
+std::string String::ShowWideCString(const wchar_t * wide_c_str) {
+ if (wide_c_str == NULL) return "(null)";
+
+ return internal::WideStringToUtf8(wide_c_str, -1);
+}
+
+// Compares two wide C strings. Returns true iff they have the same
+// content.
+//
+// Unlike wcscmp(), this function can handle NULL argument(s). A NULL
+// C string is considered different to any non-NULL C string,
+// including the empty string.
+bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) {
+ if (lhs == NULL) return rhs == NULL;
+
+ if (rhs == NULL) return false;
+
+ return wcscmp(lhs, rhs) == 0;
+}
+
+// Helper function for *_STREQ on wide strings.
+AssertionResult CmpHelperSTREQ(const char* expected_expression,
+ const char* actual_expression,
+ const wchar_t* expected,
+ const wchar_t* actual) {
+ if (String::WideCStringEquals(expected, actual)) {
+ return AssertionSuccess();
+ }
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ PrintToString(expected),
+ PrintToString(actual),
+ false);
+}
+
+// Helper function for *_STRNE on wide strings.
+AssertionResult CmpHelperSTRNE(const char* s1_expression,
+ const char* s2_expression,
+ const wchar_t* s1,
+ const wchar_t* s2) {
+ if (!String::WideCStringEquals(s1, s2)) {
+ return AssertionSuccess();
+ }
+
+ return AssertionFailure() << "Expected: (" << s1_expression << ") != ("
+ << s2_expression << "), actual: "
+ << PrintToString(s1)
+ << " vs " << PrintToString(s2);
+}
+
+// Compares two C strings, ignoring case. Returns true iff they have
+// the same content.
+//
+// Unlike strcasecmp(), this function can handle NULL argument(s). A
+// NULL C string is considered different to any non-NULL C string,
+// including the empty string.
+bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) {
+ if (lhs == NULL)
+ return rhs == NULL;
+ if (rhs == NULL)
+ return false;
+ return posix::StrCaseCmp(lhs, rhs) == 0;
+}
+
+ // Compares two wide C strings, ignoring case. Returns true iff they
+ // have the same content.
+ //
+ // Unlike wcscasecmp(), this function can handle NULL argument(s).
+ // A NULL C string is considered different to any non-NULL wide C string,
+ // including the empty string.
+ // NB: The implementations on different platforms slightly differ.
+ // On windows, this method uses _wcsicmp which compares according to LC_CTYPE
+ // environment variable. On GNU platform this method uses wcscasecmp
+ // which compares according to LC_CTYPE category of the current locale.
+ // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the
+ // current locale.
+bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs,
+ const wchar_t* rhs) {
+ if (lhs == NULL) return rhs == NULL;
+
+ if (rhs == NULL) return false;
+
+#if GTEST_OS_WINDOWS
+ return _wcsicmp(lhs, rhs) == 0;
+#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID
+ return wcscasecmp(lhs, rhs) == 0;
+#else
+ // Android, Mac OS X and Cygwin don't define wcscasecmp.
+ // Other unknown OSes may not define it either.
+ wint_t left, right;
+ do {
+ left = towlower(*lhs++);
+ right = towlower(*rhs++);
+ } while (left && left == right);
+ return left == right;
+#endif // OS selector
+}
+
+// Returns true iff str ends with the given suffix, ignoring case.
+// Any string is considered to end with an empty suffix.
+bool String::EndsWithCaseInsensitive(
+ const std::string& str, const std::string& suffix) {
+ const size_t str_len = str.length();
+ const size_t suffix_len = suffix.length();
+ return (str_len >= suffix_len) &&
+ CaseInsensitiveCStringEquals(str.c_str() + str_len - suffix_len,
+ suffix.c_str());
+}
+
+// Formats an int value as "%02d".
+std::string String::FormatIntWidth2(int value) {
+ std::stringstream ss;
+ ss << std::setfill('0') << std::setw(2) << value;
+ return ss.str();
+}
+
+// Formats an int value as "%X".
+std::string String::FormatHexInt(int value) {
+ std::stringstream ss;
+ ss << std::hex << std::uppercase << value;
+ return ss.str();
+}
+
+// Formats a byte as "%02X".
+std::string String::FormatByte(unsigned char value) {
+ std::stringstream ss;
+ ss << std::setfill('0') << std::setw(2) << std::hex << std::uppercase
+ << static_cast<unsigned int>(value);
+ return ss.str();
+}
+
+// Converts the buffer in a stringstream to an std::string, converting NUL
+// bytes to "\\0" along the way.
+std::string StringStreamToString(::std::stringstream* ss) {
+ const ::std::string& str = ss->str();
+ const char* const start = str.c_str();
+ const char* const end = start + str.length();
+
+ std::string result;
+ result.reserve(2 * (end - start));
+ for (const char* ch = start; ch != end; ++ch) {
+ if (*ch == '\0') {
+ result += "\\0"; // Replaces NUL with "\\0";
+ } else {
+ result += *ch;
+ }
+ }
+
+ return result;
+}
+
+// Appends the user-supplied message to the Google-Test-generated message.
+std::string AppendUserMessage(const std::string& gtest_msg,
+ const Message& user_msg) {
+ // Appends the user message if it's non-empty.
+ const std::string user_msg_string = user_msg.GetString();
+ if (user_msg_string.empty()) {
+ return gtest_msg;
+ }
+
+ return gtest_msg + "\n" + user_msg_string;
+}
+
+} // namespace internal
+
+// class TestResult
+
+// Creates an empty TestResult.
+TestResult::TestResult()
+ : death_test_count_(0),
+ elapsed_time_(0) {
+}
+
+// D'tor.
+TestResult::~TestResult() {
+}
+
+// Returns the i-th test part result among all the results. i can
+// range from 0 to total_part_count() - 1. If i is not in that range,
+// aborts the program.
+const TestPartResult& TestResult::GetTestPartResult(int i) const {
+ if (i < 0 || i >= total_part_count())
+ internal::posix::Abort();
+ return test_part_results_.at(i);
+}
+
+// Returns the i-th test property. i can range from 0 to
+// test_property_count() - 1. If i is not in that range, aborts the
+// program.
+const TestProperty& TestResult::GetTestProperty(int i) const {
+ if (i < 0 || i >= test_property_count())
+ internal::posix::Abort();
+ return test_properties_.at(i);
+}
+
+// Clears the test part results.
+void TestResult::ClearTestPartResults() {
+ test_part_results_.clear();
+}
+
+// Adds a test part result to the list.
+void TestResult::AddTestPartResult(const TestPartResult& test_part_result) {
+ test_part_results_.push_back(test_part_result);
+}
+
+// Adds a test property to the list. If a property with the same key as the
+// supplied property is already represented, the value of this test_property
+// replaces the old value for that key.
+void TestResult::RecordProperty(const std::string& xml_element,
+ const TestProperty& test_property) {
+ if (!ValidateTestProperty(xml_element, test_property)) {
+ return;
+ }
+ internal::MutexLock lock(&test_properites_mutex_);
+ const std::vector<TestProperty>::iterator property_with_matching_key =
+ std::find_if(test_properties_.begin(), test_properties_.end(),
+ internal::TestPropertyKeyIs(test_property.key()));
+ if (property_with_matching_key == test_properties_.end()) {
+ test_properties_.push_back(test_property);
+ return;
+ }
+ property_with_matching_key->SetValue(test_property.value());
+}
+
+// The list of reserved attributes used in the <testsuites> element of XML
+// output.
+static const char* const kReservedTestSuitesAttributes[] = {
+ "disabled",
+ "errors",
+ "failures",
+ "name",
+ "random_seed",
+ "tests",
+ "time",
+ "timestamp"
+};
+
+// The list of reserved attributes used in the <testsuite> element of XML
+// output.
+static const char* const kReservedTestSuiteAttributes[] = {
+ "disabled",
+ "errors",
+ "failures",
+ "name",
+ "tests",
+ "time"
+};
+
+// The list of reserved attributes used in the <testcase> element of XML output.
+static const char* const kReservedTestCaseAttributes[] = {
+ "classname",
+ "name",
+ "status",
+ "time",
+ "type_param",
+ "value_param"
+};
+
+template <int kSize>
+std::vector<std::string> ArrayAsVector(const char* const (&array)[kSize]) {
+ return std::vector<std::string>(array, array + kSize);
+}
+
+static std::vector<std::string> GetReservedAttributesForElement(
+ const std::string& xml_element) {
+ if (xml_element == "testsuites") {
+ return ArrayAsVector(kReservedTestSuitesAttributes);
+ } else if (xml_element == "testsuite") {
+ return ArrayAsVector(kReservedTestSuiteAttributes);
+ } else if (xml_element == "testcase") {
+ return ArrayAsVector(kReservedTestCaseAttributes);
+ } else {
+ GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element;
+ }
+ // This code is unreachable but some compilers may not realizes that.
+ return std::vector<std::string>();
+}
+
+static std::string FormatWordList(const std::vector<std::string>& words) {
+ Message word_list;
+ for (size_t i = 0; i < words.size(); ++i) {
+ if (i > 0 && words.size() > 2) {
+ word_list << ", ";
+ }
+ if (i == words.size() - 1) {
+ word_list << "and ";
+ }
+ word_list << "'" << words[i] << "'";
+ }
+ return word_list.GetString();
+}
+
+bool ValidateTestPropertyName(const std::string& property_name,
+ const std::vector<std::string>& reserved_names) {
+ if (std::find(reserved_names.begin(), reserved_names.end(), property_name) !=
+ reserved_names.end()) {
+ ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name
+ << " (" << FormatWordList(reserved_names)
+ << " are reserved by " << GTEST_NAME_ << ")";
+ return false;
+ }
+ return true;
+}
+
+// Adds a failure if the key is a reserved attribute of the element named
+// xml_element. Returns true if the property is valid.
+bool TestResult::ValidateTestProperty(const std::string& xml_element,
+ const TestProperty& test_property) {
+ return ValidateTestPropertyName(test_property.key(),
+ GetReservedAttributesForElement(xml_element));
+}
+
+// Clears the object.
+void TestResult::Clear() {
+ test_part_results_.clear();
+ test_properties_.clear();
+ death_test_count_ = 0;
+ elapsed_time_ = 0;
+}
+
+// Returns true iff the test failed.
+bool TestResult::Failed() const {
+ for (int i = 0; i < total_part_count(); ++i) {
+ if (GetTestPartResult(i).failed())
+ return true;
+ }
+ return false;
+}
+
+// Returns true iff the test part fatally failed.
+static bool TestPartFatallyFailed(const TestPartResult& result) {
+ return result.fatally_failed();
+}
+
+// Returns true iff the test fatally failed.
+bool TestResult::HasFatalFailure() const {
+ return CountIf(test_part_results_, TestPartFatallyFailed) > 0;
+}
+
+// Returns true iff the test part non-fatally failed.
+static bool TestPartNonfatallyFailed(const TestPartResult& result) {
+ return result.nonfatally_failed();
+}
+
+// Returns true iff the test has a non-fatal failure.
+bool TestResult::HasNonfatalFailure() const {
+ return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0;
+}
+
+// Gets the number of all test parts. This is the sum of the number
+// of successful test parts and the number of failed test parts.
+int TestResult::total_part_count() const {
+ return static_cast<int>(test_part_results_.size());
+}
+
+// Returns the number of the test properties.
+int TestResult::test_property_count() const {
+ return static_cast<int>(test_properties_.size());
+}
+
+// class Test
+
+// Creates a Test object.
+
+// The c'tor saves the values of all Google Test flags.
+Test::Test()
+ : gtest_flag_saver_(new internal::GTestFlagSaver) {
+}
+
+// The d'tor restores the values of all Google Test flags.
+Test::~Test() {
+ delete gtest_flag_saver_;
+}
+
+// Sets up the test fixture.
+//
+// A sub-class may override this.
+void Test::SetUp() {
+}
+
+// Tears down the test fixture.
+//
+// A sub-class may override this.
+void Test::TearDown() {
+}
+
+// Allows user supplied key value pairs to be recorded for later output.
+void Test::RecordProperty(const std::string& key, const std::string& value) {
+ UnitTest::GetInstance()->RecordProperty(key, value);
+}
+
+// Allows user supplied key value pairs to be recorded for later output.
+void Test::RecordProperty(const std::string& key, int value) {
+ Message value_message;
+ value_message << value;
+ RecordProperty(key, value_message.GetString().c_str());
+}
+
+namespace internal {
+
+void ReportFailureInUnknownLocation(TestPartResult::Type result_type,
+ const std::string& message) {
+ // This function is a friend of UnitTest and as such has access to
+ // AddTestPartResult.
+ UnitTest::GetInstance()->AddTestPartResult(
+ result_type,
+ NULL, // No info about the source file where the exception occurred.
+ -1, // We have no info on which line caused the exception.
+ message,
+ ""); // No stack trace, either.
+}
+
+} // namespace internal
+
+// Google Test requires all tests in the same test case to use the same test
+// fixture class. This function checks if the current test has the
+// same fixture class as the first test in the current test case. If
+// yes, it returns true; otherwise it generates a Google Test failure and
+// returns false.
+bool Test::HasSameFixtureClass() {
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ const TestCase* const test_case = impl->current_test_case();
+
+ // Info about the first test in the current test case.
+ const TestInfo* const first_test_info = test_case->test_info_list()[0];
+ const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_;
+ const char* const first_test_name = first_test_info->name();
+
+ // Info about the current test.
+ const TestInfo* const this_test_info = impl->current_test_info();
+ const internal::TypeId this_fixture_id = this_test_info->fixture_class_id_;
+ const char* const this_test_name = this_test_info->name();
+
+ if (this_fixture_id != first_fixture_id) {
+ // Is the first test defined using TEST?
+ const bool first_is_TEST = first_fixture_id == internal::GetTestTypeId();
+ // Is this test defined using TEST?
+ const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId();
+
+ if (first_is_TEST || this_is_TEST) {
+ // The user mixed TEST and TEST_F in this test case - we'll tell
+ // him/her how to fix it.
+
+ // Gets the name of the TEST and the name of the TEST_F. Note
+ // that first_is_TEST and this_is_TEST cannot both be true, as
+ // the fixture IDs are different for the two tests.
+ const char* const TEST_name =
+ first_is_TEST ? first_test_name : this_test_name;
+ const char* const TEST_F_name =
+ first_is_TEST ? this_test_name : first_test_name;
+
+ ADD_FAILURE()
+ << "All tests in the same test case must use the same test fixture\n"
+ << "class, so mixing TEST_F and TEST in the same test case is\n"
+ << "illegal. In test case " << this_test_info->test_case_name()
+ << ",\n"
+ << "test " << TEST_F_name << " is defined using TEST_F but\n"
+ << "test " << TEST_name << " is defined using TEST. You probably\n"
+ << "want to change the TEST to TEST_F or move it to another test\n"
+ << "case.";
+ } else {
+ // The user defined two fixture classes with the same name in
+ // two namespaces - we'll tell him/her how to fix it.
+ ADD_FAILURE()
+ << "All tests in the same test case must use the same test fixture\n"
+ << "class. However, in test case "
+ << this_test_info->test_case_name() << ",\n"
+ << "you defined test " << first_test_name
+ << " and test " << this_test_name << "\n"
+ << "using two different test fixture classes. This can happen if\n"
+ << "the two classes are from different namespaces or translation\n"
+ << "units and have the same name. You should probably rename one\n"
+ << "of the classes to put the tests into different test cases.";
+ }
+ return false;
+ }
+
+ return true;
+}
+
+#if GTEST_HAS_SEH
+
+// Adds an "exception thrown" fatal failure to the current test. This
+// function returns its result via an output parameter pointer because VC++
+// prohibits creation of objects with destructors on stack in functions
+// using __try (see error C2712).
+static std::string* FormatSehExceptionMessage(DWORD exception_code,
+ const char* location) {
+ Message message;
+ message << "SEH exception with code 0x" << std::setbase(16) <<
+ exception_code << std::setbase(10) << " thrown in " << location << ".";
+
+ return new std::string(message.GetString());
+}
+
+#endif // GTEST_HAS_SEH
+
+namespace internal {
+
+#if GTEST_HAS_EXCEPTIONS
+
+// Adds an "exception thrown" fatal failure to the current test.
+static std::string FormatCxxExceptionMessage(const char* description,
+ const char* location) {
+ Message message;
+ if (description != NULL) {
+ message << "C++ exception with description \"" << description << "\"";
+ } else {
+ message << "Unknown C++ exception";
+ }
+ message << " thrown in " << location << ".";
+
+ return message.GetString();
+}
+
+static std::string PrintTestPartResultToString(
+ const TestPartResult& test_part_result);
+
+GoogleTestFailureException::GoogleTestFailureException(
+ const TestPartResult& failure)
+ : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {}
+
+#endif // GTEST_HAS_EXCEPTIONS
+
+// We put these helper functions in the internal namespace as IBM's xlC
+// compiler rejects the code if they were declared static.
+
+// Runs the given method and handles SEH exceptions it throws, when
+// SEH is supported; returns the 0-value for type Result in case of an
+// SEH exception. (Microsoft compilers cannot handle SEH and C++
+// exceptions in the same function. Therefore, we provide a separate
+// wrapper function for handling SEH exceptions.)
+template <class T, typename Result>
+Result HandleSehExceptionsInMethodIfSupported(
+ T* object, Result (T::*method)(), const char* location) {
+#if GTEST_HAS_SEH
+ __try {
+ return (object->*method)();
+ } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT
+ GetExceptionCode())) {
+ // We create the exception message on the heap because VC++ prohibits
+ // creation of objects with destructors on stack in functions using __try
+ // (see error C2712).
+ std::string* exception_message = FormatSehExceptionMessage(
+ GetExceptionCode(), location);
+ internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure,
+ *exception_message);
+ delete exception_message;
+ return static_cast<Result>(0);
+ }
+#else
+ (void)location;
+ return (object->*method)();
+#endif // GTEST_HAS_SEH
+}
+
+// Runs the given method and catches and reports C++ and/or SEH-style
+// exceptions, if they are supported; returns the 0-value for type
+// Result in case of an SEH exception.
+template <class T, typename Result>
+Result HandleExceptionsInMethodIfSupported(
+ T* object, Result (T::*method)(), const char* location) {
+ // NOTE: The user code can affect the way in which Google Test handles
+ // exceptions by setting GTEST_FLAG(catch_exceptions), but only before
+ // RUN_ALL_TESTS() starts. It is technically possible to check the flag
+ // after the exception is caught and either report or re-throw the
+ // exception based on the flag's value:
+ //
+ // try {
+ // // Perform the test method.
+ // } catch (...) {
+ // if (GTEST_FLAG(catch_exceptions))
+ // // Report the exception as failure.
+ // else
+ // throw; // Re-throws the original exception.
+ // }
+ //
+ // However, the purpose of this flag is to allow the program to drop into
+ // the debugger when the exception is thrown. On most platforms, once the
+ // control enters the catch block, the exception origin information is
+ // lost and the debugger will stop the program at the point of the
+ // re-throw in this function -- instead of at the point of the original
+ // throw statement in the code under test. For this reason, we perform
+ // the check early, sacrificing the ability to affect Google Test's
+ // exception handling in the method where the exception is thrown.
+ if (internal::GetUnitTestImpl()->catch_exceptions()) {
+#if GTEST_HAS_EXCEPTIONS
+ try {
+ return HandleSehExceptionsInMethodIfSupported(object, method, location);
+ } catch (const internal::GoogleTestFailureException&) { // NOLINT
+ // This exception type can only be thrown by a failed Google
+ // Test assertion with the intention of letting another testing
+ // framework catch it. Therefore we just re-throw it.
+ throw;
+ } catch (const std::exception& e) { // NOLINT
+ internal::ReportFailureInUnknownLocation(
+ TestPartResult::kFatalFailure,
+ FormatCxxExceptionMessage(e.what(), location));
+ } catch (...) { // NOLINT
+ internal::ReportFailureInUnknownLocation(
+ TestPartResult::kFatalFailure,
+ FormatCxxExceptionMessage(NULL, location));
+ }
+ return static_cast<Result>(0);
+#else
+ return HandleSehExceptionsInMethodIfSupported(object, method, location);
+#endif // GTEST_HAS_EXCEPTIONS
+ } else {
+ return (object->*method)();
+ }
+}
+
+} // namespace internal
+
+// Runs the test and updates the test result.
+void Test::Run() {
+ if (!HasSameFixtureClass()) return;
+
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()");
+ // We will run the test only if SetUp() was successful.
+ if (!HasFatalFailure()) {
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ this, &Test::TestBody, "the test body");
+ }
+
+ // However, we want to clean up as much as possible. Hence we will
+ // always call TearDown(), even if SetUp() or the test body has
+ // failed.
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ this, &Test::TearDown, "TearDown()");
+}
+
+// Returns true iff the current test has a fatal failure.
+bool Test::HasFatalFailure() {
+ return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure();
+}
+
+// Returns true iff the current test has a non-fatal failure.
+bool Test::HasNonfatalFailure() {
+ return internal::GetUnitTestImpl()->current_test_result()->
+ HasNonfatalFailure();
+}
+
+// class TestInfo
+
+// Constructs a TestInfo object. It assumes ownership of the test factory
+// object.
+TestInfo::TestInfo(const std::string& a_test_case_name,
+ const std::string& a_name,
+ const char* a_type_param,
+ const char* a_value_param,
+ internal::TypeId fixture_class_id,
+ internal::TestFactoryBase* factory)
+ : test_case_name_(a_test_case_name),
+ name_(a_name),
+ type_param_(a_type_param ? new std::string(a_type_param) : NULL),
+ value_param_(a_value_param ? new std::string(a_value_param) : NULL),
+ fixture_class_id_(fixture_class_id),
+ should_run_(false),
+ is_disabled_(false),
+ matches_filter_(false),
+ factory_(factory),
+ result_() {}
+
+// Destructs a TestInfo object.
+TestInfo::~TestInfo() { delete factory_; }
+
+namespace internal {
+
+// Creates a new TestInfo object and registers it with Google Test;
+// returns the created object.
+//
+// Arguments:
+//
+// test_case_name: name of the test case
+// name: name of the test
+// type_param: the name of the test's type parameter, or NULL if
+// this is not a typed or a type-parameterized test.
+// value_param: text representation of the test's value parameter,
+// or NULL if this is not a value-parameterized test.
+// fixture_class_id: ID of the test fixture class
+// set_up_tc: pointer to the function that sets up the test case
+// tear_down_tc: pointer to the function that tears down the test case
+// factory: pointer to the factory that creates a test object.
+// The newly created TestInfo instance will assume
+// ownership of the factory object.
+TestInfo* MakeAndRegisterTestInfo(
+ const char* test_case_name,
+ const char* name,
+ const char* type_param,
+ const char* value_param,
+ TypeId fixture_class_id,
+ SetUpTestCaseFunc set_up_tc,
+ TearDownTestCaseFunc tear_down_tc,
+ TestFactoryBase* factory) {
+ TestInfo* const test_info =
+ new TestInfo(test_case_name, name, type_param, value_param,
+ fixture_class_id, factory);
+ GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);
+ return test_info;
+}
+
+#if GTEST_HAS_PARAM_TEST
+void ReportInvalidTestCaseType(const char* test_case_name,
+ const char* file, int line) {
+ Message errors;
+ errors
+ << "Attempted redefinition of test case " << test_case_name << ".\n"
+ << "All tests in the same test case must use the same test fixture\n"
+ << "class. However, in test case " << test_case_name << ", you tried\n"
+ << "to define a test using a fixture class different from the one\n"
+ << "used earlier. This can happen if the two fixture classes are\n"
+ << "from different namespaces and have the same name. You should\n"
+ << "probably rename one of the classes to put the tests into different\n"
+ << "test cases.";
+
+ fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(),
+ errors.GetString().c_str());
+}
+#endif // GTEST_HAS_PARAM_TEST
+
+} // namespace internal
+
+namespace {
+
+// A predicate that checks the test name of a TestInfo against a known
+// value.
+//
+// This is used for implementation of the TestCase class only. We put
+// it in the anonymous namespace to prevent polluting the outer
+// namespace.
+//
+// TestNameIs is copyable.
+class TestNameIs {
+ public:
+ // Constructor.
+ //
+ // TestNameIs has NO default constructor.
+ explicit TestNameIs(const char* name)
+ : name_(name) {}
+
+ // Returns true iff the test name of test_info matches name_.
+ bool operator()(const TestInfo * test_info) const {
+ return test_info && test_info->name() == name_;
+ }
+
+ private:
+ std::string name_;
+};
+
+} // namespace
+
+namespace internal {
+
+// This method expands all parameterized tests registered with macros TEST_P
+// and INSTANTIATE_TEST_CASE_P into regular tests and registers those.
+// This will be done just once during the program runtime.
+void UnitTestImpl::RegisterParameterizedTests() {
+#if GTEST_HAS_PARAM_TEST
+ if (!parameterized_tests_registered_) {
+ parameterized_test_registry_.RegisterTests();
+ parameterized_tests_registered_ = true;
+ }
+#endif
+}
+
+} // namespace internal
+
+// Creates the test object, runs it, records its result, and then
+// deletes it.
+void TestInfo::Run() {
+ if (!should_run_) return;
+
+ // Tells UnitTest where to store test result.
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ impl->set_current_test_info(this);
+
+ TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();
+
+ // Notifies the unit test event listeners that a test is about to start.
+ repeater->OnTestStart(*this);
+
+ const TimeInMillis start = internal::GetTimeInMillis();
+
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+
+ // Creates the test object.
+ Test* const test = internal::HandleExceptionsInMethodIfSupported(
+ factory_, &internal::TestFactoryBase::CreateTest,
+ "the test fixture's constructor");
+
+ // Runs the test only if the test object was created and its
+ // constructor didn't generate a fatal failure.
+ if ((test != NULL) && !Test::HasFatalFailure()) {
+ // This doesn't throw as all user code that can throw are wrapped into
+ // exception handling code.
+ test->Run();
+ }
+
+ // Deletes the test object.
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ test, &Test::DeleteSelf_, "the test fixture's destructor");
+
+ result_.set_elapsed_time(internal::GetTimeInMillis() - start);
+
+ // Notifies the unit test event listener that a test has just finished.
+ repeater->OnTestEnd(*this);
+
+ // Tells UnitTest to stop associating assertion results to this
+ // test.
+ impl->set_current_test_info(NULL);
+}
+
+// class TestCase
+
+// Gets the number of successful tests in this test case.
+int TestCase::successful_test_count() const {
+ return CountIf(test_info_list_, TestPassed);
+}
+
+// Gets the number of failed tests in this test case.
+int TestCase::failed_test_count() const {
+ return CountIf(test_info_list_, TestFailed);
+}
+
+// Gets the number of disabled tests that will be reported in the XML report.
+int TestCase::reportable_disabled_test_count() const {
+ return CountIf(test_info_list_, TestReportableDisabled);
+}
+
+// Gets the number of disabled tests in this test case.
+int TestCase::disabled_test_count() const {
+ return CountIf(test_info_list_, TestDisabled);
+}
+
+// Gets the number of tests to be printed in the XML report.
+int TestCase::reportable_test_count() const {
+ return CountIf(test_info_list_, TestReportable);
+}
+
+// Get the number of tests in this test case that should run.
+int TestCase::test_to_run_count() const {
+ return CountIf(test_info_list_, ShouldRunTest);
+}
+
+// Gets the number of all tests.
+int TestCase::total_test_count() const {
+ return static_cast<int>(test_info_list_.size());
+}
+
+// Creates a TestCase with the given name.
+//
+// Arguments:
+//
+// name: name of the test case
+// a_type_param: the name of the test case's type parameter, or NULL if
+// this is not a typed or a type-parameterized test case.
+// set_up_tc: pointer to the function that sets up the test case
+// tear_down_tc: pointer to the function that tears down the test case
+TestCase::TestCase(const char* a_name, const char* a_type_param,
+ Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc)
+ : name_(a_name),
+ type_param_(a_type_param ? new std::string(a_type_param) : NULL),
+ set_up_tc_(set_up_tc),
+ tear_down_tc_(tear_down_tc),
+ should_run_(false),
+ elapsed_time_(0) {
+}
+
+// Destructor of TestCase.
+TestCase::~TestCase() {
+ // Deletes every Test in the collection.
+ ForEach(test_info_list_, internal::Delete<TestInfo>);
+}
+
+// Returns the i-th test among all the tests. i can range from 0 to
+// total_test_count() - 1. If i is not in that range, returns NULL.
+const TestInfo* TestCase::GetTestInfo(int i) const {
+ const int index = GetElementOr(test_indices_, i, -1);
+ return index < 0 ? NULL : test_info_list_[index];
+}
+
+// Returns the i-th test among all the tests. i can range from 0 to
+// total_test_count() - 1. If i is not in that range, returns NULL.
+TestInfo* TestCase::GetMutableTestInfo(int i) {
+ const int index = GetElementOr(test_indices_, i, -1);
+ return index < 0 ? NULL : test_info_list_[index];
+}
+
+// Adds a test to this test case. Will delete the test upon
+// destruction of the TestCase object.
+void TestCase::AddTestInfo(TestInfo * test_info) {
+ test_info_list_.push_back(test_info);
+ test_indices_.push_back(static_cast<int>(test_indices_.size()));
+}
+
+// Runs every test in this TestCase.
+void TestCase::Run() {
+ if (!should_run_) return;
+
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ impl->set_current_test_case(this);
+
+ TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();
+
+ repeater->OnTestCaseStart(*this);
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ this, &TestCase::RunSetUpTestCase, "SetUpTestCase()");
+
+ const internal::TimeInMillis start = internal::GetTimeInMillis();
+ for (int i = 0; i < total_test_count(); i++) {
+ GetMutableTestInfo(i)->Run();
+ }
+ elapsed_time_ = internal::GetTimeInMillis() - start;
+
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ this, &TestCase::RunTearDownTestCase, "TearDownTestCase()");
+
+ repeater->OnTestCaseEnd(*this);
+ impl->set_current_test_case(NULL);
+}
+
+// Clears the results of all tests in this test case.
+void TestCase::ClearResult() {
+ ad_hoc_test_result_.Clear();
+ ForEach(test_info_list_, TestInfo::ClearTestResult);
+}
+
+// Shuffles the tests in this test case.
+void TestCase::ShuffleTests(internal::Random* random) {
+ Shuffle(random, &test_indices_);
+}
+
+// Restores the test order to before the first shuffle.
+void TestCase::UnshuffleTests() {
+ for (size_t i = 0; i < test_indices_.size(); i++) {
+ test_indices_[i] = static_cast<int>(i);
+ }
+}
+
+// Formats a countable noun. Depending on its quantity, either the
+// singular form or the plural form is used. e.g.
+//
+// FormatCountableNoun(1, "formula", "formuli") returns "1 formula".
+// FormatCountableNoun(5, "book", "books") returns "5 books".
+static std::string FormatCountableNoun(int count,
+ const char * singular_form,
+ const char * plural_form) {
+ return internal::StreamableToString(count) + " " +
+ (count == 1 ? singular_form : plural_form);
+}
+
+// Formats the count of tests.
+static std::string FormatTestCount(int test_count) {
+ return FormatCountableNoun(test_count, "test", "tests");
+}
+
+// Formats the count of test cases.
+static std::string FormatTestCaseCount(int test_case_count) {
+ return FormatCountableNoun(test_case_count, "test case", "test cases");
+}
+
+// Converts a TestPartResult::Type enum to human-friendly string
+// representation. Both kNonFatalFailure and kFatalFailure are translated
+// to "Failure", as the user usually doesn't care about the difference
+// between the two when viewing the test result.
+static const char * TestPartResultTypeToString(TestPartResult::Type type) {
+ switch (type) {
+ case TestPartResult::kSuccess:
+ return "Success";
+
+ case TestPartResult::kNonFatalFailure:
+ case TestPartResult::kFatalFailure:
+#ifdef _MSC_VER
+ return "error: ";
+#else
+ return "Failure\n";
+#endif
+ default:
+ return "Unknown result type";
+ }
+}
+
+namespace internal {
+
+// Prints a TestPartResult to an std::string.
+static std::string PrintTestPartResultToString(
+ const TestPartResult& test_part_result) {
+ return (Message()
+ << internal::FormatFileLocation(test_part_result.file_name(),
+ test_part_result.line_number())
+ << " " << TestPartResultTypeToString(test_part_result.type())
+ << test_part_result.message()).GetString();
+}
+
+// Prints a TestPartResult.
+static void PrintTestPartResult(const TestPartResult& test_part_result) {
+ const std::string& result =
+ PrintTestPartResultToString(test_part_result);
+ printf("%s\n", result.c_str());
+ fflush(stdout);
+ // If the test program runs in Visual Studio or a debugger, the
+ // following statements add the test part result message to the Output
+ // window such that the user can double-click on it to jump to the
+ // corresponding source code location; otherwise they do nothing.
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+ // We don't call OutputDebugString*() on Windows Mobile, as printing
+ // to stdout is done by OutputDebugString() there already - we don't
+ // want the same message printed twice.
+ ::OutputDebugStringA(result.c_str());
+ ::OutputDebugStringA("\n");
+#endif
+}
+
+// class PrettyUnitTestResultPrinter
+
+enum GTestColor {
+ COLOR_DEFAULT,
+ COLOR_RED,
+ COLOR_GREEN,
+ COLOR_YELLOW
+};
+
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+
+// Returns the character attribute for the given color.
+WORD GetColorAttribute(GTestColor color) {
+ switch (color) {
+ case COLOR_RED: return FOREGROUND_RED;
+ case COLOR_GREEN: return FOREGROUND_GREEN;
+ case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN;
+ default: return 0;
+ }
+}
+
+#else
+
+// Returns the ANSI color code for the given color. COLOR_DEFAULT is
+// an invalid input.
+const char* GetAnsiColorCode(GTestColor color) {
+ switch (color) {
+ case COLOR_RED: return "1";
+ case COLOR_GREEN: return "2";
+ case COLOR_YELLOW: return "3";
+ default: return NULL;
+ };
+}
+
+#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+
+// Returns true iff Google Test should use colors in the output.
+bool ShouldUseColor(bool stdout_is_tty) {
+ const char* const gtest_color = GTEST_FLAG(color).c_str();
+
+ if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) {
+#if GTEST_OS_WINDOWS
+ // On Windows the TERM variable is usually not set, but the
+ // console there does support colors.
+ return stdout_is_tty;
+#else
+ // On non-Windows platforms, we rely on the TERM variable.
+ const char* const term = posix::GetEnv("TERM");
+ const bool term_supports_color =
+ String::CStringEquals(term, "xterm") ||
+ String::CStringEquals(term, "xterm-color") ||
+ String::CStringEquals(term, "xterm-256color") ||
+ String::CStringEquals(term, "screen") ||
+ String::CStringEquals(term, "screen-256color") ||
+ String::CStringEquals(term, "linux") ||
+ String::CStringEquals(term, "cygwin");
+ return stdout_is_tty && term_supports_color;
+#endif // GTEST_OS_WINDOWS
+ }
+
+ return String::CaseInsensitiveCStringEquals(gtest_color, "yes") ||
+ String::CaseInsensitiveCStringEquals(gtest_color, "true") ||
+ String::CaseInsensitiveCStringEquals(gtest_color, "t") ||
+ String::CStringEquals(gtest_color, "1");
+ // We take "yes", "true", "t", and "1" as meaning "yes". If the
+ // value is neither one of these nor "auto", we treat it as "no" to
+ // be conservative.
+}
+
+// Helpers for printing colored strings to stdout. Note that on Windows, we
+// cannot simply emit special characters and have the terminal change colors.
+// This routine must actually emit the characters rather than return a string
+// that would be colored when printed, as can be done on Linux.
+void ColoredPrintf(GTestColor color, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS || GTEST_OS_IOS
+ const bool use_color = false;
+#else
+ static const bool in_color_mode =
+ ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0);
+ const bool use_color = in_color_mode && (color != COLOR_DEFAULT);
+#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS
+ // The '!= 0' comparison is necessary to satisfy MSVC 7.1.
+
+ if (!use_color) {
+ vprintf(fmt, args);
+ va_end(args);
+ return;
+ }
+
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+ const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ // Gets the current text color.
+ CONSOLE_SCREEN_BUFFER_INFO buffer_info;
+ GetConsoleScreenBufferInfo(stdout_handle, &buffer_info);
+ const WORD old_color_attrs = buffer_info.wAttributes;
+
+ // We need to flush the stream buffers into the console before each
+ // SetConsoleTextAttribute call lest it affect the text that is already
+ // printed but has not yet reached the console.
+ fflush(stdout);
+ SetConsoleTextAttribute(stdout_handle,
+ GetColorAttribute(color) | FOREGROUND_INTENSITY);
+ vprintf(fmt, args);
+
+ fflush(stdout);
+ // Restores the text color.
+ SetConsoleTextAttribute(stdout_handle, old_color_attrs);
+#else
+ printf("\033[0;3%sm", GetAnsiColorCode(color));
+ vprintf(fmt, args);
+ printf("\033[m"); // Resets the terminal to default.
+#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+ va_end(args);
+}
+
+// Text printed in Google Test's text output and --gunit_list_tests
+// output to label the type parameter and value parameter for a test.
+static const char kTypeParamLabel[] = "TypeParam";
+static const char kValueParamLabel[] = "GetParam()";
+
+void PrintFullTestCommentIfPresent(const TestInfo& test_info) {
+ const char* const type_param = test_info.type_param();
+ const char* const value_param = test_info.value_param();
+
+ if (type_param != NULL || value_param != NULL) {
+ printf(", where ");
+ if (type_param != NULL) {
+ printf("%s = %s", kTypeParamLabel, type_param);
+ if (value_param != NULL)
+ printf(" and ");
+ }
+ if (value_param != NULL) {
+ printf("%s = %s", kValueParamLabel, value_param);
+ }
+ }
+}
+
+// This class implements the TestEventListener interface.
+//
+// Class PrettyUnitTestResultPrinter is copyable.
+class PrettyUnitTestResultPrinter : public TestEventListener {
+ public:
+ PrettyUnitTestResultPrinter() {}
+ static void PrintTestName(const char * test_case, const char * test) {
+ printf("%s.%s", test_case, test);
+ }
+
+ // The following methods override what's in the TestEventListener class.
+ virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration);
+ virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test);
+ virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestCaseStart(const TestCase& test_case);
+ virtual void OnTestStart(const TestInfo& test_info);
+ virtual void OnTestPartResult(const TestPartResult& result);
+ virtual void OnTestEnd(const TestInfo& test_info);
+ virtual void OnTestCaseEnd(const TestCase& test_case);
+ virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test);
+ virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
+ virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {}
+
+ private:
+ static void PrintFailedTests(const UnitTest& unit_test);
+};
+
+ // Fired before each iteration of tests starts.
+void PrettyUnitTestResultPrinter::OnTestIterationStart(
+ const UnitTest& unit_test, int iteration) {
+ if (GTEST_FLAG(repeat) != 1)
+ printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1);
+
+ const char* const filter = GTEST_FLAG(filter).c_str();
+
+ // Prints the filter if it's not *. This reminds the user that some
+ // tests may be skipped.
+ if (!String::CStringEquals(filter, kUniversalFilter)) {
+ ColoredPrintf(COLOR_YELLOW,
+ "Note: %s filter = %s\n", GTEST_NAME_, filter);
+ }
+
+ if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) {
+ const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1);
+ ColoredPrintf(COLOR_YELLOW,
+ "Note: This is test shard %d of %s.\n",
+ static_cast<int>(shard_index) + 1,
+ internal::posix::GetEnv(kTestTotalShards));
+ }
+
+ if (GTEST_FLAG(shuffle)) {
+ ColoredPrintf(COLOR_YELLOW,
+ "Note: Randomizing tests' orders with a seed of %d .\n",
+ unit_test.random_seed());
+ }
+
+ ColoredPrintf(COLOR_GREEN, "[==========] ");
+ printf("Running %s from %s.\n",
+ FormatTestCount(unit_test.test_to_run_count()).c_str(),
+ FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str());
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart(
+ const UnitTest& /*unit_test*/) {
+ ColoredPrintf(COLOR_GREEN, "[----------] ");
+ printf("Global test environment set-up.\n");
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) {
+ const std::string counts =
+ FormatCountableNoun(test_case.test_to_run_count(), "test", "tests");
+ ColoredPrintf(COLOR_GREEN, "[----------] ");
+ printf("%s from %s", counts.c_str(), test_case.name());
+ if (test_case.type_param() == NULL) {
+ printf("\n");
+ } else {
+ printf(", where %s = %s\n", kTypeParamLabel, test_case.type_param());
+ }
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) {
+ ColoredPrintf(COLOR_GREEN, "[ RUN ] ");
+ PrintTestName(test_info.test_case_name(), test_info.name());
+ printf("\n");
+ fflush(stdout);
+}
+
+// Called after an assertion failure.
+void PrettyUnitTestResultPrinter::OnTestPartResult(
+ const TestPartResult& result) {
+ // If the test part succeeded, we don't need to do anything.
+ if (result.type() == TestPartResult::kSuccess)
+ return;
+
+ // Print failure message from the assertion (e.g. expected this and got that).
+ PrintTestPartResult(result);
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) {
+ if (test_info.result()->Passed()) {
+ ColoredPrintf(COLOR_GREEN, "[ OK ] ");
+ } else {
+ ColoredPrintf(COLOR_RED, "[ FAILED ] ");
+ }
+ PrintTestName(test_info.test_case_name(), test_info.name());
+ if (test_info.result()->Failed())
+ PrintFullTestCommentIfPresent(test_info);
+
+ if (GTEST_FLAG(print_time)) {
+ printf(" (%s ms)\n", internal::StreamableToString(
+ test_info.result()->elapsed_time()).c_str());
+ } else {
+ printf("\n");
+ }
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) {
+ if (!GTEST_FLAG(print_time)) return;
+
+ const std::string counts =
+ FormatCountableNoun(test_case.test_to_run_count(), "test", "tests");
+ ColoredPrintf(COLOR_GREEN, "[----------] ");
+ printf("%s from %s (%s ms total)\n\n",
+ counts.c_str(), test_case.name(),
+ internal::StreamableToString(test_case.elapsed_time()).c_str());
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart(
+ const UnitTest& /*unit_test*/) {
+ ColoredPrintf(COLOR_GREEN, "[----------] ");
+ printf("Global test environment tear-down\n");
+ fflush(stdout);
+}
+
+// Internal helper for printing the list of failed tests.
+void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) {
+ const int failed_test_count = unit_test.failed_test_count();
+ if (failed_test_count == 0) {
+ return;
+ }
+
+ for (int i = 0; i < unit_test.total_test_case_count(); ++i) {
+ const TestCase& test_case = *unit_test.GetTestCase(i);
+ if (!test_case.should_run() || (test_case.failed_test_count() == 0)) {
+ continue;
+ }
+ for (int j = 0; j < test_case.total_test_count(); ++j) {
+ const TestInfo& test_info = *test_case.GetTestInfo(j);
+ if (!test_info.should_run() || test_info.result()->Passed()) {
+ continue;
+ }
+ ColoredPrintf(COLOR_RED, "[ FAILED ] ");
+ printf("%s.%s", test_case.name(), test_info.name());
+ PrintFullTestCommentIfPresent(test_info);
+ printf("\n");
+ }
+ }
+}
+
+void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
+ int /*iteration*/) {
+ ColoredPrintf(COLOR_GREEN, "[==========] ");
+ printf("%s from %s ran.",
+ FormatTestCount(unit_test.test_to_run_count()).c_str(),
+ FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str());
+ if (GTEST_FLAG(print_time)) {
+ printf(" (%s ms total)",
+ internal::StreamableToString(unit_test.elapsed_time()).c_str());
+ }
+ printf("\n");
+ ColoredPrintf(COLOR_GREEN, "[ PASSED ] ");
+ printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str());
+
+ int num_failures = unit_test.failed_test_count();
+ if (!unit_test.Passed()) {
+ const int failed_test_count = unit_test.failed_test_count();
+ ColoredPrintf(COLOR_RED, "[ FAILED ] ");
+ printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str());
+ PrintFailedTests(unit_test);
+ printf("\n%2d FAILED %s\n", num_failures,
+ num_failures == 1 ? "TEST" : "TESTS");
+ }
+
+ int num_disabled = unit_test.reportable_disabled_test_count();
+ if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) {
+ if (!num_failures) {
+ printf("\n"); // Add a spacer if no FAILURE banner is displayed.
+ }
+ ColoredPrintf(COLOR_YELLOW,
+ " YOU HAVE %d DISABLED %s\n\n",
+ num_disabled,
+ num_disabled == 1 ? "TEST" : "TESTS");
+ }
+ // Ensure that Google Test output is printed before, e.g., heapchecker output.
+ fflush(stdout);
+}
+
+// End PrettyUnitTestResultPrinter
+
+// class TestEventRepeater
+//
+// This class forwards events to other event listeners.
+class TestEventRepeater : public TestEventListener {
+ public:
+ TestEventRepeater() : forwarding_enabled_(true) {}
+ virtual ~TestEventRepeater();
+ void Append(TestEventListener *listener);
+ TestEventListener* Release(TestEventListener* listener);
+
+ // Controls whether events will be forwarded to listeners_. Set to false
+ // in death test child processes.
+ bool forwarding_enabled() const { return forwarding_enabled_; }
+ void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; }
+
+ virtual void OnTestProgramStart(const UnitTest& unit_test);
+ virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration);
+ virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test);
+ virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test);
+ virtual void OnTestCaseStart(const TestCase& test_case);
+ virtual void OnTestStart(const TestInfo& test_info);
+ virtual void OnTestPartResult(const TestPartResult& result);
+ virtual void OnTestEnd(const TestInfo& test_info);
+ virtual void OnTestCaseEnd(const TestCase& test_case);
+ virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test);
+ virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test);
+ virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
+ virtual void OnTestProgramEnd(const UnitTest& unit_test);
+
+ private:
+ // Controls whether events will be forwarded to listeners_. Set to false
+ // in death test child processes.
+ bool forwarding_enabled_;
+ // The list of listeners that receive events.
+ std::vector<TestEventListener*> listeners_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater);
+};
+
+TestEventRepeater::~TestEventRepeater() {
+ ForEach(listeners_, Delete<TestEventListener>);
+}
+
+void TestEventRepeater::Append(TestEventListener *listener) {
+ listeners_.push_back(listener);
+}
+
+// TODO(vladl at google.com): Factor the search functionality into Vector::Find.
+TestEventListener* TestEventRepeater::Release(TestEventListener *listener) {
+ for (size_t i = 0; i < listeners_.size(); ++i) {
+ if (listeners_[i] == listener) {
+ listeners_.erase(listeners_.begin() + i);
+ return listener;
+ }
+ }
+
+ return NULL;
+}
+
+// Since most methods are very similar, use macros to reduce boilerplate.
+// This defines a member that forwards the call to all listeners.
+#define GTEST_REPEATER_METHOD_(Name, Type) \
+void TestEventRepeater::Name(const Type& parameter) { \
+ if (forwarding_enabled_) { \
+ for (size_t i = 0; i < listeners_.size(); i++) { \
+ listeners_[i]->Name(parameter); \
+ } \
+ } \
+}
+// This defines a member that forwards the call to all listeners in reverse
+// order.
+#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \
+void TestEventRepeater::Name(const Type& parameter) { \
+ if (forwarding_enabled_) { \
+ for (int i = static_cast<int>(listeners_.size()) - 1; i >= 0; i--) { \
+ listeners_[i]->Name(parameter); \
+ } \
+ } \
+}
+
+GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest)
+GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest)
+GTEST_REPEATER_METHOD_(OnTestCaseStart, TestCase)
+GTEST_REPEATER_METHOD_(OnTestStart, TestInfo)
+GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult)
+GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest)
+GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest)
+GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest)
+GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo)
+GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestCase)
+GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest)
+
+#undef GTEST_REPEATER_METHOD_
+#undef GTEST_REVERSE_REPEATER_METHOD_
+
+void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test,
+ int iteration) {
+ if (forwarding_enabled_) {
+ for (size_t i = 0; i < listeners_.size(); i++) {
+ listeners_[i]->OnTestIterationStart(unit_test, iteration);
+ }
+ }
+}
+
+void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test,
+ int iteration) {
+ if (forwarding_enabled_) {
+ for (int i = static_cast<int>(listeners_.size()) - 1; i >= 0; i--) {
+ listeners_[i]->OnTestIterationEnd(unit_test, iteration);
+ }
+ }
+}
+
+// End TestEventRepeater
+
+// This class generates an XML output file.
+class XmlUnitTestResultPrinter : public EmptyTestEventListener {
+ public:
+ explicit XmlUnitTestResultPrinter(const char* output_file);
+
+ virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
+
+ private:
+ // Is c a whitespace character that is normalized to a space character
+ // when it appears in an XML attribute value?
+ static bool IsNormalizableWhitespace(char c) {
+ return c == 0x9 || c == 0xA || c == 0xD;
+ }
+
+ // May c appear in a well-formed XML document?
+ static bool IsValidXmlCharacter(char c) {
+ return IsNormalizableWhitespace(c) || c >= 0x20;
+ }
+
+ // Returns an XML-escaped copy of the input string str. If
+ // is_attribute is true, the text is meant to appear as an attribute
+ // value, and normalizable whitespace is preserved by replacing it
+ // with character references.
+ static std::string EscapeXml(const std::string& str, bool is_attribute);
+
+ // Returns the given string with all characters invalid in XML removed.
+ static std::string RemoveInvalidXmlCharacters(const std::string& str);
+
+ // Convenience wrapper around EscapeXml when str is an attribute value.
+ static std::string EscapeXmlAttribute(const std::string& str) {
+ return EscapeXml(str, true);
+ }
+
+ // Convenience wrapper around EscapeXml when str is not an attribute value.
+ static std::string EscapeXmlText(const char* str) {
+ return EscapeXml(str, false);
+ }
+
+ // Verifies that the given attribute belongs to the given element and
+ // streams the attribute as XML.
+ static void OutputXmlAttribute(std::ostream* stream,
+ const std::string& element_name,
+ const std::string& name,
+ const std::string& value);
+
+ // Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
+ static void OutputXmlCDataSection(::std::ostream* stream, const char* data);
+
+ // Streams an XML representation of a TestInfo object.
+ static void OutputXmlTestInfo(::std::ostream* stream,
+ const char* test_case_name,
+ const TestInfo& test_info);
+
+ // Prints an XML representation of a TestCase object
+ static void PrintXmlTestCase(::std::ostream* stream,
+ const TestCase& test_case);
+
+ // Prints an XML summary of unit_test to output stream out.
+ static void PrintXmlUnitTest(::std::ostream* stream,
+ const UnitTest& unit_test);
+
+ // Produces a string representing the test properties in a result as space
+ // delimited XML attributes based on the property key="value" pairs.
+ // When the std::string is not empty, it includes a space at the beginning,
+ // to delimit this attribute from prior attributes.
+ static std::string TestPropertiesAsXmlAttributes(const TestResult& result);
+
+ // The output file.
+ const std::string output_file_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter);
+};
+
+// Creates a new XmlUnitTestResultPrinter.
+XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file)
+ : output_file_(output_file) {
+ if (output_file_.c_str() == NULL || output_file_.empty()) {
+ fprintf(stderr, "XML output file may not be null\n");
+ fflush(stderr);
+ exit(EXIT_FAILURE);
+ }
+}
+
+// Called after the unit test ends.
+void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
+ int /*iteration*/) {
+ FILE* xmlout = NULL;
+ FilePath output_file(output_file_);
+ FilePath output_dir(output_file.RemoveFileName());
+
+ if (output_dir.CreateDirectoriesRecursively()) {
+ xmlout = posix::FOpen(output_file_.c_str(), "w");
+ }
+ if (xmlout == NULL) {
+ // TODO(wan): report the reason of the failure.
+ //
+ // We don't do it for now as:
+ //
+ // 1. There is no urgent need for it.
+ // 2. It's a bit involved to make the errno variable thread-safe on
+ // all three operating systems (Linux, Windows, and Mac OS).
+ // 3. To interpret the meaning of errno in a thread-safe way,
+ // we need the strerror_r() function, which is not available on
+ // Windows.
+ fprintf(stderr,
+ "Unable to open file \"%s\"\n",
+ output_file_.c_str());
+ fflush(stderr);
+ exit(EXIT_FAILURE);
+ }
+ std::stringstream stream;
+ PrintXmlUnitTest(&stream, unit_test);
+ fprintf(xmlout, "%s", StringStreamToString(&stream).c_str());
+ fclose(xmlout);
+}
+
+// Returns an XML-escaped copy of the input string str. If is_attribute
+// is true, the text is meant to appear as an attribute value, and
+// normalizable whitespace is preserved by replacing it with character
+// references.
+//
+// Invalid XML characters in str, if any, are stripped from the output.
+// It is expected that most, if not all, of the text processed by this
+// module will consist of ordinary English text.
+// If this module is ever modified to produce version 1.1 XML output,
+// most invalid characters can be retained using character references.
+// TODO(wan): It might be nice to have a minimally invasive, human-readable
+// escaping scheme for invalid characters, rather than dropping them.
+std::string XmlUnitTestResultPrinter::EscapeXml(
+ const std::string& str, bool is_attribute) {
+ Message m;
+
+ for (size_t i = 0; i < str.size(); ++i) {
+ const char ch = str[i];
+ switch (ch) {
+ case '<':
+ m << "<";
+ break;
+ case '>':
+ m << ">";
+ break;
+ case '&':
+ m << "&";
+ break;
+ case '\'':
+ if (is_attribute)
+ m << "'";
+ else
+ m << '\'';
+ break;
+ case '"':
+ if (is_attribute)
+ m << """;
+ else
+ m << '"';
+ break;
+ default:
+ if (IsValidXmlCharacter(ch)) {
+ if (is_attribute && IsNormalizableWhitespace(ch))
+ m << "&#x" << String::FormatByte(static_cast<unsigned char>(ch))
+ << ";";
+ else
+ m << ch;
+ }
+ break;
+ }
+ }
+
+ return m.GetString();
+}
+
+// Returns the given string with all characters invalid in XML removed.
+// Currently invalid characters are dropped from the string. An
+// alternative is to replace them with certain characters such as . or ?.
+std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(
+ const std::string& str) {
+ std::string output;
+ output.reserve(str.size());
+ for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
+ if (IsValidXmlCharacter(*it))
+ output.push_back(*it);
+
+ return output;
+}
+
+// The following routines generate an XML representation of a UnitTest
+// object.
+//
+// This is how Google Test concepts map to the DTD:
+//
+// <testsuites name="AllTests"> <-- corresponds to a UnitTest object
+// <testsuite name="testcase-name"> <-- corresponds to a TestCase object
+// <testcase name="test-name"> <-- corresponds to a TestInfo object
+// <failure message="...">...</failure>
+// <failure message="...">...</failure>
+// <failure message="...">...</failure>
+// <-- individual assertion failures
+// </testcase>
+// </testsuite>
+// </testsuites>
+
+// Formats the given time in milliseconds as seconds.
+std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) {
+ ::std::stringstream ss;
+ ss << ms/1000.0;
+ return ss.str();
+}
+
+// Converts the given epoch time in milliseconds to a date string in the ISO
+// 8601 format, without the timezone information.
+std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) {
+ // Using non-reentrant version as localtime_r is not portable.
+ time_t seconds = static_cast<time_t>(ms / 1000);
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4996) // Temporarily disables warning 4996
+ // (function or variable may be unsafe).
+ const struct tm* const time_struct = localtime(&seconds); // NOLINT
+# pragma warning(pop) // Restores the warning state again.
+#else
+ const struct tm* const time_struct = localtime(&seconds); // NOLINT
+#endif
+ if (time_struct == NULL)
+ return ""; // Invalid ms value
+
+ // YYYY-MM-DDThh:mm:ss
+ return StreamableToString(time_struct->tm_year + 1900) + "-" +
+ String::FormatIntWidth2(time_struct->tm_mon + 1) + "-" +
+ String::FormatIntWidth2(time_struct->tm_mday) + "T" +
+ String::FormatIntWidth2(time_struct->tm_hour) + ":" +
+ String::FormatIntWidth2(time_struct->tm_min) + ":" +
+ String::FormatIntWidth2(time_struct->tm_sec);
+}
+
+// Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
+void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream,
+ const char* data) {
+ const char* segment = data;
+ *stream << "<![CDATA[";
+ for (;;) {
+ const char* const next_segment = strstr(segment, "]]>");
+ if (next_segment != NULL) {
+ stream->write(
+ segment, static_cast<std::streamsize>(next_segment - segment));
+ *stream << "]]>]]><![CDATA[";
+ segment = next_segment + strlen("]]>");
+ } else {
+ *stream << segment;
+ break;
+ }
+ }
+ *stream << "]]>";
+}
+
+void XmlUnitTestResultPrinter::OutputXmlAttribute(
+ std::ostream* stream,
+ const std::string& element_name,
+ const std::string& name,
+ const std::string& value) {
+ const std::vector<std::string>& allowed_names =
+ GetReservedAttributesForElement(element_name);
+
+ GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) !=
+ allowed_names.end())
+ << "Attribute " << name << " is not allowed for element <" << element_name
+ << ">.";
+
+ *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\"";
+}
+
+// Prints an XML representation of a TestInfo object.
+// TODO(wan): There is also value in printing properties with the plain printer.
+void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream,
+ const char* test_case_name,
+ const TestInfo& test_info) {
+ const TestResult& result = *test_info.result();
+ const std::string kTestcase = "testcase";
+
+ *stream << " <testcase";
+ OutputXmlAttribute(stream, kTestcase, "name", test_info.name());
+
+ if (test_info.value_param() != NULL) {
+ OutputXmlAttribute(stream, kTestcase, "value_param",
+ test_info.value_param());
+ }
+ if (test_info.type_param() != NULL) {
+ OutputXmlAttribute(stream, kTestcase, "type_param", test_info.type_param());
+ }
+
+ OutputXmlAttribute(stream, kTestcase, "status",
+ test_info.should_run() ? "run" : "notrun");
+ OutputXmlAttribute(stream, kTestcase, "time",
+ FormatTimeInMillisAsSeconds(result.elapsed_time()));
+ OutputXmlAttribute(stream, kTestcase, "classname", test_case_name);
+ *stream << TestPropertiesAsXmlAttributes(result);
+
+ int failures = 0;
+ for (int i = 0; i < result.total_part_count(); ++i) {
+ const TestPartResult& part = result.GetTestPartResult(i);
+ if (part.failed()) {
+ if (++failures == 1) {
+ *stream << ">\n";
+ }
+ const string location = internal::FormatCompilerIndependentFileLocation(
+ part.file_name(), part.line_number());
+ const string summary = location + "\n" + part.summary();
+ *stream << " <failure message=\""
+ << EscapeXmlAttribute(summary.c_str())
+ << "\" type=\"\">";
+ const string detail = location + "\n" + part.message();
+ OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str());
+ *stream << "</failure>\n";
+ }
+ }
+
+ if (failures == 0)
+ *stream << " />\n";
+ else
+ *stream << " </testcase>\n";
+}
+
+// Prints an XML representation of a TestCase object
+void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream,
+ const TestCase& test_case) {
+ const std::string kTestsuite = "testsuite";
+ *stream << " <" << kTestsuite;
+ OutputXmlAttribute(stream, kTestsuite, "name", test_case.name());
+ OutputXmlAttribute(stream, kTestsuite, "tests",
+ StreamableToString(test_case.reportable_test_count()));
+ OutputXmlAttribute(stream, kTestsuite, "failures",
+ StreamableToString(test_case.failed_test_count()));
+ OutputXmlAttribute(
+ stream, kTestsuite, "disabled",
+ StreamableToString(test_case.reportable_disabled_test_count()));
+ OutputXmlAttribute(stream, kTestsuite, "errors", "0");
+ OutputXmlAttribute(stream, kTestsuite, "time",
+ FormatTimeInMillisAsSeconds(test_case.elapsed_time()));
+ *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result())
+ << ">\n";
+
+ for (int i = 0; i < test_case.total_test_count(); ++i) {
+ if (test_case.GetTestInfo(i)->is_reportable())
+ OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i));
+ }
+ *stream << " </" << kTestsuite << ">\n";
+}
+
+// Prints an XML summary of unit_test to output stream out.
+void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream,
+ const UnitTest& unit_test) {
+ const std::string kTestsuites = "testsuites";
+
+ *stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ *stream << "<" << kTestsuites;
+
+ OutputXmlAttribute(stream, kTestsuites, "tests",
+ StreamableToString(unit_test.reportable_test_count()));
+ OutputXmlAttribute(stream, kTestsuites, "failures",
+ StreamableToString(unit_test.failed_test_count()));
+ OutputXmlAttribute(
+ stream, kTestsuites, "disabled",
+ StreamableToString(unit_test.reportable_disabled_test_count()));
+ OutputXmlAttribute(stream, kTestsuites, "errors", "0");
+ OutputXmlAttribute(
+ stream, kTestsuites, "timestamp",
+ FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp()));
+ OutputXmlAttribute(stream, kTestsuites, "time",
+ FormatTimeInMillisAsSeconds(unit_test.elapsed_time()));
+
+ if (GTEST_FLAG(shuffle)) {
+ OutputXmlAttribute(stream, kTestsuites, "random_seed",
+ StreamableToString(unit_test.random_seed()));
+ }
+
+ *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result());
+
+ OutputXmlAttribute(stream, kTestsuites, "name", "AllTests");
+ *stream << ">\n";
+
+ for (int i = 0; i < unit_test.total_test_case_count(); ++i) {
+ if (unit_test.GetTestCase(i)->reportable_test_count() > 0)
+ PrintXmlTestCase(stream, *unit_test.GetTestCase(i));
+ }
+ *stream << "</" << kTestsuites << ">\n";
+}
+
+// Produces a string representing the test properties in a result as space
+// delimited XML attributes based on the property key="value" pairs.
+std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes(
+ const TestResult& result) {
+ Message attributes;
+ for (int i = 0; i < result.test_property_count(); ++i) {
+ const TestProperty& property = result.GetTestProperty(i);
+ attributes << " " << property.key() << "="
+ << "\"" << EscapeXmlAttribute(property.value()) << "\"";
+ }
+ return attributes.GetString();
+}
+
+// End XmlUnitTestResultPrinter
+
+#if GTEST_CAN_STREAM_RESULTS_
+
+// Checks if str contains '=', '&', '%' or '\n' characters. If yes,
+// replaces them by "%xx" where xx is their hexadecimal value. For
+// example, replaces "=" with "%3D". This algorithm is O(strlen(str))
+// in both time and space -- important as the input str may contain an
+// arbitrarily long test failure message and stack trace.
+string StreamingListener::UrlEncode(const char* str) {
+ string result;
+ result.reserve(strlen(str) + 1);
+ for (char ch = *str; ch != '\0'; ch = *++str) {
+ switch (ch) {
+ case '%':
+ case '=':
+ case '&':
+ case '\n':
+ result.append("%" + String::FormatByte(static_cast<unsigned char>(ch)));
+ break;
+ default:
+ result.push_back(ch);
+ break;
+ }
+ }
+ return result;
+}
+
+void StreamingListener::SocketWriter::MakeConnection() {
+ GTEST_CHECK_(sockfd_ == -1)
+ << "MakeConnection() can't be called when there is already a connection.";
+
+ addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses.
+ hints.ai_socktype = SOCK_STREAM;
+ addrinfo* servinfo = NULL;
+
+ // Use the getaddrinfo() to get a linked list of IP addresses for
+ // the given host name.
+ const int error_num = getaddrinfo(
+ host_name_.c_str(), port_num_.c_str(), &hints, &servinfo);
+ if (error_num != 0) {
+ GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: "
+ << gai_strerror(error_num);
+ }
+
+ // Loop through all the results and connect to the first we can.
+ for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL;
+ cur_addr = cur_addr->ai_next) {
+ sockfd_ = socket(
+ cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol);
+ if (sockfd_ != -1) {
+ // Connect the client socket to the server socket.
+ if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) {
+ close(sockfd_);
+ sockfd_ = -1;
+ }
+ }
+ }
+
+ freeaddrinfo(servinfo); // all done with this structure
+
+ if (sockfd_ == -1) {
+ GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to "
+ << host_name_ << ":" << port_num_;
+ }
+}
+
+// End of class Streaming Listener
+#endif // GTEST_CAN_STREAM_RESULTS__
+
+// Class ScopedTrace
+
+// Pushes the given source file location and message onto a per-thread
+// trace stack maintained by Google Test.
+ScopedTrace::ScopedTrace(const char* file, int line, const Message& message)
+ GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) {
+ TraceInfo trace;
+ trace.file = file;
+ trace.line = line;
+ trace.message = message.GetString();
+
+ UnitTest::GetInstance()->PushGTestTrace(trace);
+}
+
+// Pops the info pushed by the c'tor.
+ScopedTrace::~ScopedTrace()
+ GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) {
+ UnitTest::GetInstance()->PopGTestTrace();
+}
+
+
+// class OsStackTraceGetter
+
+// Returns the current OS stack trace as an std::string. Parameters:
+//
+// max_depth - the maximum number of stack frames to be included
+// in the trace.
+// skip_count - the number of top frames to be skipped; doesn't count
+// against max_depth.
+//
+string OsStackTraceGetter::CurrentStackTrace(int /* max_depth */,
+ int /* skip_count */)
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+ return "";
+}
+
+void OsStackTraceGetter::UponLeavingGTest()
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+}
+
+const char* const
+OsStackTraceGetter::kElidedFramesMarker =
+ "... " GTEST_NAME_ " internal frames ...";
+
+// A helper class that creates the premature-exit file in its
+// constructor and deletes the file in its destructor.
+class ScopedPrematureExitFile {
+ public:
+ explicit ScopedPrematureExitFile(const char* premature_exit_filepath)
+ : premature_exit_filepath_(premature_exit_filepath) {
+ // If a path to the premature-exit file is specified...
+ if (premature_exit_filepath != NULL && *premature_exit_filepath != '\0') {
+ // create the file with a single "0" character in it. I/O
+ // errors are ignored as there's nothing better we can do and we
+ // don't want to fail the test because of this.
+ FILE* pfile = posix::FOpen(premature_exit_filepath, "w");
+ fwrite("0", 1, 1, pfile);
+ fclose(pfile);
+ }
+ }
+
+ ~ScopedPrematureExitFile() {
+ if (premature_exit_filepath_ != NULL && *premature_exit_filepath_ != '\0') {
+ remove(premature_exit_filepath_);
+ }
+ }
+
+ private:
+ const char* const premature_exit_filepath_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile);
+};
+
+} // namespace internal
+
+// class TestEventListeners
+
+TestEventListeners::TestEventListeners()
+ : repeater_(new internal::TestEventRepeater()),
+ default_result_printer_(NULL),
+ default_xml_generator_(NULL) {
+}
+
+TestEventListeners::~TestEventListeners() { delete repeater_; }
+
+// Returns the standard listener responsible for the default console
+// output. Can be removed from the listeners list to shut down default
+// console output. Note that removing this object from the listener list
+// with Release transfers its ownership to the user.
+void TestEventListeners::Append(TestEventListener* listener) {
+ repeater_->Append(listener);
+}
+
+// Removes the given event listener from the list and returns it. It then
+// becomes the caller's responsibility to delete the listener. Returns
+// NULL if the listener is not found in the list.
+TestEventListener* TestEventListeners::Release(TestEventListener* listener) {
+ if (listener == default_result_printer_)
+ default_result_printer_ = NULL;
+ else if (listener == default_xml_generator_)
+ default_xml_generator_ = NULL;
+ return repeater_->Release(listener);
+}
+
+// Returns repeater that broadcasts the TestEventListener events to all
+// subscribers.
+TestEventListener* TestEventListeners::repeater() { return repeater_; }
+
+// Sets the default_result_printer attribute to the provided listener.
+// The listener is also added to the listener list and previous
+// default_result_printer is removed from it and deleted. The listener can
+// also be NULL in which case it will not be added to the list. Does
+// nothing if the previous and the current listener objects are the same.
+void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) {
+ if (default_result_printer_ != listener) {
+ // It is an error to pass this method a listener that is already in the
+ // list.
+ delete Release(default_result_printer_);
+ default_result_printer_ = listener;
+ if (listener != NULL)
+ Append(listener);
+ }
+}
+
+// Sets the default_xml_generator attribute to the provided listener. The
+// listener is also added to the listener list and previous
+// default_xml_generator is removed from it and deleted. The listener can
+// also be NULL in which case it will not be added to the list. Does
+// nothing if the previous and the current listener objects are the same.
+void TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) {
+ if (default_xml_generator_ != listener) {
+ // It is an error to pass this method a listener that is already in the
+ // list.
+ delete Release(default_xml_generator_);
+ default_xml_generator_ = listener;
+ if (listener != NULL)
+ Append(listener);
+ }
+}
+
+// Controls whether events will be forwarded by the repeater to the
+// listeners in the list.
+bool TestEventListeners::EventForwardingEnabled() const {
+ return repeater_->forwarding_enabled();
+}
+
+void TestEventListeners::SuppressEventForwarding() {
+ repeater_->set_forwarding_enabled(false);
+}
+
+// class UnitTest
+
+// Gets the singleton UnitTest object. The first time this method is
+// called, a UnitTest object is constructed and returned. Consecutive
+// calls will return the same object.
+//
+// We don't protect this under mutex_ as a user is not supposed to
+// call this before main() starts, from which point on the return
+// value will never change.
+UnitTest* UnitTest::GetInstance() {
+ // When compiled with MSVC 7.1 in optimized mode, destroying the
+ // UnitTest object upon exiting the program messes up the exit code,
+ // causing successful tests to appear failed. We have to use a
+ // different implementation in this case to bypass the compiler bug.
+ // This implementation makes the compiler happy, at the cost of
+ // leaking the UnitTest object.
+
+ // CodeGear C++Builder insists on a public destructor for the
+ // default implementation. Use this implementation to keep good OO
+ // design with private destructor.
+
+#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__)
+ static UnitTest* const instance = new UnitTest;
+ return instance;
+#else
+ static UnitTest instance;
+ return &instance;
+#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__)
+}
+
+// Gets the number of successful test cases.
+int UnitTest::successful_test_case_count() const {
+ return impl()->successful_test_case_count();
+}
+
+// Gets the number of failed test cases.
+int UnitTest::failed_test_case_count() const {
+ return impl()->failed_test_case_count();
+}
+
+// Gets the number of all test cases.
+int UnitTest::total_test_case_count() const {
+ return impl()->total_test_case_count();
+}
+
+// Gets the number of all test cases that contain at least one test
+// that should run.
+int UnitTest::test_case_to_run_count() const {
+ return impl()->test_case_to_run_count();
+}
+
+// Gets the number of successful tests.
+int UnitTest::successful_test_count() const {
+ return impl()->successful_test_count();
+}
+
+// Gets the number of failed tests.
+int UnitTest::failed_test_count() const { return impl()->failed_test_count(); }
+
+// Gets the number of disabled tests that will be reported in the XML report.
+int UnitTest::reportable_disabled_test_count() const {
+ return impl()->reportable_disabled_test_count();
+}
+
+// Gets the number of disabled tests.
+int UnitTest::disabled_test_count() const {
+ return impl()->disabled_test_count();
+}
+
+// Gets the number of tests to be printed in the XML report.
+int UnitTest::reportable_test_count() const {
+ return impl()->reportable_test_count();
+}
+
+// Gets the number of all tests.
+int UnitTest::total_test_count() const { return impl()->total_test_count(); }
+
+// Gets the number of tests that should run.
+int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); }
+
+// Gets the time of the test program start, in ms from the start of the
+// UNIX epoch.
+internal::TimeInMillis UnitTest::start_timestamp() const {
+ return impl()->start_timestamp();
+}
+
+// Gets the elapsed time, in milliseconds.
+internal::TimeInMillis UnitTest::elapsed_time() const {
+ return impl()->elapsed_time();
+}
+
+// Returns true iff the unit test passed (i.e. all test cases passed).
+bool UnitTest::Passed() const { return impl()->Passed(); }
+
+// Returns true iff the unit test failed (i.e. some test case failed
+// or something outside of all tests failed).
+bool UnitTest::Failed() const { return impl()->Failed(); }
+
+// Gets the i-th test case among all the test cases. i can range from 0 to
+// total_test_case_count() - 1. If i is not in that range, returns NULL.
+const TestCase* UnitTest::GetTestCase(int i) const {
+ return impl()->GetTestCase(i);
+}
+
+// Returns the TestResult containing information on test failures and
+// properties logged outside of individual test cases.
+const TestResult& UnitTest::ad_hoc_test_result() const {
+ return *impl()->ad_hoc_test_result();
+}
+
+// Gets the i-th test case among all the test cases. i can range from 0 to
+// total_test_case_count() - 1. If i is not in that range, returns NULL.
+TestCase* UnitTest::GetMutableTestCase(int i) {
+ return impl()->GetMutableTestCase(i);
+}
+
+// Returns the list of event listeners that can be used to track events
+// inside Google Test.
+TestEventListeners& UnitTest::listeners() {
+ return *impl()->listeners();
+}
+
+// Registers and returns a global test environment. When a test
+// program is run, all global test environments will be set-up in the
+// order they were registered. After all tests in the program have
+// finished, all global test environments will be torn-down in the
+// *reverse* order they were registered.
+//
+// The UnitTest object takes ownership of the given environment.
+//
+// We don't protect this under mutex_, as we only support calling it
+// from the main thread.
+Environment* UnitTest::AddEnvironment(Environment* env) {
+ if (env == NULL) {
+ return NULL;
+ }
+
+ impl_->environments().push_back(env);
+ return env;
+}
+
+// Adds a TestPartResult to the current TestResult object. All Google Test
+// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call
+// this to report their results. The user code should use the
+// assertion macros instead of calling this directly.
+void UnitTest::AddTestPartResult(
+ TestPartResult::Type result_type,
+ const char* file_name,
+ int line_number,
+ const std::string& message,
+ const std::string& os_stack_trace) GTEST_LOCK_EXCLUDED_(mutex_) {
+ Message msg;
+ msg << message;
+
+ internal::MutexLock lock(&mutex_);
+ if (impl_->gtest_trace_stack().size() > 0) {
+ msg << "\n" << GTEST_NAME_ << " trace:";
+
+ for (int i = static_cast<int>(impl_->gtest_trace_stack().size());
+ i > 0; --i) {
+ const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1];
+ msg << "\n" << internal::FormatFileLocation(trace.file, trace.line)
+ << " " << trace.message;
+ }
+ }
+
+ if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) {
+ msg << internal::kStackTraceMarker << os_stack_trace;
+ }
+
+ const TestPartResult result =
+ TestPartResult(result_type, file_name, line_number,
+ msg.GetString().c_str());
+ impl_->GetTestPartResultReporterForCurrentThread()->
+ ReportTestPartResult(result);
+
+ if (result_type != TestPartResult::kSuccess) {
+ // gtest_break_on_failure takes precedence over
+ // gtest_throw_on_failure. This allows a user to set the latter
+ // in the code (perhaps in order to use Google Test assertions
+ // with another testing framework) and specify the former on the
+ // command line for debugging.
+ if (GTEST_FLAG(break_on_failure)) {
+#if GTEST_OS_WINDOWS
+ // Using DebugBreak on Windows allows gtest to still break into a debugger
+ // when a failure happens and both the --gtest_break_on_failure and
+ // the --gtest_catch_exceptions flags are specified.
+ DebugBreak();
+#else
+ // Dereference NULL through a volatile pointer to prevent the compiler
+ // from removing. We use this rather than abort() or __builtin_trap() for
+ // portability: Symbian doesn't implement abort() well, and some debuggers
+ // don't correctly trap abort().
+ *static_cast<volatile int*>(NULL) = 1;
+#endif // GTEST_OS_WINDOWS
+ } else if (GTEST_FLAG(throw_on_failure)) {
+#if GTEST_HAS_EXCEPTIONS
+ throw internal::GoogleTestFailureException(result);
+#else
+ // We cannot call abort() as it generates a pop-up in debug mode
+ // that cannot be suppressed in VC 7.1 or below.
+ exit(1);
+#endif
+ }
+ }
+}
+
+// Adds a TestProperty to the current TestResult object when invoked from
+// inside a test, to current TestCase's ad_hoc_test_result_ when invoked
+// from SetUpTestCase or TearDownTestCase, or to the global property set
+// when invoked elsewhere. If the result already contains a property with
+// the same key, the value will be updated.
+void UnitTest::RecordProperty(const std::string& key,
+ const std::string& value) {
+ impl_->RecordProperty(TestProperty(key, value));
+}
+
+// Runs all tests in this UnitTest object and prints the result.
+// Returns 0 if successful, or 1 otherwise.
+//
+// We don't protect this under mutex_, as we only support calling it
+// from the main thread.
+int UnitTest::Run() {
+ const bool in_death_test_child_process =
+ internal::GTEST_FLAG(internal_run_death_test).length() > 0;
+
+ // Google Test implements this protocol for catching that a test
+ // program exits before returning control to Google Test:
+ //
+ // 1. Upon start, Google Test creates a file whose absolute path
+ // is specified by the environment variable
+ // TEST_PREMATURE_EXIT_FILE.
+ // 2. When Google Test has finished its work, it deletes the file.
+ //
+ // This allows a test runner to set TEST_PREMATURE_EXIT_FILE before
+ // running a Google-Test-based test program and check the existence
+ // of the file at the end of the test execution to see if it has
+ // exited prematurely.
+
+ // If we are in the child process of a death test, don't
+ // create/delete the premature exit file, as doing so is unnecessary
+ // and will confuse the parent process. Otherwise, create/delete
+ // the file upon entering/leaving this function. If the program
+ // somehow exits before this function has a chance to return, the
+ // premature-exit file will be left undeleted, causing a test runner
+ // that understands the premature-exit-file protocol to report the
+ // test as having failed.
+ const internal::ScopedPrematureExitFile premature_exit_file(
+ in_death_test_child_process ?
+ NULL : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE"));
+
+ // Captures the value of GTEST_FLAG(catch_exceptions). This value will be
+ // used for the duration of the program.
+ impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions));
+
+#if GTEST_HAS_SEH
+ // Either the user wants Google Test to catch exceptions thrown by the
+ // tests or this is executing in the context of death test child
+ // process. In either case the user does not want to see pop-up dialogs
+ // about crashes - they are expected.
+ if (impl()->catch_exceptions() || in_death_test_child_process) {
+# if !GTEST_OS_WINDOWS_MOBILE
+ // SetErrorMode doesn't exist on CE.
+ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT |
+ SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
+# endif // !GTEST_OS_WINDOWS_MOBILE
+
+# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE
+ // Death test children can be terminated with _abort(). On Windows,
+ // _abort() can show a dialog with a warning message. This forces the
+ // abort message to go to stderr instead.
+ _set_error_mode(_OUT_TO_STDERR);
+# endif
+
+# if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE
+ // In the debug version, Visual Studio pops up a separate dialog
+ // offering a choice to debug the aborted program. We need to suppress
+ // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement
+ // executed. Google Test will notify the user of any unexpected
+ // failure via stderr.
+ //
+ // VC++ doesn't define _set_abort_behavior() prior to the version 8.0.
+ // Users of prior VC versions shall suffer the agony and pain of
+ // clicking through the countless debug dialogs.
+ // TODO(vladl at google.com): find a way to suppress the abort dialog() in the
+ // debug mode when compiled with VC 7.1 or lower.
+ if (!GTEST_FLAG(break_on_failure))
+ _set_abort_behavior(
+ 0x0, // Clear the following flags:
+ _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump.
+# endif
+ }
+#endif // GTEST_HAS_SEH
+
+ return internal::HandleExceptionsInMethodIfSupported(
+ impl(),
+ &internal::UnitTestImpl::RunAllTests,
+ "auxiliary test code (environments or event listeners)") ? 0 : 1;
+}
+
+// Returns the working directory when the first TEST() or TEST_F() was
+// executed.
+const char* UnitTest::original_working_dir() const {
+ return impl_->original_working_dir_.c_str();
+}
+
+// Returns the TestCase object for the test that's currently running,
+// or NULL if no test is running.
+const TestCase* UnitTest::current_test_case() const
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+ internal::MutexLock lock(&mutex_);
+ return impl_->current_test_case();
+}
+
+// Returns the TestInfo object for the test that's currently running,
+// or NULL if no test is running.
+const TestInfo* UnitTest::current_test_info() const
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+ internal::MutexLock lock(&mutex_);
+ return impl_->current_test_info();
+}
+
+// Returns the random seed used at the start of the current test run.
+int UnitTest::random_seed() const { return impl_->random_seed(); }
+
+#if GTEST_HAS_PARAM_TEST
+// Returns ParameterizedTestCaseRegistry object used to keep track of
+// value-parameterized tests and instantiate and register them.
+internal::ParameterizedTestCaseRegistry&
+ UnitTest::parameterized_test_registry()
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+ return impl_->parameterized_test_registry();
+}
+#endif // GTEST_HAS_PARAM_TEST
+
+// Creates an empty UnitTest.
+UnitTest::UnitTest() {
+ impl_ = new internal::UnitTestImpl(this);
+}
+
+// Destructor of UnitTest.
+UnitTest::~UnitTest() {
+ delete impl_;
+}
+
+// Pushes a trace defined by SCOPED_TRACE() on to the per-thread
+// Google Test trace stack.
+void UnitTest::PushGTestTrace(const internal::TraceInfo& trace)
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+ internal::MutexLock lock(&mutex_);
+ impl_->gtest_trace_stack().push_back(trace);
+}
+
+// Pops a trace from the per-thread Google Test trace stack.
+void UnitTest::PopGTestTrace()
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+ internal::MutexLock lock(&mutex_);
+ impl_->gtest_trace_stack().pop_back();
+}
+
+namespace internal {
+
+UnitTestImpl::UnitTestImpl(UnitTest* parent)
+ : parent_(parent),
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4355) // Temporarily disables warning 4355
+ // (using this in initializer).
+ default_global_test_part_result_reporter_(this),
+ default_per_thread_test_part_result_reporter_(this),
+# pragma warning(pop) // Restores the warning state again.
+#else
+ default_global_test_part_result_reporter_(this),
+ default_per_thread_test_part_result_reporter_(this),
+#endif // _MSC_VER
+ global_test_part_result_repoter_(
+ &default_global_test_part_result_reporter_),
+ per_thread_test_part_result_reporter_(
+ &default_per_thread_test_part_result_reporter_),
+#if GTEST_HAS_PARAM_TEST
+ parameterized_test_registry_(),
+ parameterized_tests_registered_(false),
+#endif // GTEST_HAS_PARAM_TEST
+ last_death_test_case_(-1),
+ current_test_case_(NULL),
+ current_test_info_(NULL),
+ ad_hoc_test_result_(),
+ os_stack_trace_getter_(NULL),
+ post_flag_parse_init_performed_(false),
+ random_seed_(0), // Will be overridden by the flag before first use.
+ random_(0), // Will be reseeded before first use.
+ start_timestamp_(0),
+ elapsed_time_(0),
+#if GTEST_HAS_DEATH_TEST
+ death_test_factory_(new DefaultDeathTestFactory),
+#endif
+ // Will be overridden by the flag before first use.
+ catch_exceptions_(false) {
+ listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter);
+}
+
+UnitTestImpl::~UnitTestImpl() {
+ // Deletes every TestCase.
+ ForEach(test_cases_, internal::Delete<TestCase>);
+
+ // Deletes every Environment.
+ ForEach(environments_, internal::Delete<Environment>);
+
+ delete os_stack_trace_getter_;
+}
+
+// Adds a TestProperty to the current TestResult object when invoked in a
+// context of a test, to current test case's ad_hoc_test_result when invoke
+// from SetUpTestCase/TearDownTestCase, or to the global property set
+// otherwise. If the result already contains a property with the same key,
+// the value will be updated.
+void UnitTestImpl::RecordProperty(const TestProperty& test_property) {
+ std::string xml_element;
+ TestResult* test_result; // TestResult appropriate for property recording.
+
+ if (current_test_info_ != NULL) {
+ xml_element = "testcase";
+ test_result = &(current_test_info_->result_);
+ } else if (current_test_case_ != NULL) {
+ xml_element = "testsuite";
+ test_result = &(current_test_case_->ad_hoc_test_result_);
+ } else {
+ xml_element = "testsuites";
+ test_result = &ad_hoc_test_result_;
+ }
+ test_result->RecordProperty(xml_element, test_property);
+}
+
+#if GTEST_HAS_DEATH_TEST
+// Disables event forwarding if the control is currently in a death test
+// subprocess. Must not be called before InitGoogleTest.
+void UnitTestImpl::SuppressTestEventsIfInSubprocess() {
+ if (internal_run_death_test_flag_.get() != NULL)
+ listeners()->SuppressEventForwarding();
+}
+#endif // GTEST_HAS_DEATH_TEST
+
+// Initializes event listeners performing XML output as specified by
+// UnitTestOptions. Must not be called before InitGoogleTest.
+void UnitTestImpl::ConfigureXmlOutput() {
+ const std::string& output_format = UnitTestOptions::GetOutputFormat();
+ if (output_format == "xml") {
+ listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter(
+ UnitTestOptions::GetAbsolutePathToOutputFile().c_str()));
+ } else if (output_format != "") {
+ printf("WARNING: unrecognized output format \"%s\" ignored.\n",
+ output_format.c_str());
+ fflush(stdout);
+ }
+}
+
+#if GTEST_CAN_STREAM_RESULTS_
+// Initializes event listeners for streaming test results in string form.
+// Must not be called before InitGoogleTest.
+void UnitTestImpl::ConfigureStreamingOutput() {
+ const std::string& target = GTEST_FLAG(stream_result_to);
+ if (!target.empty()) {
+ const size_t pos = target.find(':');
+ if (pos != std::string::npos) {
+ listeners()->Append(new StreamingListener(target.substr(0, pos),
+ target.substr(pos+1)));
+ } else {
+ printf("WARNING: unrecognized streaming target \"%s\" ignored.\n",
+ target.c_str());
+ fflush(stdout);
+ }
+ }
+}
+#endif // GTEST_CAN_STREAM_RESULTS_
+
+// Performs initialization dependent upon flag values obtained in
+// ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to
+// ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest
+// this function is also called from RunAllTests. Since this function can be
+// called more than once, it has to be idempotent.
+void UnitTestImpl::PostFlagParsingInit() {
+ // Ensures that this function does not execute more than once.
+ if (!post_flag_parse_init_performed_) {
+ post_flag_parse_init_performed_ = true;
+
+#if GTEST_HAS_DEATH_TEST
+ InitDeathTestSubprocessControlInfo();
+ SuppressTestEventsIfInSubprocess();
+#endif // GTEST_HAS_DEATH_TEST
+
+ // Registers parameterized tests. This makes parameterized tests
+ // available to the UnitTest reflection API without running
+ // RUN_ALL_TESTS.
+ RegisterParameterizedTests();
+
+ // Configures listeners for XML output. This makes it possible for users
+ // to shut down the default XML output before invoking RUN_ALL_TESTS.
+ ConfigureXmlOutput();
+
+#if GTEST_CAN_STREAM_RESULTS_
+ // Configures listeners for streaming test results to the specified server.
+ ConfigureStreamingOutput();
+#endif // GTEST_CAN_STREAM_RESULTS_
+ }
+}
+
+// A predicate that checks the name of a TestCase against a known
+// value.
+//
+// This is used for implementation of the UnitTest class only. We put
+// it in the anonymous namespace to prevent polluting the outer
+// namespace.
+//
+// TestCaseNameIs is copyable.
+class TestCaseNameIs {
+ public:
+ // Constructor.
+ explicit TestCaseNameIs(const std::string& name)
+ : name_(name) {}
+
+ // Returns true iff the name of test_case matches name_.
+ bool operator()(const TestCase* test_case) const {
+ return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0;
+ }
+
+ private:
+ std::string name_;
+};
+
+// Finds and returns a TestCase with the given name. If one doesn't
+// exist, creates one and returns it. It's the CALLER'S
+// RESPONSIBILITY to ensure that this function is only called WHEN THE
+// TESTS ARE NOT SHUFFLED.
+//
+// Arguments:
+//
+// test_case_name: name of the test case
+// type_param: the name of the test case's type parameter, or NULL if
+// this is not a typed or a type-parameterized test case.
+// set_up_tc: pointer to the function that sets up the test case
+// tear_down_tc: pointer to the function that tears down the test case
+TestCase* UnitTestImpl::GetTestCase(const char* test_case_name,
+ const char* type_param,
+ Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc) {
+ // Can we find a TestCase with the given name?
+ const std::vector<TestCase*>::const_iterator test_case =
+ std::find_if(test_cases_.begin(), test_cases_.end(),
+ TestCaseNameIs(test_case_name));
+
+ if (test_case != test_cases_.end())
+ return *test_case;
+
+ // No. Let's create one.
+ TestCase* const new_test_case =
+ new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc);
+
+ // Is this a death test case?
+ if (internal::UnitTestOptions::MatchesFilter(test_case_name,
+ kDeathTestCaseFilter)) {
+ // Yes. Inserts the test case after the last death test case
+ // defined so far. This only works when the test cases haven't
+ // been shuffled. Otherwise we may end up running a death test
+ // after a non-death test.
+ ++last_death_test_case_;
+ test_cases_.insert(test_cases_.begin() + last_death_test_case_,
+ new_test_case);
+ } else {
+ // No. Appends to the end of the list.
+ test_cases_.push_back(new_test_case);
+ }
+
+ test_case_indices_.push_back(static_cast<int>(test_case_indices_.size()));
+ return new_test_case;
+}
+
+// Helpers for setting up / tearing down the given environment. They
+// are for use in the ForEach() function.
+static void SetUpEnvironment(Environment* env) { env->SetUp(); }
+static void TearDownEnvironment(Environment* env) { env->TearDown(); }
+
+// Runs all tests in this UnitTest object, prints the result, and
+// returns true if all tests are successful. If any exception is
+// thrown during a test, the test is considered to be failed, but the
+// rest of the tests will still be run.
+//
+// When parameterized tests are enabled, it expands and registers
+// parameterized tests first in RegisterParameterizedTests().
+// All other functions called from RunAllTests() may safely assume that
+// parameterized tests are ready to be counted and run.
+bool UnitTestImpl::RunAllTests() {
+ // Makes sure InitGoogleTest() was called.
+ if (!GTestIsInitialized()) {
+ printf("%s",
+ "\nThis test program did NOT call ::testing::InitGoogleTest "
+ "before calling RUN_ALL_TESTS(). Please fix it.\n");
+ return false;
+ }
+
+ // Do not run any test if the --help flag was specified.
+ if (g_help_flag)
+ return true;
+
+ // Repeats the call to the post-flag parsing initialization in case the
+ // user didn't call InitGoogleTest.
+ PostFlagParsingInit();
+
+ // Even if sharding is not on, test runners may want to use the
+ // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding
+ // protocol.
+ internal::WriteToShardStatusFileIfNeeded();
+
+ // True iff we are in a subprocess for running a thread-safe-style
+ // death test.
+ bool in_subprocess_for_death_test = false;
+
+#if GTEST_HAS_DEATH_TEST
+ in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL);
+#endif // GTEST_HAS_DEATH_TEST
+
+ const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex,
+ in_subprocess_for_death_test);
+
+ // Compares the full test names with the filter to decide which
+ // tests to run.
+ const bool has_tests_to_run = FilterTests(should_shard
+ ? HONOR_SHARDING_PROTOCOL
+ : IGNORE_SHARDING_PROTOCOL) > 0;
+
+ // Lists the tests and exits if the --gtest_list_tests flag was specified.
+ if (GTEST_FLAG(list_tests)) {
+ // This must be called *after* FilterTests() has been called.
+ ListTestsMatchingFilter();
+ return true;
+ }
+
+ random_seed_ = GTEST_FLAG(shuffle) ?
+ GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0;
+
+ // True iff at least one test has failed.
+ bool failed = false;
+
+ TestEventListener* repeater = listeners()->repeater();
+
+ start_timestamp_ = GetTimeInMillis();
+ repeater->OnTestProgramStart(*parent_);
+
+ // How many times to repeat the tests? We don't want to repeat them
+ // when we are inside the subprocess of a death test.
+ const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat);
+ // Repeats forever if the repeat count is negative.
+ const bool forever = repeat < 0;
+ for (int i = 0; forever || i != repeat; i++) {
+ // We want to preserve failures generated by ad-hoc test
+ // assertions executed before RUN_ALL_TESTS().
+ ClearNonAdHocTestResult();
+
+ const TimeInMillis start = GetTimeInMillis();
+
+ // Shuffles test cases and tests if requested.
+ if (has_tests_to_run && GTEST_FLAG(shuffle)) {
+ random()->Reseed(random_seed_);
+ // This should be done before calling OnTestIterationStart(),
+ // such that a test event listener can see the actual test order
+ // in the event.
+ ShuffleTests();
+ }
+
+ // Tells the unit test event listeners that the tests are about to start.
+ repeater->OnTestIterationStart(*parent_, i);
+
+ // Runs each test case if there is at least one test to run.
+ if (has_tests_to_run) {
+ // Sets up all environments beforehand.
+ repeater->OnEnvironmentsSetUpStart(*parent_);
+ ForEach(environments_, SetUpEnvironment);
+ repeater->OnEnvironmentsSetUpEnd(*parent_);
+
+ // Runs the tests only if there was no fatal failure during global
+ // set-up.
+ if (!Test::HasFatalFailure()) {
+ for (int test_index = 0; test_index < total_test_case_count();
+ test_index++) {
+ GetMutableTestCase(test_index)->Run();
+ }
+ }
+
+ // Tears down all environments in reverse order afterwards.
+ repeater->OnEnvironmentsTearDownStart(*parent_);
+ std::for_each(environments_.rbegin(), environments_.rend(),
+ TearDownEnvironment);
+ repeater->OnEnvironmentsTearDownEnd(*parent_);
+ }
+
+ elapsed_time_ = GetTimeInMillis() - start;
+
+ // Tells the unit test event listener that the tests have just finished.
+ repeater->OnTestIterationEnd(*parent_, i);
+
+ // Gets the result and clears it.
+ if (!Passed()) {
+ failed = true;
+ }
+
+ // Restores the original test order after the iteration. This
+ // allows the user to quickly repro a failure that happens in the
+ // N-th iteration without repeating the first (N - 1) iterations.
+ // This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in
+ // case the user somehow changes the value of the flag somewhere
+ // (it's always safe to unshuffle the tests).
+ UnshuffleTests();
+
+ if (GTEST_FLAG(shuffle)) {
+ // Picks a new random seed for each iteration.
+ random_seed_ = GetNextRandomSeed(random_seed_);
+ }
+ }
+
+ repeater->OnTestProgramEnd(*parent_);
+
+ return !failed;
+}
+
+// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file
+// if the variable is present. If a file already exists at this location, this
+// function will write over it. If the variable is present, but the file cannot
+// be created, prints an error and exits.
+void WriteToShardStatusFileIfNeeded() {
+ const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile);
+ if (test_shard_file != NULL) {
+ FILE* const file = posix::FOpen(test_shard_file, "w");
+ if (file == NULL) {
+ ColoredPrintf(COLOR_RED,
+ "Could not write to the test shard status file \"%s\" "
+ "specified by the %s environment variable.\n",
+ test_shard_file, kTestShardStatusFile);
+ fflush(stdout);
+ exit(EXIT_FAILURE);
+ }
+ fclose(file);
+ }
+}
+
+// Checks whether sharding is enabled by examining the relevant
+// environment variable values. If the variables are present,
+// but inconsistent (i.e., shard_index >= total_shards), prints
+// an error and exits. If in_subprocess_for_death_test, sharding is
+// disabled because it must only be applied to the original test
+// process. Otherwise, we could filter out death tests we intended to execute.
+bool ShouldShard(const char* total_shards_env,
+ const char* shard_index_env,
+ bool in_subprocess_for_death_test) {
+ if (in_subprocess_for_death_test) {
+ return false;
+ }
+
+ const Int32 total_shards = Int32FromEnvOrDie(total_shards_env, -1);
+ const Int32 shard_index = Int32FromEnvOrDie(shard_index_env, -1);
+
+ if (total_shards == -1 && shard_index == -1) {
+ return false;
+ } else if (total_shards == -1 && shard_index != -1) {
+ const Message msg = Message()
+ << "Invalid environment variables: you have "
+ << kTestShardIndex << " = " << shard_index
+ << ", but have left " << kTestTotalShards << " unset.\n";
+ ColoredPrintf(COLOR_RED, msg.GetString().c_str());
+ fflush(stdout);
+ exit(EXIT_FAILURE);
+ } else if (total_shards != -1 && shard_index == -1) {
+ const Message msg = Message()
+ << "Invalid environment variables: you have "
+ << kTestTotalShards << " = " << total_shards
+ << ", but have left " << kTestShardIndex << " unset.\n";
+ ColoredPrintf(COLOR_RED, msg.GetString().c_str());
+ fflush(stdout);
+ exit(EXIT_FAILURE);
+ } else if (shard_index < 0 || shard_index >= total_shards) {
+ const Message msg = Message()
+ << "Invalid environment variables: we require 0 <= "
+ << kTestShardIndex << " < " << kTestTotalShards
+ << ", but you have " << kTestShardIndex << "=" << shard_index
+ << ", " << kTestTotalShards << "=" << total_shards << ".\n";
+ ColoredPrintf(COLOR_RED, msg.GetString().c_str());
+ fflush(stdout);
+ exit(EXIT_FAILURE);
+ }
+
+ return total_shards > 1;
+}
+
+// Parses the environment variable var as an Int32. If it is unset,
+// returns default_val. If it is not an Int32, prints an error
+// and aborts.
+Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) {
+ const char* str_val = posix::GetEnv(var);
+ if (str_val == NULL) {
+ return default_val;
+ }
+
+ Int32 result;
+ if (!ParseInt32(Message() << "The value of environment variable " << var,
+ str_val, &result)) {
+ exit(EXIT_FAILURE);
+ }
+ return result;
+}
+
+// Given the total number of shards, the shard index, and the test id,
+// returns true iff the test should be run on this shard. The test id is
+// some arbitrary but unique non-negative integer assigned to each test
+// method. Assumes that 0 <= shard_index < total_shards.
+bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) {
+ return (test_id % total_shards) == shard_index;
+}
+
+// Compares the name of each test with the user-specified filter to
+// decide whether the test should be run, then records the result in
+// each TestCase and TestInfo object.
+// If shard_tests == true, further filters tests based on sharding
+// variables in the environment - see
+// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide.
+// Returns the number of tests that should run.
+int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) {
+ const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ?
+ Int32FromEnvOrDie(kTestTotalShards, -1) : -1;
+ const Int32 shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ?
+ Int32FromEnvOrDie(kTestShardIndex, -1) : -1;
+
+ // num_runnable_tests are the number of tests that will
+ // run across all shards (i.e., match filter and are not disabled).
+ // num_selected_tests are the number of tests to be run on
+ // this shard.
+ int num_runnable_tests = 0;
+ int num_selected_tests = 0;
+ for (size_t i = 0; i < test_cases_.size(); i++) {
+ TestCase* const test_case = test_cases_[i];
+ const std::string &test_case_name = test_case->name();
+ test_case->set_should_run(false);
+
+ for (size_t j = 0; j < test_case->test_info_list().size(); j++) {
+ TestInfo* const test_info = test_case->test_info_list()[j];
+ const std::string test_name(test_info->name());
+ // A test is disabled if test case name or test name matches
+ // kDisableTestFilter.
+ const bool is_disabled =
+ internal::UnitTestOptions::MatchesFilter(test_case_name,
+ kDisableTestFilter) ||
+ internal::UnitTestOptions::MatchesFilter(test_name,
+ kDisableTestFilter);
+ test_info->is_disabled_ = is_disabled;
+
+ const bool matches_filter =
+ internal::UnitTestOptions::FilterMatchesTest(test_case_name,
+ test_name);
+ test_info->matches_filter_ = matches_filter;
+
+ const bool is_runnable =
+ (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) &&
+ matches_filter;
+
+ const bool is_selected = is_runnable &&
+ (shard_tests == IGNORE_SHARDING_PROTOCOL ||
+ ShouldRunTestOnShard(total_shards, shard_index,
+ num_runnable_tests));
+
+ num_runnable_tests += is_runnable;
+ num_selected_tests += is_selected;
+
+ test_info->should_run_ = is_selected;
+ test_case->set_should_run(test_case->should_run() || is_selected);
+ }
+ }
+ return num_selected_tests;
+}
+
+// Prints the given C-string on a single line by replacing all '\n'
+// characters with string "\\n". If the output takes more than
+// max_length characters, only prints the first max_length characters
+// and "...".
+static void PrintOnOneLine(const char* str, int max_length) {
+ if (str != NULL) {
+ for (int i = 0; *str != '\0'; ++str) {
+ if (i >= max_length) {
+ printf("...");
+ break;
+ }
+ if (*str == '\n') {
+ printf("\\n");
+ i += 2;
+ } else {
+ printf("%c", *str);
+ ++i;
+ }
+ }
+ }
+}
+
+// Prints the names of the tests matching the user-specified filter flag.
+void UnitTestImpl::ListTestsMatchingFilter() {
+ // Print at most this many characters for each type/value parameter.
+ const int kMaxParamLength = 250;
+
+ for (size_t i = 0; i < test_cases_.size(); i++) {
+ const TestCase* const test_case = test_cases_[i];
+ bool printed_test_case_name = false;
+
+ for (size_t j = 0; j < test_case->test_info_list().size(); j++) {
+ const TestInfo* const test_info =
+ test_case->test_info_list()[j];
+ if (test_info->matches_filter_) {
+ if (!printed_test_case_name) {
+ printed_test_case_name = true;
+ printf("%s.", test_case->name());
+ if (test_case->type_param() != NULL) {
+ printf(" # %s = ", kTypeParamLabel);
+ // We print the type parameter on a single line to make
+ // the output easy to parse by a program.
+ PrintOnOneLine(test_case->type_param(), kMaxParamLength);
+ }
+ printf("\n");
+ }
+ printf(" %s", test_info->name());
+ if (test_info->value_param() != NULL) {
+ printf(" # %s = ", kValueParamLabel);
+ // We print the value parameter on a single line to make the
+ // output easy to parse by a program.
+ PrintOnOneLine(test_info->value_param(), kMaxParamLength);
+ }
+ printf("\n");
+ }
+ }
+ }
+ fflush(stdout);
+}
+
+// Sets the OS stack trace getter.
+//
+// Does nothing if the input and the current OS stack trace getter are
+// the same; otherwise, deletes the old getter and makes the input the
+// current getter.
+void UnitTestImpl::set_os_stack_trace_getter(
+ OsStackTraceGetterInterface* getter) {
+ if (os_stack_trace_getter_ != getter) {
+ delete os_stack_trace_getter_;
+ os_stack_trace_getter_ = getter;
+ }
+}
+
+// Returns the current OS stack trace getter if it is not NULL;
+// otherwise, creates an OsStackTraceGetter, makes it the current
+// getter, and returns it.
+OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() {
+ if (os_stack_trace_getter_ == NULL) {
+ os_stack_trace_getter_ = new OsStackTraceGetter;
+ }
+
+ return os_stack_trace_getter_;
+}
+
+// Returns the TestResult for the test that's currently running, or
+// the TestResult for the ad hoc test if no test is running.
+TestResult* UnitTestImpl::current_test_result() {
+ return current_test_info_ ?
+ &(current_test_info_->result_) : &ad_hoc_test_result_;
+}
+
+// Shuffles all test cases, and the tests within each test case,
+// making sure that death tests are still run first.
+void UnitTestImpl::ShuffleTests() {
+ // Shuffles the death test cases.
+ ShuffleRange(random(), 0, last_death_test_case_ + 1, &test_case_indices_);
+
+ // Shuffles the non-death test cases.
+ ShuffleRange(random(), last_death_test_case_ + 1,
+ static_cast<int>(test_cases_.size()), &test_case_indices_);
+
+ // Shuffles the tests inside each test case.
+ for (size_t i = 0; i < test_cases_.size(); i++) {
+ test_cases_[i]->ShuffleTests(random());
+ }
+}
+
+// Restores the test cases and tests to their order before the first shuffle.
+void UnitTestImpl::UnshuffleTests() {
+ for (size_t i = 0; i < test_cases_.size(); i++) {
+ // Unshuffles the tests in each test case.
+ test_cases_[i]->UnshuffleTests();
+ // Resets the index of each test case.
+ test_case_indices_[i] = static_cast<int>(i);
+ }
+}
+
+// Returns the current OS stack trace as an std::string.
+//
+// The maximum number of stack frames to be included is specified by
+// the gtest_stack_trace_depth flag. The skip_count parameter
+// specifies the number of top frames to be skipped, which doesn't
+// count against the number of frames to be included.
+//
+// For example, if Foo() calls Bar(), which in turn calls
+// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in
+// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't.
+std::string GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/,
+ int skip_count) {
+ // We pass skip_count + 1 to skip this wrapper function in addition
+ // to what the user really wants to skip.
+ return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1);
+}
+
+// Used by the GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_ macro to
+// suppress unreachable code warnings.
+namespace {
+class ClassUniqueToAlwaysTrue {};
+}
+
+bool IsTrue(bool condition) { return condition; }
+
+bool AlwaysTrue() {
+#if GTEST_HAS_EXCEPTIONS
+ // This condition is always false so AlwaysTrue() never actually throws,
+ // but it makes the compiler think that it may throw.
+ if (IsTrue(false))
+ throw ClassUniqueToAlwaysTrue();
+#endif // GTEST_HAS_EXCEPTIONS
+ return true;
+}
+
+// If *pstr starts with the given prefix, modifies *pstr to be right
+// past the prefix and returns true; otherwise leaves *pstr unchanged
+// and returns false. None of pstr, *pstr, and prefix can be NULL.
+bool SkipPrefix(const char* prefix, const char** pstr) {
+ const size_t prefix_len = strlen(prefix);
+ if (strncmp(*pstr, prefix, prefix_len) == 0) {
+ *pstr += prefix_len;
+ return true;
+ }
+ return false;
+}
+
+// Parses a string as a command line flag. The string should have
+// the format "--flag=value". When def_optional is true, the "=value"
+// part can be omitted.
+//
+// Returns the value of the flag, or NULL if the parsing failed.
+const char* ParseFlagValue(const char* str,
+ const char* flag,
+ bool def_optional) {
+ // str and flag must not be NULL.
+ if (str == NULL || flag == NULL) return NULL;
+
+ // The flag must start with "--" followed by GTEST_FLAG_PREFIX_.
+ const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag;
+ const size_t flag_len = flag_str.length();
+ if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL;
+
+ // Skips the flag name.
+ const char* flag_end = str + flag_len;
+
+ // When def_optional is true, it's OK to not have a "=value" part.
+ if (def_optional && (flag_end[0] == '\0')) {
+ return flag_end;
+ }
+
+ // If def_optional is true and there are more characters after the
+ // flag name, or if def_optional is false, there must be a '=' after
+ // the flag name.
+ if (flag_end[0] != '=') return NULL;
+
+ // Returns the string after "=".
+ return flag_end + 1;
+}
+
+// Parses a string for a bool flag, in the form of either
+// "--flag=value" or "--flag".
+//
+// In the former case, the value is taken as true as long as it does
+// not start with '0', 'f', or 'F'.
+//
+// In the latter case, the value is taken as true.
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+bool ParseBoolFlag(const char* str, const char* flag, bool* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseFlagValue(str, flag, true);
+
+ // Aborts if the parsing failed.
+ if (value_str == NULL) return false;
+
+ // Converts the string value to a bool.
+ *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F');
+ return true;
+}
+
+// Parses a string for an Int32 flag, in the form of
+// "--flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+bool ParseInt32Flag(const char* str, const char* flag, Int32* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseFlagValue(str, flag, false);
+
+ // Aborts if the parsing failed.
+ if (value_str == NULL) return false;
+
+ // Sets *value to the value of the flag.
+ return ParseInt32(Message() << "The value of flag --" << flag,
+ value_str, value);
+}
+
+// Parses a string for a string flag, in the form of
+// "--flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+bool ParseStringFlag(const char* str, const char* flag, std::string* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseFlagValue(str, flag, false);
+
+ // Aborts if the parsing failed.
+ if (value_str == NULL) return false;
+
+ // Sets *value to the value of the flag.
+ *value = value_str;
+ return true;
+}
+
+// Determines whether a string has a prefix that Google Test uses for its
+// flags, i.e., starts with GTEST_FLAG_PREFIX_ or GTEST_FLAG_PREFIX_DASH_.
+// If Google Test detects that a command line flag has its prefix but is not
+// recognized, it will print its help message. Flags starting with
+// GTEST_INTERNAL_PREFIX_ followed by "internal_" are considered Google Test
+// internal flags and do not trigger the help message.
+static bool HasGoogleTestFlagPrefix(const char* str) {
+ return (SkipPrefix("--", &str) ||
+ SkipPrefix("-", &str) ||
+ SkipPrefix("/", &str)) &&
+ !SkipPrefix(GTEST_FLAG_PREFIX_ "internal_", &str) &&
+ (SkipPrefix(GTEST_FLAG_PREFIX_, &str) ||
+ SkipPrefix(GTEST_FLAG_PREFIX_DASH_, &str));
+}
+
+// Prints a string containing code-encoded text. The following escape
+// sequences can be used in the string to control the text color:
+//
+// @@ prints a single '@' character.
+// @R changes the color to red.
+// @G changes the color to green.
+// @Y changes the color to yellow.
+// @D changes to the default terminal text color.
+//
+// TODO(wan at google.com): Write tests for this once we add stdout
+// capturing to Google Test.
+static void PrintColorEncoded(const char* str) {
+ GTestColor color = COLOR_DEFAULT; // The current color.
+
+ // Conceptually, we split the string into segments divided by escape
+ // sequences. Then we print one segment at a time. At the end of
+ // each iteration, the str pointer advances to the beginning of the
+ // next segment.
+ for (;;) {
+ const char* p = strchr(str, '@');
+ if (p == NULL) {
+ ColoredPrintf(color, "%s", str);
+ return;
+ }
+
+ ColoredPrintf(color, "%s", std::string(str, p).c_str());
+
+ const char ch = p[1];
+ str = p + 2;
+ if (ch == '@') {
+ ColoredPrintf(color, "@");
+ } else if (ch == 'D') {
+ color = COLOR_DEFAULT;
+ } else if (ch == 'R') {
+ color = COLOR_RED;
+ } else if (ch == 'G') {
+ color = COLOR_GREEN;
+ } else if (ch == 'Y') {
+ color = COLOR_YELLOW;
+ } else {
+ --str;
+ }
+ }
+}
+
+static const char kColorEncodedHelpMessage[] =
+"This program contains tests written using " GTEST_NAME_ ". You can use the\n"
+"following command line flags to control its behavior:\n"
+"\n"
+"Test Selection:\n"
+" @G--" GTEST_FLAG_PREFIX_ "list_tests at D\n"
+" List the names of all tests instead of running them. The name of\n"
+" TEST(Foo, Bar) is \"Foo.Bar\".\n"
+" @G--" GTEST_FLAG_PREFIX_ "filter=@YPOSTIVE_PATTERNS"
+ "[@G- at YNEGATIVE_PATTERNS]@D\n"
+" Run only the tests whose name matches one of the positive patterns but\n"
+" none of the negative patterns. '?' matches any single character; '*'\n"
+" matches any substring; ':' separates two patterns.\n"
+" @G--" GTEST_FLAG_PREFIX_ "also_run_disabled_tests at D\n"
+" Run all disabled tests too.\n"
+"\n"
+"Test Execution:\n"
+" @G--" GTEST_FLAG_PREFIX_ "repeat=@Y[COUNT]@D\n"
+" Run the tests repeatedly; use a negative count to repeat forever.\n"
+" @G--" GTEST_FLAG_PREFIX_ "shuffle at D\n"
+" Randomize tests' orders on every iteration.\n"
+" @G--" GTEST_FLAG_PREFIX_ "random_seed=@Y[NUMBER]@D\n"
+" Random number seed to use for shuffling test orders (between 1 and\n"
+" 99999, or 0 to use a seed based on the current time).\n"
+"\n"
+"Test Output:\n"
+" @G--" GTEST_FLAG_PREFIX_ "color=@Y(@Gyes at Y|@Gno at Y|@Gauto at Y)@D\n"
+" Enable/disable colored output. The default is @Gauto at D.\n"
+" - at G-" GTEST_FLAG_PREFIX_ "print_time=0 at D\n"
+" Don't print the elapsed time of each test.\n"
+" @G--" GTEST_FLAG_PREFIX_ "output=xml at Y[@G:@YDIRECTORY_PATH at G"
+ GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n"
+" Generate an XML report in the given directory or with the given file\n"
+" name. @YFILE_PATH at D defaults to @Gtest_details.xml at D.\n"
+#if GTEST_CAN_STREAM_RESULTS_
+" @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST at G:@YPORT at D\n"
+" Stream test results to the given server.\n"
+#endif // GTEST_CAN_STREAM_RESULTS_
+"\n"
+"Assertion Behavior:\n"
+#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
+" @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast at Y|@Gthreadsafe at Y)@D\n"
+" Set the default death test style.\n"
+#endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
+" @G--" GTEST_FLAG_PREFIX_ "break_on_failure at D\n"
+" Turn assertion failures into debugger break-points.\n"
+" @G--" GTEST_FLAG_PREFIX_ "throw_on_failure at D\n"
+" Turn assertion failures into C++ exceptions.\n"
+" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0 at D\n"
+" Do not report exceptions as test failures. Instead, allow them\n"
+" to crash the program or throw a pop-up (on Windows).\n"
+"\n"
+"Except for @G--" GTEST_FLAG_PREFIX_ "list_tests at D, you can alternatively set "
+ "the corresponding\n"
+"environment variable of a flag (all letters in upper-case). For example, to\n"
+"disable colored text output, you can either specify @G--" GTEST_FLAG_PREFIX_
+ "color=no at D or set\n"
+"the @G" GTEST_FLAG_PREFIX_UPPER_ "COLOR at D environment variable to @Gno at D.\n"
+"\n"
+"For more information, please read the " GTEST_NAME_ " documentation at\n"
+"@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ "\n"
+"(not one in your own code or tests), please report it to\n"
+"@G<" GTEST_DEV_EMAIL_ ">@D.\n";
+
+// Parses the command line for Google Test flags, without initializing
+// other parts of Google Test. The type parameter CharType can be
+// instantiated to either char or wchar_t.
+template <typename CharType>
+void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) {
+ for (int i = 1; i < *argc; i++) {
+ const std::string arg_string = StreamableToString(argv[i]);
+ const char* const arg = arg_string.c_str();
+
+ using internal::ParseBoolFlag;
+ using internal::ParseInt32Flag;
+ using internal::ParseStringFlag;
+
+ // Do we see a Google Test flag?
+ if (ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag,
+ >EST_FLAG(also_run_disabled_tests)) ||
+ ParseBoolFlag(arg, kBreakOnFailureFlag,
+ >EST_FLAG(break_on_failure)) ||
+ ParseBoolFlag(arg, kCatchExceptionsFlag,
+ >EST_FLAG(catch_exceptions)) ||
+ ParseStringFlag(arg, kColorFlag, >EST_FLAG(color)) ||
+ ParseStringFlag(arg, kDeathTestStyleFlag,
+ >EST_FLAG(death_test_style)) ||
+ ParseBoolFlag(arg, kDeathTestUseFork,
+ >EST_FLAG(death_test_use_fork)) ||
+ ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) ||
+ ParseStringFlag(arg, kInternalRunDeathTestFlag,
+ >EST_FLAG(internal_run_death_test)) ||
+ ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) ||
+ ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) ||
+ ParseBoolFlag(arg, kPrintTimeFlag, >EST_FLAG(print_time)) ||
+ ParseInt32Flag(arg, kRandomSeedFlag, >EST_FLAG(random_seed)) ||
+ ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) ||
+ ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) ||
+ ParseInt32Flag(arg, kStackTraceDepthFlag,
+ >EST_FLAG(stack_trace_depth)) ||
+ ParseStringFlag(arg, kStreamResultToFlag,
+ >EST_FLAG(stream_result_to)) ||
+ ParseBoolFlag(arg, kThrowOnFailureFlag,
+ >EST_FLAG(throw_on_failure))
+ ) {
+ // Yes. Shift the remainder of the argv list left by one. Note
+ // that argv has (*argc + 1) elements, the last one always being
+ // NULL. The following loop moves the trailing NULL element as
+ // well.
+ for (int j = i; j != *argc; j++) {
+ argv[j] = argv[j + 1];
+ }
+
+ // Decrements the argument count.
+ (*argc)--;
+
+ // We also need to decrement the iterator as we just removed
+ // an element.
+ i--;
+ } else if (arg_string == "--help" || arg_string == "-h" ||
+ arg_string == "-?" || arg_string == "/?" ||
+ HasGoogleTestFlagPrefix(arg)) {
+ // Both help flag and unrecognized Google Test flags (excluding
+ // internal ones) trigger help display.
+ g_help_flag = true;
+ }
+ }
+
+ if (g_help_flag) {
+ // We print the help here instead of in RUN_ALL_TESTS(), as the
+ // latter may not be called at all if the user is using Google
+ // Test with another testing framework.
+ PrintColorEncoded(kColorEncodedHelpMessage);
+ }
+}
+
+// Parses the command line for Google Test flags, without initializing
+// other parts of Google Test.
+void ParseGoogleTestFlagsOnly(int* argc, char** argv) {
+ ParseGoogleTestFlagsOnlyImpl(argc, argv);
+}
+void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) {
+ ParseGoogleTestFlagsOnlyImpl(argc, argv);
+}
+
+// The internal implementation of InitGoogleTest().
+//
+// The type parameter CharType can be instantiated to either char or
+// wchar_t.
+template <typename CharType>
+void InitGoogleTestImpl(int* argc, CharType** argv) {
+ g_init_gtest_count++;
+
+ // We don't want to run the initialization code twice.
+ if (g_init_gtest_count != 1) return;
+
+ if (*argc <= 0) return;
+
+ internal::g_executable_path = internal::StreamableToString(argv[0]);
+
+#if GTEST_HAS_DEATH_TEST
+
+ g_argvs.clear();
+ for (int i = 0; i != *argc; i++) {
+ g_argvs.push_back(StreamableToString(argv[i]));
+ }
+
+#endif // GTEST_HAS_DEATH_TEST
+
+ ParseGoogleTestFlagsOnly(argc, argv);
+ GetUnitTestImpl()->PostFlagParsingInit();
+}
+
+} // namespace internal
+
+// Initializes Google Test. This must be called before calling
+// RUN_ALL_TESTS(). In particular, it parses a command line for the
+// flags that Google Test recognizes. Whenever a Google Test flag is
+// seen, it is removed from argv, and *argc is decremented.
+//
+// No value is returned. Instead, the Google Test flag variables are
+// updated.
+//
+// Calling the function for the second time has no user-visible effect.
+void InitGoogleTest(int* argc, char** argv) {
+ internal::InitGoogleTestImpl(argc, argv);
+}
+
+// This overloaded version can be used in Windows programs compiled in
+// UNICODE mode.
+void InitGoogleTest(int* argc, wchar_t** argv) {
+ internal::InitGoogleTestImpl(argc, argv);
+}
+
+} // namespace testing
diff --git a/test/gtest/src/gtest_main.cc b/test/gtest/src/gtest_main.cc
new file mode 100644
index 0000000..f302822
--- /dev/null
+++ b/test/gtest/src/gtest_main.cc
@@ -0,0 +1,38 @@
+// Copyright 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdio.h>
+
+#include "gtest/gtest.h"
+
+GTEST_API_ int main(int argc, char **argv) {
+ printf("Running main() from gtest_main.cc\n");
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt
new file mode 100644
index 0000000..ba38c2d
--- /dev/null
+++ b/test/integration/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(TEST_TYPE "INTEGRATION")
+
+set(tests
+)
+
+link_directories(${PROJECT_BINARY_DIR}/test)
+
+ign_build_tests(TYPE INTEGRATION SOURCES ${tests})
diff --git a/test/performance/CMakeLists.txt b/test/performance/CMakeLists.txt
new file mode 100644
index 0000000..7cf00db
--- /dev/null
+++ b/test/performance/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(TEST_TYPE "PERFORMANCE")
+
+set(tests
+)
+
+link_directories(${PROJECT_BINARY_DIR}/test)
+
+ign_build_tests(TYPE PERFORMANCE SOURCES ${tests})
diff --git a/test/regression/CMakeLists.txt b/test/regression/CMakeLists.txt
new file mode 100644
index 0000000..ebbc7da
--- /dev/null
+++ b/test/regression/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(TEST_TYPE "REGRESSION")
+
+set(tests
+)
+
+link_directories(${PROJECT_BINARY_DIR}/test)
+
+ign_build_tests(TYPE REGRESSION SOURCES ${tests})
diff --git a/test/test_config.h.in b/test/test_config.h.in
new file mode 100644
index 0000000..b6ba74a
--- /dev/null
+++ b/test/test_config.h.in
@@ -0,0 +1 @@
+#define PROJECT_SOURCE_PATH "${PROJECT_SOURCE_DIR}"
diff --git a/tools/check_test_ran.py b/tools/check_test_ran.py
new file mode 100755
index 0000000..4b64fc7
--- /dev/null
+++ b/tools/check_test_ran.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+# Software License Agreement (BSD License)
+#
+# Copyright (c) 2008, Willow Garage, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of Willow Garage, Inc. nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# Revision $Id: check_test_ran.py 16671 2012-04-27 16:15:28Z dthomas $
+
+"""
+Writes a test failure out to test file if it doesn't exist.
+"""
+
+# Adapted from rosunit/check_test_ran.py
+
+from __future__ import print_function
+NAME="check_test_ran.py"
+
+import os
+import sys
+
+def usage():
+ print("""Usage:
+\t%s test-file.xml
+"""%(NAME), file=sys.stderr)
+ print(sys.argv)
+ sys.exit(getattr(os, 'EX_USAGE', 1))
+
+def check_main():
+ if len(sys.argv) < 2:
+ usage()
+ test_file = sys.argv[1]
+
+ print("Checking for test results in %s"%test_file)
+
+ if not os.path.exists(test_file):
+ if not os.path.exists(os.path.dirname(test_file)):
+ os.makedirs(os.path.dirname(test_file))
+
+ print("Cannot find results, writing failure results to", test_file)
+
+ with open(test_file, 'w') as f:
+ test_name = os.path.basename(test_file)
+ d = {'test': test_name, 'test_file': test_file }
+ f.write("""<?xml version="1.0" encoding="UTF-8"?>
+<testsuite tests="1" failures="1" time="1" errors="0" name="%(test)s">
+ <testcase name="test_ran" status="run" time="1" classname="Results">
+ <failure message="Unable to find test results for %(test)s, test did not run.\nExpected results in %(test_file)s" type=""/>
+ </testcase>
+</testsuite>"""%d)
+
+if __name__ == '__main__':
+ check_main()
diff --git a/tools/code_check.sh b/tools/code_check.sh
new file mode 100644
index 0000000..42b03e3
--- /dev/null
+++ b/tools/code_check.sh
@@ -0,0 +1,150 @@
+#!/bin/sh
+
+# Jenkins will pass -xml, in which case we want to generate XML output
+xmlout=0
+if test "$1" = "-xmldir" -a -n "$2"; then
+ xmlout=1
+ xmldir=$2
+ mkdir -p $xmldir
+ rm -rf $xmldir/*.xml
+ # Assuming that Jenkins called, the `build` directory is sibling to src dir
+ builddir=../build
+else
+ # This is a heuristic guess; not everyone puts the `build` dir in the src dir
+ builddir=./build
+fi
+
+# Identify cppcheck version
+CPPCHECK_VERSION=`cppcheck --version | sed -e 's at Cppcheck @@'`
+CPPCHECK_LT_157=`echo "$CPPCHECK_VERSION 1.57" | \
+ awk '{if ($1 < $2) print 1; else print 0}'`
+
+QUICK_CHECK=0
+if test "$1" = "--quick"
+then
+ QUICK_CHECK=1
+ QUICK_SOURCE=$2
+ hg_root=`hg root`
+ if [ "$?" -ne "0" ] ; then
+ echo This is not an hg repository
+ exit
+ fi
+ cd $hg_root
+ hg log -r $QUICK_SOURCE > /dev/null
+ if [ "$?" -ne "0" ] ; then
+ echo $QUICK_SOURCE is not a valid changeset hash
+ exit
+ fi
+ CHECK_FILES=""
+ while read line; do
+ for f in $line; do
+ CHECK_FILES="$CHECK_FILES `echo $f | grep '\.[ch][ch]*$' | grep -v '^deps'`"
+ done
+ done
+ CPPCHECK_FILES="$CHECK_FILES"
+ CPPLINT_FILES="$CHECK_FILES"
+ QUICK_TMP=`mktemp -t asdfXXXXXXXXXX`
+else
+ CHECK_DIRS="./src ./include ./test/integration ./test/regression ./test/performance"
+ if [ $CPPCHECK_LT_157 -eq 1 ]; then
+ # cppcheck is older than 1.57, so don't check header files (issue #907)
+ CPPCHECK_FILES=`find $CHECK_DIRS -name "*.cc"`
+ else
+ CPPCHECK_FILES=`find $CHECK_DIRS -name "*.cc" -o -name "*.hh"`
+ fi
+ CPPLINT_FILES=`\
+ find $CHECK_DIRS -name "*.cc" -o -name "*.hh" -o -name "*.c" -o -name "*.h"`
+fi
+
+SUPPRESS=/tmp/cpp_check.suppress
+
+# The follow suppression is useful when checking for missing includes.
+# It's disable for now because checking for missing includes is very
+# time consuming. See CPPCHECK_CMD3.
+SUPPRESS=/tmp/gazebo_cpp_check.suppress
+# false positives related to explicit constructors where there is no
+# constructor declared
+echo "*:include/ignition/math/Vector2.hh:230" > $SUPPRESS
+echo "*:include/ignition/math/Vector3.hh:372" >> $SUPPRESS
+echo "*:include/ignition/math/Vector3.hh:433" >> $SUPPRESS
+echo "*:include/ignition/math/Vector3.hh:536" >> $SUPPRESS
+echo "*:include/ignition/math/Vector4.hh:257" >> $SUPPRESS
+echo "*:include/ignition/math/Vector4.hh:258" >> $SUPPRESS
+
+#cppcheck
+CPPCHECK_BASE="cppcheck -q --suppressions-list=$SUPPRESS --inline-suppr"
+if [ $CPPCHECK_LT_157 -eq 0 ]; then
+ # use --language argument if 1.57 or greater (issue #907)
+ CPPCHECK_BASE="$CPPCHECK_BASE --language=c++"
+fi
+CPPCHECK_INCLUDES="-I ./include -I $builddir -I test -I ./include/ignition/math"
+CPPCHECK_RULES="-DIGNITION_MATH_VISIBLE"\
+" --rule-file=./tools/cppcheck_rules/header_guard.rule"\
+" --rule-file=./tools/cppcheck_rules/namespace_AZ.rule"
+CPPCHECK_CMD1A="-j 4 --enable=style,performance,portability,information"
+CPPCHECK_CMD1B="$CPPCHECK_RULES $CPPCHECK_FILES"
+CPPCHECK_CMD1="$CPPCHECK_CMD1A $CPPCHECK_CMD1B"
+CPPCHECK_CMD2="--enable=unusedFunction $CPPCHECK_FILES"
+
+# Checking for missing includes is very time consuming. This is disabled
+# for now
+CPPCHECK_CMD3="-j 4 --enable=missingInclude $CPPCHECK_FILES $CPPCHECK_INCLUDES"
+# CPPCHECK_CMD3=""
+
+if [ $xmlout -eq 1 ]; then
+ # Performance, style, portability, and information
+ ($CPPCHECK_BASE --xml $CPPCHECK_CMD1) 2> $xmldir/cppcheck.xml
+
+ # Check the configuration
+ ($CPPCHECK_BASE --xml $CPPCHECK_CMD3) 2> $xmldir/cppcheck-configuration.xml
+elif [ $QUICK_CHECK -eq 1 ]; then
+ for f in $CHECK_FILES; do
+ prefix=`basename $f | sed -e 's@\..*$@@'`
+ ext=`echo $f | sed -e 's@^.*\.@@'`
+ tmp2="$QUICK_TMP"."$ext"
+ tmp2base=`basename "$QUICK_TMP"`
+ hg cat -r $QUICK_SOURCE $hg_root/$f > $tmp2
+
+ # Fix suppressions for tmp files
+ sed -i -e "s@$f@$tmp2@" $SUPPRESS
+
+ # Skip cppcheck for header files if cppcheck is old
+ DO_CPPCHECK=0
+ if [ $ext = 'cc' ]; then
+ DO_CPPCHECK=1
+ elif [ $CPPCHECK_LT_157 -eq 0 ]; then
+ DO_CPPCHECK=1
+ fi
+
+ if [ $DO_CPPCHECK -eq 1 ]; then
+ $CPPCHECK_BASE $CPPCHECK_CMD1A $CPPCHECK_RULES $tmp2 2>&1 \
+ | sed -e "s@$tmp2@$f at g" \
+ | grep -v 'use --check-config for details' \
+ | grep -v 'Include file: .*not found'
+ fi
+
+ # Undo changes to suppression file
+ sed -i -e "s@$tmp2@$f@" $SUPPRESS
+
+ python $hg_root/tools/cpplint.py $tmp2 2>&1 \
+ | sed -e "s@$tmp2@$f at g" -e "s@$tmp2base@$prefix at g" \
+ | grep -v 'Total errors found: 0'
+
+ rm $tmp2
+ done
+ rm $QUICK_TMP
+else
+ # Performance, style, portability, and information
+ $CPPCHECK_BASE $CPPCHECK_INCLUDES $CPPCHECK_CMD1 2>&1
+
+ # Check the configuration
+ $CPPCHECK_BASE $CPPCHECK_CMD3 2>&1
+fi
+
+# cpplint
+if [ $xmlout -eq 1 ]; then
+ (echo $CPPLINT_FILES | xargs python tools/cpplint.p --extensions=cc,hhy 2>&1) \
+ | python tools/cpplint_to_cppcheckxml.py 2> $xmldir/cpplint.xml
+elif [ $QUICK_CHECK -eq 0 ]; then
+ echo $CPPLINT_FILES | xargs python tools/cpplint.py --extensions=cc,hh 2>&1
+fi
diff --git a/tools/cppcheck_rules/header_guard.rule b/tools/cppcheck_rules/header_guard.rule
new file mode 100644
index 0000000..6607951
--- /dev/null
+++ b/tools/cppcheck_rules/header_guard.rule
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<rule version="1">
+ <tokenlist>define</tokenlist>
+ <pattern>#define _[A-Z]</pattern>
+ <message>
+ <id>_AZdefine</id>
+ <severity>warning</severity>
+ <summary>Definitions should not start with underscore and then a capital letter.</summary>
+ </message>
+</rule>
diff --git a/tools/cppcheck_rules/namespace_AZ.rule b/tools/cppcheck_rules/namespace_AZ.rule
new file mode 100644
index 0000000..75250d7
--- /dev/null
+++ b/tools/cppcheck_rules/namespace_AZ.rule
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<rule version="1">
+ <pattern>:: _[A-Z]</pattern>
+ <message>
+ <id>_AZnaming</id>
+ <severity>warning</severity>
+ <summary>Identifier that starts with underscore and then a capital letter is reserved.</summary>
+ </message>
+</rule>
diff --git a/tools/cpplint.py b/tools/cpplint.py
new file mode 100644
index 0000000..1b1e4cf
--- /dev/null
+++ b/tools/cpplint.py
@@ -0,0 +1,5622 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2009 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Does google-lint on c++ files.
+
+The goal of this script is to identify places in the code that *may*
+be in non-compliance with google style. It does not attempt to fix
+up these problems -- the point is to educate. It does also not
+attempt to find all problems, or to ensure that everything it does
+find is legitimately a problem.
+
+In particular, we can get very confused by /* and // inside strings!
+We do a small hack, which is to ignore //'s with "'s after them on the
+same line, but it is far from perfect (in either direction).
+"""
+
+import codecs
+import copy
+import getopt
+import math # for log
+import os
+import re
+import sre_compile
+import string
+import sys
+import unicodedata
+
+
+_USAGE = """
+Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
+ [--counting=total|toplevel|detailed] [--root=subdir]
+ [--linelength=digits]
+ <file> [file] ...
+
+ The style guidelines this tries to follow are those in
+ http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml
+
+ Every problem is given a confidence score from 1-5, with 5 meaning we are
+ certain of the problem, and 1 meaning it could be a legitimate construct.
+ This will miss some errors, and is not a substitute for a code review.
+
+ To suppress false-positive errors of a certain category, add a
+ 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*)
+ suppresses errors of all categories on that line.
+
+ The files passed in will be linted; at least one file must be provided.
+ Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the
+ extensions with the --extensions flag.
+
+ Flags:
+
+ output=vs7
+ By default, the output is formatted to ease emacs parsing. Visual Studio
+ compatible output (vs7) may also be used. Other formats are unsupported.
+
+ verbose=#
+ Specify a number 0-5 to restrict errors to certain verbosity levels.
+
+ filter=-x,+y,...
+ Specify a comma-separated list of category-filters to apply: only
+ error messages whose category names pass the filters will be printed.
+ (Category names are printed with the message and look like
+ "[whitespace/indent]".) Filters are evaluated left to right.
+ "-FOO" and "FOO" means "do not print categories that start with FOO".
+ "+FOO" means "do print categories that start with FOO".
+
+ Examples: --filter=-whitespace,+whitespace/braces
+ --filter=whitespace,runtime/printf,+runtime/printf_format
+ --filter=-,+build/include_what_you_use
+
+ To see a list of all the categories used in cpplint, pass no arg:
+ --filter=
+
+ counting=total|toplevel|detailed
+ The total number of errors found is always printed. If
+ 'toplevel' is provided, then the count of errors in each of
+ the top-level categories like 'build' and 'whitespace' will
+ also be printed. If 'detailed' is provided, then a count
+ is provided for each category like 'build/class'.
+
+ root=subdir
+ The root directory used for deriving header guard CPP variable.
+ By default, the header guard CPP variable is calculated as the relative
+ path to the directory that contains .git, .hg, or .svn. When this flag
+ is specified, the relative path is calculated from the specified
+ directory. If the specified directory does not exist, this flag is
+ ignored.
+
+ Examples:
+ Assuming that src/.git exists, the header guard CPP variables for
+ src/chrome/browser/ui/browser.h are:
+
+ No flag => CHROME_BROWSER_UI_BROWSER_H_
+ --root=chrome => BROWSER_UI_BROWSER_H_
+ --root=chrome/browser => UI_BROWSER_H_
+
+ linelength=digits
+ This is the allowed line length for the project. The default value is
+ 80 characters.
+
+ Examples:
+ --linelength=120
+
+ extensions=extension,extension,...
+ The allowed file extensions that cpplint will check
+
+ Examples:
+ --extensions=hpp,cpp
+"""
+
+# We categorize each error message we print. Here are the categories.
+# We want an explicit list so we can list them all in cpplint --filter=.
+# If you add a new error message with a new category, add it to the list
+# here! cpplint_unittest.py should tell you if you forget to do this.
+_ERROR_CATEGORIES = [
+ 'build/class',
+ 'build/c++11',
+ 'build/deprecated',
+ 'build/endif_comment',
+ 'build/explicit_make_pair',
+ 'build/forward_decl',
+ 'build/header_guard',
+ 'build/include',
+ 'build/include_alpha',
+ 'build/include_order',
+ 'build/include_what_you_use',
+ 'build/namespaces',
+ 'build/printf_format',
+ 'build/storage_class',
+ 'legal/copyright',
+ 'readability/alt_tokens',
+ 'readability/braces',
+ 'readability/casting',
+ 'readability/check',
+ 'readability/constructors',
+ 'readability/fn_size',
+ 'readability/function',
+ 'readability/multiline_comment',
+ 'readability/multiline_string',
+ 'readability/namespace',
+ 'readability/nolint',
+ 'readability/nul',
+ 'readability/streams',
+ 'readability/todo',
+ 'readability/utf8',
+ 'runtime/arrays',
+ 'runtime/casting',
+ 'runtime/explicit',
+ 'runtime/int',
+ 'runtime/init',
+ 'runtime/invalid_increment',
+ 'runtime/member_string_references',
+ 'runtime/memset',
+ 'runtime/operator',
+ 'runtime/printf',
+ 'runtime/printf_format',
+ 'runtime/references',
+ 'runtime/string',
+ 'runtime/threadsafe_fn',
+ 'runtime/vlog',
+ 'whitespace/blank_line',
+ 'whitespace/braces',
+ 'whitespace/comma',
+ 'whitespace/comments',
+ 'whitespace/empty_conditional_body',
+ 'whitespace/empty_loop_body',
+ 'whitespace/end_of_line',
+ 'whitespace/ending_newline',
+ 'whitespace/forcolon',
+ 'whitespace/indent',
+ 'whitespace/line_length',
+ 'whitespace/newline',
+ 'whitespace/operators',
+ 'whitespace/parens',
+ 'whitespace/semicolon',
+ 'whitespace/tab',
+ 'whitespace/todo'
+ ]
+
+# The default state of the category filter. This is overridden by the --filter=
+# flag. By default all errors are on, so only add here categories that should be
+# off by default (i.e., categories that must be enabled by the --filter= flags).
+# All entries here should start with a '-' or '+', as in the --filter= flag.
+_DEFAULT_FILTERS = ['-build/include_alpha']
+
+# We used to check for high-bit characters, but after much discussion we
+# decided those were OK, as long as they were in UTF-8 and didn't represent
+# hard-coded international strings, which belong in a separate i18n file.
+
+# C++ headers
+_CPP_HEADERS = frozenset([
+ # Legacy
+ 'algobase.h',
+ 'algo.h',
+ 'alloc.h',
+ 'builtinbuf.h',
+ 'bvector.h',
+ 'complex.h',
+ 'defalloc.h',
+ 'deque.h',
+ 'editbuf.h',
+ 'fstream.h',
+ 'function.h',
+ 'hash_map',
+ 'hash_map.h',
+ 'hash_set',
+ 'hash_set.h',
+ 'hashtable.h',
+ 'heap.h',
+ 'indstream.h',
+ 'iomanip.h',
+ 'iostream.h',
+ 'istream.h',
+ 'iterator.h',
+ 'list.h',
+ 'map.h',
+ 'multimap.h',
+ 'multiset.h',
+ 'ostream.h',
+ 'pair.h',
+ 'parsestream.h',
+ 'pfstream.h',
+ 'procbuf.h',
+ 'pthread_alloc',
+ 'pthread_alloc.h',
+ 'rope',
+ 'rope.h',
+ 'ropeimpl.h',
+ 'set.h',
+ 'slist',
+ 'slist.h',
+ 'stack.h',
+ 'stdiostream.h',
+ 'stl_alloc.h',
+ 'stl_relops.h',
+ 'streambuf.h',
+ 'stream.h',
+ 'strfile.h',
+ 'strstream.h',
+ 'tempbuf.h',
+ 'tree.h',
+ 'type_traits.h',
+ 'vector.h',
+ # 17.6.1.2 C++ library headers
+ 'algorithm',
+ 'array',
+ 'atomic',
+ 'bitset',
+ 'chrono',
+ 'codecvt',
+ 'complex',
+ 'condition_variable',
+ 'deque',
+ 'exception',
+ 'forward_list',
+ 'fstream',
+ 'functional',
+ 'future',
+ 'initializer_list',
+ 'iomanip',
+ 'ios',
+ 'iosfwd',
+ 'iostream',
+ 'istream',
+ 'iterator',
+ 'limits',
+ 'list',
+ 'locale',
+ 'map',
+ 'memory',
+ 'mutex',
+ 'new',
+ 'numeric',
+ 'ostream',
+ 'queue',
+ 'random',
+ 'ratio',
+ 'regex',
+ 'set',
+ 'sstream',
+ 'stack',
+ 'stdexcept',
+ 'streambuf',
+ 'string',
+ 'strstream',
+ 'system_error',
+ 'thread',
+ 'tuple',
+ 'typeindex',
+ 'typeinfo',
+ 'type_traits',
+ 'unordered_map',
+ 'unordered_set',
+ 'utility',
+ 'valarray',
+ 'vector',
+ # 17.6.1.2 C++ headers for C library facilities
+ 'cassert',
+ 'ccomplex',
+ 'cctype',
+ 'cerrno',
+ 'cfenv',
+ 'cfloat',
+ 'cinttypes',
+ 'ciso646',
+ 'climits',
+ 'clocale',
+ 'cmath',
+ 'csetjmp',
+ 'csignal',
+ 'cstdalign',
+ 'cstdarg',
+ 'cstdbool',
+ 'cstddef',
+ 'cstdint',
+ 'cstdio',
+ 'cstdlib',
+ 'cstring',
+ 'ctgmath',
+ 'ctime',
+ 'cuchar',
+ 'cwchar',
+ 'cwctype',
+ ])
+
+
+# Assertion macros. These are defined in base/logging.h and
+# testing/base/gunit.h. Note that the _M versions need to come first
+# for substring matching to work.
+_CHECK_MACROS = [
+ 'DCHECK', 'CHECK',
+ 'EXPECT_TRUE_M', 'EXPECT_TRUE',
+ 'ASSERT_TRUE_M', 'ASSERT_TRUE',
+ 'EXPECT_FALSE_M', 'EXPECT_FALSE',
+ 'ASSERT_FALSE_M', 'ASSERT_FALSE',
+ ]
+
+# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE
+_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS])
+
+for op, replacement in [('==', 'EQ'), ('!=', 'NE'),
+ ('>=', 'GE'), ('>', 'GT'),
+ ('<=', 'LE'), ('<', 'LT')]:
+ _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement
+ _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement
+ _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement
+ _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement
+ _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement
+ _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement
+
+for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'),
+ ('>=', 'LT'), ('>', 'LE'),
+ ('<=', 'GT'), ('<', 'GE')]:
+ _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement
+ _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement
+ _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement
+ _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement
+
+# Alternative tokens and their replacements. For full list, see section 2.5
+# Alternative tokens [lex.digraph] in the C++ standard.
+#
+# Digraphs (such as '%:') are not included here since it's a mess to
+# match those on a word boundary.
+_ALT_TOKEN_REPLACEMENT = {
+ 'and': '&&',
+ 'bitor': '|',
+ 'or': '||',
+ 'xor': '^',
+ 'compl': '~',
+ 'bitand': '&',
+ 'and_eq': '&=',
+ 'or_eq': '|=',
+ 'xor_eq': '^=',
+ 'not': '!',
+ 'not_eq': '!='
+ }
+
+# Compile regular expression that matches all the above keywords. The "[ =()]"
+# bit is meant to avoid matching these keywords outside of boolean expressions.
+#
+# False positives include C-style multi-line comments and multi-line strings
+# but those have always been troublesome for cpplint.
+_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile(
+ r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)')
+
+
+# These constants define types of headers for use with
+# _IncludeState.CheckNextIncludeOrder().
+_C_SYS_HEADER = 1
+_CPP_SYS_HEADER = 2
+_LIKELY_MY_HEADER = 3
+_POSSIBLE_MY_HEADER = 4
+_OTHER_HEADER = 5
+
+# These constants define the current inline assembly state
+_NO_ASM = 0 # Outside of inline assembly block
+_INSIDE_ASM = 1 # Inside inline assembly block
+_END_ASM = 2 # Last line of inline assembly block
+_BLOCK_ASM = 3 # The whole block is an inline assembly block
+
+# Match start of assembly blocks
+_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)'
+ r'(?:\s+(volatile|__volatile__))?'
+ r'\s*[{(]')
+
+
+_regexp_compile_cache = {}
+
+# Finds occurrences of NOLINT or NOLINT(...).
+_RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?')
+
+# {str, set(int)}: a map from error categories to sets of linenumbers
+# on which those errors are expected and should be suppressed.
+_error_suppressions = {}
+
+# The root directory used for deriving header guard CPP variable.
+# This is set by --root flag.
+_root = None
+
+# The allowed line length of files.
+# This is set by --linelength flag.
+_line_length = 80
+
+# The allowed extensions for file names
+# This is set by --extensions flag.
+_valid_extensions = set(['cc', 'hh', 'h', 'cpp', 'cu', 'cuh'])
+
+def ParseNolintSuppressions(filename, raw_line, linenum, error):
+ """Updates the global list of error-suppressions.
+
+ Parses any NOLINT comments on the current line, updating the global
+ error_suppressions store. Reports an error if the NOLINT comment
+ was malformed.
+
+ Args:
+ filename: str, the name of the input file.
+ raw_line: str, the line of input text, with comments.
+ linenum: int, the number of the current line.
+ error: function, an error handler.
+ """
+ # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*).
+ matched = _RE_SUPPRESSION.search(raw_line)
+ if matched:
+ category = matched.group(1)
+ if category in (None, '(*)'): # => "suppress all"
+ _error_suppressions.setdefault(None, set()).add(linenum)
+ else:
+ if category.startswith('(') and category.endswith(')'):
+ category = category[1:-1]
+ if category in _ERROR_CATEGORIES:
+ _error_suppressions.setdefault(category, set()).add(linenum)
+ else:
+ error(filename, linenum, 'readability/nolint', 5,
+ 'Unknown NOLINT error category: %s' % category)
+
+
+def ResetNolintSuppressions():
+ "Resets the set of NOLINT suppressions to empty."
+ _error_suppressions.clear()
+
+
+def IsErrorSuppressedByNolint(category, linenum):
+ """Returns true if the specified error category is suppressed on this line.
+
+ Consults the global error_suppressions map populated by
+ ParseNolintSuppressions/ResetNolintSuppressions.
+
+ Args:
+ category: str, the category of the error.
+ linenum: int, the current line number.
+ Returns:
+ bool, True iff the error should be suppressed due to a NOLINT comment.
+ """
+ return (linenum in _error_suppressions.get(category, set()) or
+ linenum in _error_suppressions.get(None, set()))
+
+
+def Match(pattern, s):
+ """Matches the string with the pattern, caching the compiled regexp."""
+ # The regexp compilation caching is inlined in both Match and Search for
+ # performance reasons; factoring it out into a separate function turns out
+ # to be noticeably expensive.
+ if pattern not in _regexp_compile_cache:
+ _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
+ return _regexp_compile_cache[pattern].match(s)
+
+
+def ReplaceAll(pattern, rep, s):
+ """Replaces instances of pattern in a string with a replacement.
+
+ The compiled regex is kept in a cache shared by Match and Search.
+
+ Args:
+ pattern: regex pattern
+ rep: replacement text
+ s: search string
+
+ Returns:
+ string with replacements made (or original string if no replacements)
+ """
+ if pattern not in _regexp_compile_cache:
+ _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
+ return _regexp_compile_cache[pattern].sub(rep, s)
+
+
+def Search(pattern, s):
+ """Searches the string for the pattern, caching the compiled regexp."""
+ if pattern not in _regexp_compile_cache:
+ _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
+ return _regexp_compile_cache[pattern].search(s)
+
+
+class _IncludeState(dict):
+ """Tracks line numbers for includes, and the order in which includes appear.
+
+ As a dict, an _IncludeState object serves as a mapping between include
+ filename and line number on which that file was included.
+
+ Call CheckNextIncludeOrder() once for each header in the file, passing
+ in the type constants defined above. Calls in an illegal order will
+ raise an _IncludeError with an appropriate error message.
+
+ """
+ # self._section will move monotonically through this set. If it ever
+ # needs to move backwards, CheckNextIncludeOrder will raise an error.
+ _INITIAL_SECTION = 0
+ _MY_H_SECTION = 1
+ _C_SECTION = 2
+ _CPP_SECTION = 3
+ _OTHER_H_SECTION = 4
+
+ _TYPE_NAMES = {
+ _C_SYS_HEADER: 'C system header',
+ _CPP_SYS_HEADER: 'C++ system header',
+ _LIKELY_MY_HEADER: 'header this file implements',
+ _POSSIBLE_MY_HEADER: 'header this file may implement',
+ _OTHER_HEADER: 'other header',
+ }
+ _SECTION_NAMES = {
+ _INITIAL_SECTION: "... nothing. (This can't be an error.)",
+ _MY_H_SECTION: 'a header this file implements',
+ _C_SECTION: 'C system header',
+ _CPP_SECTION: 'C++ system header',
+ _OTHER_H_SECTION: 'other header',
+ }
+
+ def __init__(self):
+ dict.__init__(self)
+ self.ResetSection()
+
+ def ResetSection(self):
+ # The name of the current section.
+ self._section = self._INITIAL_SECTION
+ # The path of last found header.
+ self._last_header = ''
+
+ def SetLastHeader(self, header_path):
+ self._last_header = header_path
+
+ def CanonicalizeAlphabeticalOrder(self, header_path):
+ """Returns a path canonicalized for alphabetical comparison.
+
+ - replaces "-" with "_" so they both cmp the same.
+ - removes '-inl' since we don't require them to be after the main header.
+ - lowercase everything, just in case.
+
+ Args:
+ header_path: Path to be canonicalized.
+
+ Returns:
+ Canonicalized path.
+ """
+ return header_path.replace('-inl.h', '.h').replace('-', '_').lower()
+
+ def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path):
+ """Check if a header is in alphabetical order with the previous header.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ header_path: Canonicalized header to be checked.
+
+ Returns:
+ Returns true if the header is in alphabetical order.
+ """
+ # If previous section is different from current section, _last_header will
+ # be reset to empty string, so it's always less than current header.
+ #
+ # If previous line was a blank line, assume that the headers are
+ # intentionally sorted the way they are.
+ if (self._last_header > header_path and
+ not Match(r'^\s*$', clean_lines.elided[linenum - 1])):
+ return False
+ return True
+
+ def CheckNextIncludeOrder(self, header_type):
+ """Returns a non-empty error message if the next header is out of order.
+
+ This function also updates the internal state to be ready to check
+ the next include.
+
+ Args:
+ header_type: One of the _XXX_HEADER constants defined above.
+
+ Returns:
+ The empty string if the header is in the right order, or an
+ error message describing what's wrong.
+
+ """
+ error_message = ('Found %s after %s' %
+ (self._TYPE_NAMES[header_type],
+ self._SECTION_NAMES[self._section]))
+
+ last_section = self._section
+
+ if header_type == _C_SYS_HEADER:
+ if self._section <= self._C_SECTION:
+ self._section = self._C_SECTION
+ else:
+ self._last_header = ''
+ return error_message
+ elif header_type == _CPP_SYS_HEADER:
+ if self._section <= self._CPP_SECTION:
+ self._section = self._CPP_SECTION
+ else:
+ self._last_header = ''
+ return error_message
+ elif header_type == _LIKELY_MY_HEADER:
+ if self._section <= self._MY_H_SECTION:
+ self._section = self._MY_H_SECTION
+ else:
+ self._section = self._OTHER_H_SECTION
+ elif header_type == _POSSIBLE_MY_HEADER:
+ if self._section <= self._MY_H_SECTION:
+ self._section = self._MY_H_SECTION
+ else:
+ # This will always be the fallback because we're not sure
+ # enough that the header is associated with this file.
+ self._section = self._OTHER_H_SECTION
+ else:
+ assert header_type == _OTHER_HEADER
+ self._section = self._OTHER_H_SECTION
+
+ if last_section != self._section:
+ self._last_header = ''
+
+ return ''
+
+
+class _CppLintState(object):
+ """Maintains module-wide state.."""
+
+ def __init__(self):
+ self.verbose_level = 1 # global setting.
+ self.error_count = 0 # global count of reported errors
+ # filters to apply when emitting error messages
+ self.filters = _DEFAULT_FILTERS[:]
+ self.counting = 'total' # In what way are we counting errors?
+ self.errors_by_category = {} # string to int dict storing error counts
+
+ # output format:
+ # "emacs" - format that emacs can parse (default)
+ # "vs7" - format that Microsoft Visual Studio 7 can parse
+ self.output_format = 'emacs'
+
+ def SetOutputFormat(self, output_format):
+ """Sets the output format for errors."""
+ self.output_format = output_format
+
+ def SetVerboseLevel(self, level):
+ """Sets the module's verbosity, and returns the previous setting."""
+ last_verbose_level = self.verbose_level
+ self.verbose_level = level
+ return last_verbose_level
+
+ def SetCountingStyle(self, counting_style):
+ """Sets the module's counting options."""
+ self.counting = counting_style
+
+ def SetFilters(self, filters):
+ """Sets the error-message filters.
+
+ These filters are applied when deciding whether to emit a given
+ error message.
+
+ Args:
+ filters: A string of comma-separated filters (eg "+whitespace/indent").
+ Each filter should start with + or -; else we die.
+
+ Raises:
+ ValueError: The comma-separated filters did not all start with '+' or '-'.
+ E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter"
+ """
+ # Default filters always have less priority than the flag ones.
+ self.filters = _DEFAULT_FILTERS[:]
+ for filt in filters.split(','):
+ clean_filt = filt.strip()
+ if clean_filt:
+ self.filters.append(clean_filt)
+ for filt in self.filters:
+ if not (filt.startswith('+') or filt.startswith('-')):
+ raise ValueError('Every filter in --filters must start with + or -'
+ ' (%s does not)' % filt)
+
+ def ResetErrorCounts(self):
+ """Sets the module's error statistic back to zero."""
+ self.error_count = 0
+ self.errors_by_category = {}
+
+ def IncrementErrorCount(self, category):
+ """Bumps the module's error statistic."""
+ self.error_count += 1
+ if self.counting in ('toplevel', 'detailed'):
+ if self.counting != 'detailed':
+ category = category.split('/')[0]
+ if category not in self.errors_by_category:
+ self.errors_by_category[category] = 0
+ self.errors_by_category[category] += 1
+
+ def PrintErrorCounts(self):
+ """Print a summary of errors by category, and the total."""
+ for category, count in self.errors_by_category.iteritems():
+ sys.stderr.write('Category \'%s\' errors found: %d\n' %
+ (category, count))
+ sys.stderr.write('Total errors found: %d\n' % self.error_count)
+
+_cpplint_state = _CppLintState()
+
+
+def _OutputFormat():
+ """Gets the module's output format."""
+ return _cpplint_state.output_format
+
+
+def _SetOutputFormat(output_format):
+ """Sets the module's output format."""
+ _cpplint_state.SetOutputFormat(output_format)
+
+
+def _VerboseLevel():
+ """Returns the module's verbosity setting."""
+ return _cpplint_state.verbose_level
+
+
+def _SetVerboseLevel(level):
+ """Sets the module's verbosity, and returns the previous setting."""
+ return _cpplint_state.SetVerboseLevel(level)
+
+
+def _SetCountingStyle(level):
+ """Sets the module's counting options."""
+ _cpplint_state.SetCountingStyle(level)
+
+
+def _Filters():
+ """Returns the module's list of output filters, as a list."""
+ return _cpplint_state.filters
+
+
+def _SetFilters(filters):
+ """Sets the module's error-message filters.
+
+ These filters are applied when deciding whether to emit a given
+ error message.
+
+ Args:
+ filters: A string of comma-separated filters (eg "whitespace/indent").
+ Each filter should start with + or -; else we die.
+ """
+ _cpplint_state.SetFilters(filters)
+
+
+class _FunctionState(object):
+ """Tracks current function name and the number of lines in its body."""
+
+ _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc.
+ _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER.
+
+ def __init__(self):
+ self.in_a_function = False
+ self.lines_in_function = 0
+ self.current_function = ''
+
+ def Begin(self, function_name):
+ """Start analyzing function body.
+
+ Args:
+ function_name: The name of the function being tracked.
+ """
+ self.in_a_function = True
+ self.lines_in_function = 0
+ self.current_function = function_name
+
+ def Count(self):
+ """Count line in current function body."""
+ if self.in_a_function:
+ self.lines_in_function += 1
+
+ def Check(self, error, filename, linenum):
+ """Report if too many lines in function body.
+
+ Args:
+ error: The function to call with any errors found.
+ filename: The name of the current file.
+ linenum: The number of the line to check.
+ """
+ if Match(r'T(EST|est)', self.current_function):
+ base_trigger = self._TEST_TRIGGER
+ else:
+ base_trigger = self._NORMAL_TRIGGER
+ trigger = base_trigger * 2**_VerboseLevel()
+
+ if self.lines_in_function > trigger:
+ error_level = int(math.log(self.lines_in_function / base_trigger, 2))
+ # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ...
+ if error_level > 5:
+ error_level = 5
+ error(filename, linenum, 'readability/fn_size', error_level,
+ 'Small and focused functions are preferred:'
+ ' %s has %d non-comment lines'
+ ' (error triggered by exceeding %d lines).' % (
+ self.current_function, self.lines_in_function, trigger))
+
+ def End(self):
+ """Stop analyzing function body."""
+ self.in_a_function = False
+
+
+class _IncludeError(Exception):
+ """Indicates a problem with the include order in a file."""
+ pass
+
+
+class FileInfo:
+ """Provides utility functions for filenames.
+
+ FileInfo provides easy access to the components of a file's path
+ relative to the project root.
+ """
+
+ def __init__(self, filename):
+ self._filename = filename
+
+ def FullName(self):
+ """Make Windows paths like Unix."""
+ return os.path.abspath(self._filename).replace('\\', '/')
+
+ def RepositoryName(self):
+ """FullName after removing the local path to the repository.
+
+ If we have a real absolute path name here we can try to do something smart:
+ detecting the root of the checkout and truncating /path/to/checkout from
+ the name so that we get header guards that don't include things like
+ "C:\Documents and Settings\..." or "/home/username/..." in them and thus
+ people on different computers who have checked the source out to different
+ locations won't see bogus errors.
+ """
+ fullname = self.FullName()
+
+ if os.path.exists(fullname):
+ project_dir = os.path.dirname(fullname)
+
+ if os.path.exists(os.path.join(project_dir, ".svn")):
+ # If there's a .svn file in the current directory, we recursively look
+ # up the directory tree for the top of the SVN checkout
+ root_dir = project_dir
+ one_up_dir = os.path.dirname(root_dir)
+ while os.path.exists(os.path.join(one_up_dir, ".svn")):
+ root_dir = os.path.dirname(root_dir)
+ one_up_dir = os.path.dirname(one_up_dir)
+
+ prefix = os.path.commonprefix([root_dir, project_dir])
+ return fullname[len(prefix) + 1:]
+
+ # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by
+ # searching up from the current path.
+ root_dir = os.path.dirname(fullname)
+ while (root_dir != os.path.dirname(root_dir) and
+ not os.path.exists(os.path.join(root_dir, ".git")) and
+ not os.path.exists(os.path.join(root_dir, ".hg")) and
+ not os.path.exists(os.path.join(root_dir, ".svn"))):
+ root_dir = os.path.dirname(root_dir)
+
+ if (os.path.exists(os.path.join(root_dir, ".git")) or
+ os.path.exists(os.path.join(root_dir, ".hg")) or
+ os.path.exists(os.path.join(root_dir, ".svn"))):
+ prefix = os.path.commonprefix([root_dir, project_dir])
+ return fullname[len(prefix) + 1:]
+
+ # Don't know what to do; header guard warnings may be wrong...
+ return fullname
+
+ def Split(self):
+ """Splits the file into the directory, basename, and extension.
+
+ For 'chrome/browser/browser.cc', Split() would
+ return ('chrome/browser', 'browser', '.cc')
+
+ Returns:
+ A tuple of (directory, basename, extension).
+ """
+
+ googlename = self.RepositoryName()
+ project, rest = os.path.split(googlename)
+ return (project,) + os.path.splitext(rest)
+
+ def BaseName(self):
+ """File base name - text after the final slash, before the final period."""
+ return self.Split()[1]
+
+ def Extension(self):
+ """File extension - text following the final period."""
+ return self.Split()[2]
+
+ def NoExtension(self):
+ """File has no source file extension."""
+ return '/'.join(self.Split()[0:2])
+
+ def IsSource(self):
+ """File has a source file extension."""
+ return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx')
+
+
+def _ShouldPrintError(category, confidence, linenum):
+ """If confidence >= verbose, category passes filter and is not suppressed."""
+
+ # There are three ways we might decide not to print an error message:
+ # a "NOLINT(category)" comment appears in the source,
+ # the verbosity level isn't high enough, or the filters filter it out.
+ if IsErrorSuppressedByNolint(category, linenum):
+ return False
+
+ if confidence < _cpplint_state.verbose_level:
+ return False
+
+ is_filtered = False
+ for one_filter in _Filters():
+ if one_filter.startswith('-'):
+ if category.startswith(one_filter[1:]):
+ is_filtered = True
+ elif one_filter.startswith('+'):
+ if category.startswith(one_filter[1:]):
+ is_filtered = False
+ else:
+ assert False # should have been checked for in SetFilter.
+ if is_filtered:
+ return False
+
+ return True
+
+
+def Error(filename, linenum, category, confidence, message):
+ """Logs the fact we've found a lint error.
+
+ We log where the error was found, and also our confidence in the error,
+ that is, how certain we are this is a legitimate style regression, and
+ not a misidentification or a use that's sometimes justified.
+
+ False positives can be suppressed by the use of
+ "cpplint(category)" comments on the offending line. These are
+ parsed into _error_suppressions.
+
+ Args:
+ filename: The name of the file containing the error.
+ linenum: The number of the line containing the error.
+ category: A string used to describe the "category" this bug
+ falls under: "whitespace", say, or "runtime". Categories
+ may have a hierarchy separated by slashes: "whitespace/indent".
+ confidence: A number from 1-5 representing a confidence score for
+ the error, with 5 meaning that we are certain of the problem,
+ and 1 meaning that it could be a legitimate construct.
+ message: The error message.
+ """
+ if _ShouldPrintError(category, confidence, linenum):
+ _cpplint_state.IncrementErrorCount(category)
+ if _cpplint_state.output_format == 'vs7':
+ sys.stderr.write('%s(%s): %s [%s] [%d]\n' % (
+ filename, linenum, message, category, confidence))
+ elif _cpplint_state.output_format == 'eclipse':
+ sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % (
+ filename, linenum, message, category, confidence))
+ else:
+ sys.stderr.write('%s:%s: %s [%s] [%d]\n' % (
+ filename, linenum, message, category, confidence))
+
+
+# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard.
+_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile(
+ r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)')
+# Match a single C style comment on the same line.
+_RE_PATTERN_C_COMMENTS = r'/\*(?:[^*]|\*(?!/))*\*/'
+# Matches multi-line C style comments.
+# This RE is a little bit more complicated than one might expect, because we
+# have to take care of space removals tools so we can handle comments inside
+# statements better.
+# The current rule is: We only clear spaces from both sides when we're at the
+# end of the line. Otherwise, we try to remove spaces from the right side,
+# if this doesn't work we try on left side but only if there's a non-character
+# on the right.
+_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile(
+ r'(\s*' + _RE_PATTERN_C_COMMENTS + r'\s*$|' +
+ _RE_PATTERN_C_COMMENTS + r'\s+|' +
+ r'\s+' + _RE_PATTERN_C_COMMENTS + r'(?=\W)|' +
+ _RE_PATTERN_C_COMMENTS + r')')
+
+
+def IsCppString(line):
+ """Does line terminate so, that the next symbol is in string constant.
+
+ This function does not consider single-line nor multi-line comments.
+
+ Args:
+ line: is a partial line of code starting from the 0..n.
+
+ Returns:
+ True, if next character appended to 'line' is inside a
+ string constant.
+ """
+
+ line = line.replace(r'\\', 'XX') # after this, \\" does not match to \"
+ return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1
+
+
+def CleanseRawStrings(raw_lines):
+ """Removes C++11 raw strings from lines.
+
+ Before:
+ static const char kData[] = R"(
+ multi-line string
+ )";
+
+ After:
+ static const char kData[] = ""
+ (replaced by blank line)
+ "";
+
+ Args:
+ raw_lines: list of raw lines.
+
+ Returns:
+ list of lines with C++11 raw strings replaced by empty strings.
+ """
+
+ delimiter = None
+ lines_without_raw_strings = []
+ for line in raw_lines:
+ if delimiter:
+ # Inside a raw string, look for the end
+ end = line.find(delimiter)
+ if end >= 0:
+ # Found the end of the string, match leading space for this
+ # line and resume copying the original lines, and also insert
+ # a "" on the last line.
+ leading_space = Match(r'^(\s*)\S', line)
+ line = leading_space.group(1) + '""' + line[end + len(delimiter):]
+ delimiter = None
+ else:
+ # Haven't found the end yet, append a blank line.
+ line = '""'
+
+ # Look for beginning of a raw string, and replace them with
+ # empty strings. This is done in a loop to handle multiple raw
+ # strings on the same line.
+ while delimiter is None:
+ # Look for beginning of a raw string.
+ # See 2.14.15 [lex.string] for syntax.
+ matched = Match(r'^(.*)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line)
+ if matched:
+ delimiter = ')' + matched.group(2) + '"'
+
+ end = matched.group(3).find(delimiter)
+ if end >= 0:
+ # Raw string ended on same line
+ line = (matched.group(1) + '""' +
+ matched.group(3)[end + len(delimiter):])
+ delimiter = None
+ else:
+ # Start of a multi-line raw string
+ line = matched.group(1) + '""'
+ else:
+ break
+
+ lines_without_raw_strings.append(line)
+
+ # TODO(unknown): if delimiter is not None here, we might want to
+ # emit a warning for unterminated string.
+ return lines_without_raw_strings
+
+
+def FindNextMultiLineCommentStart(lines, lineix):
+ """Find the beginning marker for a multiline comment."""
+ while lineix < len(lines):
+ if lines[lineix].strip().startswith('/*'):
+ # Only return this marker if the comment goes beyond this line
+ if lines[lineix].strip().find('*/', 2) < 0:
+ return lineix
+ lineix += 1
+ return len(lines)
+
+
+def FindNextMultiLineCommentEnd(lines, lineix):
+ """We are inside a comment, find the end marker."""
+ while lineix < len(lines):
+ if lines[lineix].strip().endswith('*/'):
+ return lineix
+ lineix += 1
+ return len(lines)
+
+
+def RemoveMultiLineCommentsFromRange(lines, begin, end):
+ """Clears a range of lines for multi-line comments."""
+ # Having // dummy comments makes the lines non-empty, so we will not get
+ # unnecessary blank line warnings later in the code.
+ for i in range(begin, end):
+ lines[i] = '// dummy'
+
+
+def RemoveMultiLineComments(filename, lines, error):
+ """Removes multiline (c-style) comments from lines."""
+ lineix = 0
+ while lineix < len(lines):
+ lineix_begin = FindNextMultiLineCommentStart(lines, lineix)
+ if lineix_begin >= len(lines):
+ return
+ lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin)
+ if lineix_end >= len(lines):
+ error(filename, lineix_begin + 1, 'readability/multiline_comment', 5,
+ 'Could not find end of multi-line comment')
+ return
+ RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1)
+ lineix = lineix_end + 1
+
+
+def CleanseComments(line):
+ """Removes //-comments and single-line C-style /* */ comments.
+
+ Args:
+ line: A line of C++ source.
+
+ Returns:
+ The line with single-line comments removed.
+ """
+ commentpos = line.find('//')
+ if commentpos != -1 and not IsCppString(line[:commentpos]):
+ line = line[:commentpos].rstrip()
+ # get rid of /* ... */
+ return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line)
+
+
+class CleansedLines(object):
+ """Holds 3 copies of all lines with different preprocessing applied to them.
+
+ 1) elided member contains lines without strings and comments,
+ 2) lines member contains lines without comments, and
+ 3) raw_lines member contains all the lines without processing.
+ All these three members are of <type 'list'>, and of the same length.
+ """
+
+ def __init__(self, lines):
+ self.elided = []
+ self.lines = []
+ self.raw_lines = lines
+ self.num_lines = len(lines)
+ self.lines_without_raw_strings = CleanseRawStrings(lines)
+ for linenum in range(len(self.lines_without_raw_strings)):
+ self.lines.append(CleanseComments(
+ self.lines_without_raw_strings[linenum]))
+ elided = self._CollapseStrings(self.lines_without_raw_strings[linenum])
+ self.elided.append(CleanseComments(elided))
+
+ def NumLines(self):
+ """Returns the number of lines represented."""
+ return self.num_lines
+
+ @staticmethod
+ def _CollapseStrings(elided):
+ """Collapses strings and chars on a line to simple "" or '' blocks.
+
+ We nix strings first so we're not fooled by text like '"http://"'
+
+ Args:
+ elided: The line being processed.
+
+ Returns:
+ The line with collapsed strings.
+ """
+ if _RE_PATTERN_INCLUDE.match(elided):
+ return elided
+
+ # Remove escaped characters first to make quote/single quote collapsing
+ # basic. Things that look like escaped characters shouldn't occur
+ # outside of strings and chars.
+ elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided)
+
+ # Replace quoted strings and digit separators. Both single quotes
+ # and double quotes are processed in the same loop, otherwise
+ # nested quotes wouldn't work.
+ collapsed = ''
+ while True:
+ # Find the first quote character
+ match = Match(r'^([^\'"]*)([\'"])(.*)$', elided)
+ if not match:
+ collapsed += elided
+ break
+ head, quote, tail = match.groups()
+
+ if quote == '"':
+ # Collapse double quoted strings
+ second_quote = tail.find('"')
+ if second_quote >= 0:
+ collapsed += head + '""'
+ elided = tail[second_quote + 1:]
+ else:
+ # Unmatched double quote, don't bother processing the rest
+ # of the line since this is probably a multiline string.
+ collapsed += elided
+ break
+ else:
+ # Found single quote, check nearby text to eliminate digit separators.
+ #
+ # There is no special handling for floating point here, because
+ # the integer/fractional/exponent parts would all be parsed
+ # correctly as long as there are digits on both sides of the
+ # separator. So we are fine as long as we don't see something
+ # like "0.'3" (gcc 4.9.0 will not allow this literal).
+ if Search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head):
+ match_literal = Match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail)
+ collapsed += head + match_literal.group(1).replace("'", '')
+ elided = match_literal.group(2)
+ else:
+ second_quote = tail.find('\'')
+ if second_quote >= 0:
+ collapsed += head + "''"
+ elided = tail[second_quote + 1:]
+ else:
+ # Unmatched single quote
+ collapsed += elided
+ break
+
+ return collapsed
+
+
+def FindEndOfExpressionInLine(line, startpos, stack):
+ """Find the position just after the end of current parenthesized expression.
+
+ Args:
+ line: a CleansedLines line.
+ startpos: start searching at this position.
+ stack: nesting stack at startpos.
+
+ Returns:
+ On finding matching end: (index just after matching end, None)
+ On finding an unclosed expression: (-1, None)
+ Otherwise: (-1, new stack at end of this line)
+ """
+ for i in xrange(startpos, len(line)):
+ char = line[i]
+ if char in '([{':
+ # Found start of parenthesized expression, push to expression stack
+ stack.append(char)
+ elif char == '<':
+ # Found potential start of template argument list
+ if i > 0 and line[i - 1] == '<':
+ # Left shift operator
+ if stack and stack[-1] == '<':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+ elif i > 0 and Search(r'\boperator\s*$', line[0:i]):
+ # operator<, don't add to stack
+ continue
+ else:
+ # Tentative start of template argument list
+ stack.append('<')
+ elif char in ')]}':
+ # Found end of parenthesized expression.
+ #
+ # If we are currently expecting a matching '>', the pending '<'
+ # must have been an operator. Remove them from expression stack.
+ while stack and stack[-1] == '<':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+ if ((stack[-1] == '(' and char == ')') or
+ (stack[-1] == '[' and char == ']') or
+ (stack[-1] == '{' and char == '}')):
+ stack.pop()
+ if not stack:
+ return (i + 1, None)
+ else:
+ # Mismatched parentheses
+ return (-1, None)
+ elif char == '>':
+ # Found potential end of template argument list.
+
+ # Ignore "->" and operator functions
+ if (i > 0 and
+ (line[i - 1] == '-' or Search(r'\boperator\s*$', line[0:i - 1]))):
+ continue
+
+ # Pop the stack if there is a matching '<'. Otherwise, ignore
+ # this '>' since it must be an operator.
+ if stack:
+ if stack[-1] == '<':
+ stack.pop()
+ if not stack:
+ return (i + 1, None)
+ elif char == ';':
+ # Found something that look like end of statements. If we are currently
+ # expecting a '>', the matching '<' must have been an operator, since
+ # template argument list should not contain statements.
+ while stack and stack[-1] == '<':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+
+ # Did not find end of expression or unbalanced parentheses on this line
+ return (-1, stack)
+
+
+def CloseExpression(clean_lines, linenum, pos):
+ """If input points to ( or { or [ or <, finds the position that closes it.
+
+ If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the
+ linenum/pos that correspond to the closing of the expression.
+
+ TODO(unknown): cpplint spends a fair bit of time matching parentheses.
+ Ideally we would want to index all opening and closing parentheses once
+ and have CloseExpression be just a simple lookup, but due to preprocessor
+ tricks, this is not so easy.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ pos: A position on the line.
+
+ Returns:
+ A tuple (line, linenum, pos) pointer *past* the closing brace, or
+ (line, len(lines), -1) if we never find a close. Note we ignore
+ strings and comments when matching; and the line we return is the
+ 'cleansed' line at linenum.
+ """
+
+ line = clean_lines.elided[linenum]
+ if (line[pos] not in '({[<') or Match(r'<[<=]', line[pos:]):
+ return (line, clean_lines.NumLines(), -1)
+
+ # Check first line
+ (end_pos, stack) = FindEndOfExpressionInLine(line, pos, [])
+ if end_pos > -1:
+ return (line, linenum, end_pos)
+
+ # Continue scanning forward
+ while stack and linenum < clean_lines.NumLines() - 1:
+ linenum += 1
+ line = clean_lines.elided[linenum]
+ (end_pos, stack) = FindEndOfExpressionInLine(line, 0, stack)
+ if end_pos > -1:
+ return (line, linenum, end_pos)
+
+ # Did not find end of expression before end of file, give up
+ return (line, clean_lines.NumLines(), -1)
+
+
+def FindStartOfExpressionInLine(line, endpos, stack):
+ """Find position at the matching start of current expression.
+
+ This is almost the reverse of FindEndOfExpressionInLine, but note
+ that the input position and returned position differs by 1.
+
+ Args:
+ line: a CleansedLines line.
+ endpos: start searching at this position.
+ stack: nesting stack at endpos.
+
+ Returns:
+ On finding matching start: (index at matching start, None)
+ On finding an unclosed expression: (-1, None)
+ Otherwise: (-1, new stack at beginning of this line)
+ """
+ i = endpos
+ while i >= 0:
+ char = line[i]
+ if char in ')]}':
+ # Found end of expression, push to expression stack
+ stack.append(char)
+ elif char == '>':
+ # Found potential end of template argument list.
+ #
+ # Ignore it if it's a "->" or ">=" or "operator>"
+ if (i > 0 and
+ (line[i - 1] == '-' or
+ Match(r'\s>=\s', line[i - 1:]) or
+ Search(r'\boperator\s*$', line[0:i]))):
+ i -= 1
+ else:
+ stack.append('>')
+ elif char == '<':
+ # Found potential start of template argument list
+ if i > 0 and line[i - 1] == '<':
+ # Left shift operator
+ i -= 1
+ else:
+ # If there is a matching '>', we can pop the expression stack.
+ # Otherwise, ignore this '<' since it must be an operator.
+ if stack and stack[-1] == '>':
+ stack.pop()
+ if not stack:
+ return (i, None)
+ elif char in '([{':
+ # Found start of expression.
+ #
+ # If there are any unmatched '>' on the stack, they must be
+ # operators. Remove those.
+ while stack and stack[-1] == '>':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+ if ((char == '(' and stack[-1] == ')') or
+ (char == '[' and stack[-1] == ']') or
+ (char == '{' and stack[-1] == '}')):
+ stack.pop()
+ if not stack:
+ return (i, None)
+ else:
+ # Mismatched parentheses
+ return (-1, None)
+ elif char == ';':
+ # Found something that look like end of statements. If we are currently
+ # expecting a '<', the matching '>' must have been an operator, since
+ # template argument list should not contain statements.
+ while stack and stack[-1] == '>':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+
+ i -= 1
+
+ return (-1, stack)
+
+
+def ReverseCloseExpression(clean_lines, linenum, pos):
+ """If input points to ) or } or ] or >, finds the position that opens it.
+
+ If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the
+ linenum/pos that correspond to the opening of the expression.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ pos: A position on the line.
+
+ Returns:
+ A tuple (line, linenum, pos) pointer *at* the opening brace, or
+ (line, 0, -1) if we never find the matching opening brace. Note
+ we ignore strings and comments when matching; and the line we
+ return is the 'cleansed' line at linenum.
+ """
+ line = clean_lines.elided[linenum]
+ if line[pos] not in ')}]>':
+ return (line, 0, -1)
+
+ # Check last line
+ (start_pos, stack) = FindStartOfExpressionInLine(line, pos, [])
+ if start_pos > -1:
+ return (line, linenum, start_pos)
+
+ # Continue scanning backward
+ while stack and linenum > 0:
+ linenum -= 1
+ line = clean_lines.elided[linenum]
+ (start_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack)
+ if start_pos > -1:
+ return (line, linenum, start_pos)
+
+ # Did not find start of expression before beginning of file, give up
+ return (line, 0, -1)
+
+
+def CheckForCopyright(filename, lines, error):
+ """Logs an error if no Copyright message appears at the top of the file."""
+
+ # We'll say it should occur by line 10. Don't forget there's a
+ # dummy line at the front.
+ for line in xrange(1, min(len(lines), 11)):
+ if re.search(r'Copyright', lines[line], re.I): break
+ else: # means no copyright line was found
+ error(filename, 0, 'legal/copyright', 5,
+ 'No copyright message found. '
+ 'You should have a line: "Copyright [year] <Copyright Owner>"')
+
+
+def GetIndentLevel(line):
+ """Return the number of leading spaces in line.
+
+ Args:
+ line: A string to check.
+
+ Returns:
+ An integer count of leading spaces, possibly zero.
+ """
+ indent = Match(r'^( *)\S', line)
+ if indent:
+ return len(indent.group(1))
+ else:
+ return 0
+
+
+def GetHeaderGuardCPPVariable(filename):
+ """Returns the CPP variable that should be used as a header guard.
+
+ Args:
+ filename: The name of a C++ header file.
+
+ Returns:
+ The CPP variable that should be used as a header guard in the
+ named file.
+
+ """
+
+ # Restores original filename in case that cpplint is invoked from Emacs's
+ # flymake.
+ filename = re.sub(r'_flymake\.h$', '.h', filename)
+ filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename)
+
+ fileinfo = FileInfo(filename)
+ file_path_from_root = fileinfo.RepositoryName()
+ if _root:
+ file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root)
+ return re.sub(r'[-./\s]', '_', file_path_from_root).upper() + '_'
+
+
+def CheckForHeaderGuard(filename, lines, error):
+ """Checks that the file contains a header guard.
+
+ Logs an error if no #ifndef header guard is present. For other
+ headers, checks that the full pathname is used.
+
+ Args:
+ filename: The name of the C++ header file.
+ lines: An array of strings, each representing a line of the file.
+ error: The function to call with any errors found.
+ """
+
+ cppvar = GetHeaderGuardCPPVariable(filename)
+
+ ifndef = None
+ ifndef_linenum = 0
+ define = None
+ endif = None
+ endif_linenum = 0
+ for linenum, line in enumerate(lines):
+ linesplit = line.split()
+ if len(linesplit) >= 2:
+ # find the first occurrence of #ifndef and #define, save arg
+ if not ifndef and linesplit[0] == '#ifndef':
+ # set ifndef to the header guard presented on the #ifndef line.
+ ifndef = linesplit[1]
+ ifndef_linenum = linenum
+ if not define and linesplit[0] == '#define':
+ define = linesplit[1]
+ # find the last occurrence of #endif, save entire line
+ if line.startswith('#endif'):
+ endif = line
+ endif_linenum = linenum
+
+ if not ifndef:
+ error(filename, 0, 'build/header_guard', 5,
+ 'No #ifndef header guard found, suggested CPP variable is: %s' %
+ cppvar)
+ return
+
+ if not define:
+ error(filename, 0, 'build/header_guard', 5,
+ 'No #define header guard found, suggested CPP variable is: %s' %
+ cppvar)
+ return
+
+ # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__
+ # for backward compatibility.
+ if ifndef != cppvar:
+ error_level = 0
+ if ifndef != cppvar + '_':
+ error_level = 5
+
+ ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum,
+ error)
+ error(filename, ifndef_linenum, 'build/header_guard', error_level,
+ '#ifndef header guard has wrong style, please use: %s' % cppvar)
+
+ if define != ifndef:
+ error(filename, 0, 'build/header_guard', 5,
+ '#ifndef and #define don\'t match, suggested CPP variable is: %s' %
+ cppvar)
+ return
+
+ if endif != ('#endif // %s' % cppvar):
+ error_level = 0
+ if endif != ('#endif // %s' % (cppvar + '_')):
+ error_level = 5
+
+ ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum,
+ error)
+ error(filename, endif_linenum, 'build/header_guard', error_level,
+ '#endif line should be "#endif // %s"' % cppvar)
+
+
+def CheckForBadCharacters(filename, lines, error):
+ """Logs an error for each line containing bad characters.
+
+ Two kinds of bad characters:
+
+ 1. Unicode replacement characters: These indicate that either the file
+ contained invalid UTF-8 (likely) or Unicode replacement characters (which
+ it shouldn't). Note that it's possible for this to throw off line
+ numbering if the invalid UTF-8 occurred adjacent to a newline.
+
+ 2. NUL bytes. These are problematic for some tools.
+
+ Args:
+ filename: The name of the current file.
+ lines: An array of strings, each representing a line of the file.
+ error: The function to call with any errors found.
+ """
+ for linenum, line in enumerate(lines):
+ if u'\ufffd' in line:
+ error(filename, linenum, 'readability/utf8', 5,
+ 'Line contains invalid UTF-8 (or Unicode replacement character).')
+ if '\0' in line:
+ error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.')
+
+
+def CheckForNewlineAtEOF(filename, lines, error):
+ """Logs an error if there is no newline char at the end of the file.
+
+ Args:
+ filename: The name of the current file.
+ lines: An array of strings, each representing a line of the file.
+ error: The function to call with any errors found.
+ """
+
+ # The array lines() was created by adding two newlines to the
+ # original file (go figure), then splitting on \n.
+ # To verify that the file ends in \n, we just have to make sure the
+ # last-but-two element of lines() exists and is empty.
+ if len(lines) < 3 or lines[-2]:
+ error(filename, len(lines) - 2, 'whitespace/ending_newline', 5,
+ 'Could not find a newline character at the end of the file.')
+
+
+def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error):
+ """Logs an error if we see /* ... */ or "..." that extend past one line.
+
+ /* ... */ comments are legit inside macros, for one line.
+ Otherwise, we prefer // comments, so it's ok to warn about the
+ other. Likewise, it's ok for strings to extend across multiple
+ lines, as long as a line continuation character (backslash)
+ terminates each line. Although not currently prohibited by the C++
+ style guide, it's ugly and unnecessary. We don't do well with either
+ in this lint program, so we warn about both.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Remove all \\ (escaped backslashes) from the line. They are OK, and the
+ # second (escaped) slash may trigger later \" detection erroneously.
+ line = line.replace('\\\\', '')
+
+ if line.count('/*') > line.count('*/'):
+ error(filename, linenum, 'readability/multiline_comment', 5,
+ 'Complex multi-line /*...*/-style comment found. '
+ 'Lint may give bogus warnings. '
+ 'Consider replacing these with //-style comments, '
+ 'with #if 0...#endif, '
+ 'or with more clearly structured multi-line comments.')
+
+ if (line.count('"') - line.count('\\"')) % 2:
+ error(filename, linenum, 'readability/multiline_string', 5,
+ 'Multi-line string ("...") found. This lint script doesn\'t '
+ 'do well with such strings, and may give bogus warnings. '
+ 'Use C++11 raw strings or concatenation instead.')
+
+
+# (non-threadsafe name, thread-safe alternative, validation pattern)
+#
+# The validation pattern is used to eliminate false positives such as:
+# _rand(); // false positive due to substring match.
+# ->rand(); // some member function rand().
+# ACMRandom rand(seed); // some variable named rand.
+# ISAACRandom rand(); // another variable named rand.
+#
+# Basically we require the return value of these functions to be used
+# in some expression context on the same line by matching on some
+# operator before the function name. This eliminates constructors and
+# member function calls.
+_UNSAFE_FUNC_PREFIX = r'(?:[-+*/=%^&|(<]\s*|>\s+)'
+_THREADING_LIST = (
+ ('asctime(', 'asctime_r(', _UNSAFE_FUNC_PREFIX + r'asctime\([^)]+\)'),
+ ('ctime(', 'ctime_r(', _UNSAFE_FUNC_PREFIX + r'ctime\([^)]+\)'),
+ ('getgrgid(', 'getgrgid_r(', _UNSAFE_FUNC_PREFIX + r'getgrgid\([^)]+\)'),
+ ('getgrnam(', 'getgrnam_r(', _UNSAFE_FUNC_PREFIX + r'getgrnam\([^)]+\)'),
+ ('getlogin(', 'getlogin_r(', _UNSAFE_FUNC_PREFIX + r'getlogin\(\)'),
+ ('getpwnam(', 'getpwnam_r(', _UNSAFE_FUNC_PREFIX + r'getpwnam\([^)]+\)'),
+ ('getpwuid(', 'getpwuid_r(', _UNSAFE_FUNC_PREFIX + r'getpwuid\([^)]+\)'),
+ ('gmtime(', 'gmtime_r(', _UNSAFE_FUNC_PREFIX + r'gmtime\([^)]+\)'),
+ ('localtime(', 'localtime_r(', _UNSAFE_FUNC_PREFIX + r'localtime\([^)]+\)'),
+ ('rand(', 'rand_r(', _UNSAFE_FUNC_PREFIX + r'rand\(\)'),
+ ('strtok(', 'strtok_r(',
+ _UNSAFE_FUNC_PREFIX + r'strtok\([^)]+\)'),
+ ('ttyname(', 'ttyname_r(', _UNSAFE_FUNC_PREFIX + r'ttyname\([^)]+\)'),
+ )
+
+
+def CheckPosixThreading(filename, clean_lines, linenum, error):
+ """Checks for calls to thread-unsafe functions.
+
+ Much code has been originally written without consideration of
+ multi-threading. Also, engineers are relying on their old experience;
+ they have learned posix before threading extensions were added. These
+ tests guide the engineers to use thread-safe functions (when using
+ posix directly).
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+ for single_thread_func, multithread_safe_func, pattern in _THREADING_LIST:
+ # Additional pattern matching check to confirm that this is the
+ # function we are looking for
+ if Search(pattern, line):
+ error(filename, linenum, 'runtime/threadsafe_fn', 2,
+ 'Consider using ' + multithread_safe_func +
+ '...) instead of ' + single_thread_func +
+ '...) for improved thread safety.')
+
+
+def CheckVlogArguments(filename, clean_lines, linenum, error):
+ """Checks that VLOG() is only used for defining a logging level.
+
+ For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and
+ VLOG(FATAL) are not.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+ if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line):
+ error(filename, linenum, 'runtime/vlog', 5,
+ 'VLOG() should be used with numeric verbosity level. '
+ 'Use LOG() if you want symbolic severity levels.')
+
+# Matches invalid increment: *count++, which moves pointer instead of
+# incrementing a value.
+_RE_PATTERN_INVALID_INCREMENT = re.compile(
+ r'^\s*\*\w+(\+\+|--);')
+
+
+def CheckInvalidIncrement(filename, clean_lines, linenum, error):
+ """Checks for invalid increment *count++.
+
+ For example following function:
+ void increment_counter(int* count) {
+ *count++;
+ }
+ is invalid, because it effectively does count++, moving pointer, and should
+ be replaced with ++*count, (*count)++ or *count += 1.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+ if _RE_PATTERN_INVALID_INCREMENT.match(line):
+ error(filename, linenum, 'runtime/invalid_increment', 5,
+ 'Changing pointer instead of value (or unused value of operator*).')
+
+
+class _BlockInfo(object):
+ """Stores information about a generic block of code."""
+
+ def __init__(self, seen_open_brace):
+ self.seen_open_brace = seen_open_brace
+ self.open_parentheses = 0
+ self.inline_asm = _NO_ASM
+
+ def CheckBegin(self, filename, clean_lines, linenum, error):
+ """Run checks that applies to text up to the opening brace.
+
+ This is mostly for checking the text after the class identifier
+ and the "{", usually where the base class is specified. For other
+ blocks, there isn't much to check, so we always pass.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ pass
+
+ def CheckEnd(self, filename, clean_lines, linenum, error):
+ """Run checks that applies to text after the closing brace.
+
+ This is mostly used for checking end of namespace comments.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ pass
+
+ def IsBlockInfo(self):
+ """Returns true if this block is a _BlockInfo.
+
+ This is convenient for verifying that an object is an instance of
+ a _BlockInfo, but not an instance of any of the derived classes.
+
+ Returns:
+ True for this class, False for derived classes.
+ """
+ return self.__class__ == _BlockInfo
+
+
+class _ExternCInfo(_BlockInfo):
+ """Stores information about an 'extern "C"' block."""
+
+ def __init__(self):
+ _BlockInfo.__init__(self, True)
+
+
+class _ClassInfo(_BlockInfo):
+ """Stores information about a class."""
+
+ def __init__(self, name, class_or_struct, clean_lines, linenum):
+ _BlockInfo.__init__(self, False)
+ self.name = name
+ self.starting_linenum = linenum
+ self.is_derived = False
+ if class_or_struct == 'struct':
+ self.access = 'public'
+ self.is_struct = True
+ else:
+ self.access = 'private'
+ self.is_struct = False
+
+ # Remember initial indentation level for this class. Using raw_lines here
+ # instead of elided to account for leading comments.
+ self.class_indent = GetIndentLevel(clean_lines.raw_lines[linenum])
+
+ # Try to find the end of the class. This will be confused by things like:
+ # class A {
+ # } *x = { ...
+ #
+ # But it's still good enough for CheckSectionSpacing.
+ self.last_line = 0
+ depth = 0
+ for i in range(linenum, clean_lines.NumLines()):
+ line = clean_lines.elided[i]
+ depth += line.count('{') - line.count('}')
+ if not depth:
+ self.last_line = i
+ break
+
+ def CheckBegin(self, filename, clean_lines, linenum, error):
+ # Look for a bare ':'
+ if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]):
+ self.is_derived = True
+
+ def CheckEnd(self, filename, clean_lines, linenum, error):
+ # Check that closing brace is aligned with beginning of the class.
+ # Only do this if the closing brace is indented by only whitespaces.
+ # This means we will not check single-line class definitions.
+ indent = Match(r'^( *)\}', clean_lines.elided[linenum])
+ if indent and len(indent.group(1)) != self.class_indent:
+ if self.is_struct:
+ parent = 'struct ' + self.name
+ else:
+ parent = 'class ' + self.name
+ error(filename, linenum, 'whitespace/indent', 3,
+ 'Closing brace should be aligned with beginning of %s' % parent)
+
+
+class _NamespaceInfo(_BlockInfo):
+ """Stores information about a namespace."""
+
+ def __init__(self, name, linenum):
+ _BlockInfo.__init__(self, False)
+ self.name = name or ''
+ self.starting_linenum = linenum
+
+ def CheckEnd(self, filename, clean_lines, linenum, error):
+ """Check end of namespace comments."""
+ line = clean_lines.raw_lines[linenum]
+
+ # Check how many lines is enclosed in this namespace. Don't issue
+ # warning for missing namespace comments if there aren't enough
+ # lines. However, do apply checks if there is already an end of
+ # namespace comment and it's incorrect.
+ #
+ # TODO(unknown): We always want to check end of namespace comments
+ # if a namespace is large, but sometimes we also want to apply the
+ # check if a short namespace contained nontrivial things (something
+ # other than forward declarations). There is currently no logic on
+ # deciding what these nontrivial things are, so this check is
+ # triggered by namespace size only, which works most of the time.
+ if (linenum - self.starting_linenum < 10
+ and not Match(r'};*\s*(//|/\*).*\bnamespace\b', line)):
+ return
+
+ # Look for matching comment at end of namespace.
+ #
+ # Note that we accept C style "/* */" comments for terminating
+ # namespaces, so that code that terminate namespaces inside
+ # preprocessor macros can be cpplint clean.
+ #
+ # We also accept stuff like "// end of namespace <name>." with the
+ # period at the end.
+ #
+ # Besides these, we don't accept anything else, otherwise we might
+ # get false negatives when existing comment is a substring of the
+ # expected namespace.
+ if self.name:
+ # Named namespace
+ if Match((r'};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) +
+ r'[\*/\.\\\s]*$'),
+ line):
+ error(filename, linenum, 'readability/namespace', 5,
+ 'Namespace should not be terminated with "// namespace %s"' %
+ self.name)
+ else:
+ # Anonymous namespace
+ if not Match(r'};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line):
+ # If "// namespace anonymous" or "// anonymous namespace (more text)",
+ # mention "// anonymous namespace" as an acceptable form
+ if Match(r'}.*\b(namespace anonymous|anonymous namespace)\b', line):
+ error(filename, linenum, 'readability/namespace', 5,
+ 'Anonymous namespace should be terminated with "// namespace"'
+ ' or "// anonymous namespace"')
+ else:
+ error(filename, linenum, 'readability/namespace', 5,
+ 'Anonymous namespace should be terminated with "// namespace"')
+
+
+class _PreprocessorInfo(object):
+ """Stores checkpoints of nesting stacks when #if/#else is seen."""
+
+ def __init__(self, stack_before_if):
+ # The entire nesting stack before #if
+ self.stack_before_if = stack_before_if
+
+ # The entire nesting stack up to #else
+ self.stack_before_else = []
+
+ # Whether we have already seen #else or #elif
+ self.seen_else = False
+
+
+class NestingState(object):
+ """Holds states related to parsing braces."""
+
+ def __init__(self):
+ # Stack for tracking all braces. An object is pushed whenever we
+ # see a "{", and popped when we see a "}". Only 3 types of
+ # objects are possible:
+ # - _ClassInfo: a class or struct.
+ # - _NamespaceInfo: a namespace.
+ # - _BlockInfo: some other type of block.
+ self.stack = []
+
+ # Top of the previous stack before each Update().
+ #
+ # Because the nesting_stack is updated at the end of each line, we
+ # had to do some convoluted checks to find out what is the current
+ # scope at the beginning of the line. This check is simplified by
+ # saving the previous top of nesting stack.
+ #
+ # We could save the full stack, but we only need the top. Copying
+ # the full nesting stack would slow down cpplint by ~10%.
+ self.previous_stack_top = []
+
+ # Stack of _PreprocessorInfo objects.
+ self.pp_stack = []
+
+ def SeenOpenBrace(self):
+ """Check if we have seen the opening brace for the innermost block.
+
+ Returns:
+ True if we have seen the opening brace, False if the innermost
+ block is still expecting an opening brace.
+ """
+ return (not self.stack) or self.stack[-1].seen_open_brace
+
+ def InNamespaceBody(self):
+ """Check if we are currently one level inside a namespace body.
+
+ Returns:
+ True if top of the stack is a namespace block, False otherwise.
+ """
+ return self.stack and isinstance(self.stack[-1], _NamespaceInfo)
+
+ def InExternC(self):
+ """Check if we are currently one level inside an 'extern "C"' block.
+
+ Returns:
+ True if top of the stack is an extern block, False otherwise.
+ """
+ return self.stack and isinstance(self.stack[-1], _ExternCInfo)
+
+ def InClassDeclaration(self):
+ """Check if we are currently one level inside a class or struct declaration.
+
+ Returns:
+ True if top of the stack is a class/struct, False otherwise.
+ """
+ return self.stack and isinstance(self.stack[-1], _ClassInfo)
+
+ def InAsmBlock(self):
+ """Check if we are currently one level inside an inline ASM block.
+
+ Returns:
+ True if the top of the stack is a block containing inline ASM.
+ """
+ return self.stack and self.stack[-1].inline_asm != _NO_ASM
+
+ def InTemplateArgumentList(self, clean_lines, linenum, pos):
+ """Check if current position is inside template argument list.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ pos: position just after the suspected template argument.
+ Returns:
+ True if (linenum, pos) is inside template arguments.
+ """
+ while linenum < clean_lines.NumLines():
+ # Find the earliest character that might indicate a template argument
+ line = clean_lines.elided[linenum]
+ match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:])
+ if not match:
+ linenum += 1
+ pos = 0
+ continue
+ token = match.group(1)
+ pos += len(match.group(0))
+
+ # These things do not look like template argument list:
+ # class Suspect {
+ # class Suspect x; }
+ if token in ('{', '}', ';'): return False
+
+ # These things look like template argument list:
+ # template <class Suspect>
+ # template <class Suspect = default_value>
+ # template <class Suspect[]>
+ # template <class Suspect...>
+ if token in ('>', '=', '[', ']', '.'): return True
+
+ # Check if token is an unmatched '<'.
+ # If not, move on to the next character.
+ if token != '<':
+ pos += 1
+ if pos >= len(line):
+ linenum += 1
+ pos = 0
+ continue
+
+ # We can't be sure if we just find a single '<', and need to
+ # find the matching '>'.
+ (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1)
+ if end_pos < 0:
+ # Not sure if template argument list or syntax error in file
+ return False
+ linenum = end_line
+ pos = end_pos
+ return False
+
+ def UpdatePreprocessor(self, line):
+ """Update preprocessor stack.
+
+ We need to handle preprocessors due to classes like this:
+ #ifdef SWIG
+ struct ResultDetailsPageElementExtensionPoint {
+ #else
+ struct ResultDetailsPageElementExtensionPoint : public Extension {
+ #endif
+
+ We make the following assumptions (good enough for most files):
+ - Preprocessor condition evaluates to true from #if up to first
+ #else/#elif/#endif.
+
+ - Preprocessor condition evaluates to false from #else/#elif up
+ to #endif. We still perform lint checks on these lines, but
+ these do not affect nesting stack.
+
+ Args:
+ line: current line to check.
+ """
+ if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line):
+ # Beginning of #if block, save the nesting stack here. The saved
+ # stack will allow us to restore the parsing state in the #else case.
+ self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack)))
+ elif Match(r'^\s*#\s*(else|elif)\b', line):
+ # Beginning of #else block
+ if self.pp_stack:
+ if not self.pp_stack[-1].seen_else:
+ # This is the first #else or #elif block. Remember the
+ # whole nesting stack up to this point. This is what we
+ # keep after the #endif.
+ self.pp_stack[-1].seen_else = True
+ self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack)
+
+ # Restore the stack to how it was before the #if
+ self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if)
+ else:
+ # TODO(unknown): unexpected #else, issue warning?
+ pass
+ elif Match(r'^\s*#\s*endif\b', line):
+ # End of #if or #else blocks.
+ if self.pp_stack:
+ # If we saw an #else, we will need to restore the nesting
+ # stack to its former state before the #else, otherwise we
+ # will just continue from where we left off.
+ if self.pp_stack[-1].seen_else:
+ # Here we can just use a shallow copy since we are the last
+ # reference to it.
+ self.stack = self.pp_stack[-1].stack_before_else
+ # Drop the corresponding #if
+ self.pp_stack.pop()
+ else:
+ # TODO(unknown): unexpected #endif, issue warning?
+ pass
+
+ # TODO(unknown): Update() is too long, but we will refactor later.
+ def Update(self, filename, clean_lines, linenum, error):
+ """Update nesting state with current line.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Remember top of the previous nesting stack.
+ #
+ # The stack is always pushed/popped and not modified in place, so
+ # we can just do a shallow copy instead of copy.deepcopy. Using
+ # deepcopy would slow down cpplint by ~28%.
+ if self.stack:
+ self.previous_stack_top = self.stack[-1]
+ else:
+ self.previous_stack_top = None
+
+ # Update pp_stack
+ self.UpdatePreprocessor(line)
+
+ # Count parentheses. This is to avoid adding struct arguments to
+ # the nesting stack.
+ if self.stack:
+ inner_block = self.stack[-1]
+ depth_change = line.count('(') - line.count(')')
+ inner_block.open_parentheses += depth_change
+
+ # Also check if we are starting or ending an inline assembly block.
+ if inner_block.inline_asm in (_NO_ASM, _END_ASM):
+ if (depth_change != 0 and
+ inner_block.open_parentheses == 1 and
+ _MATCH_ASM.match(line)):
+ # Enter assembly block
+ inner_block.inline_asm = _INSIDE_ASM
+ else:
+ # Not entering assembly block. If previous line was _END_ASM,
+ # we will now shift to _NO_ASM state.
+ inner_block.inline_asm = _NO_ASM
+ elif (inner_block.inline_asm == _INSIDE_ASM and
+ inner_block.open_parentheses == 0):
+ # Exit assembly block
+ inner_block.inline_asm = _END_ASM
+
+ # Consume namespace declaration at the beginning of the line. Do
+ # this in a loop so that we catch same line declarations like this:
+ # namespace proto2 { namespace bridge { class MessageSet; } }
+ while True:
+ # Match start of namespace. The "\b\s*" below catches namespace
+ # declarations even if it weren't followed by a whitespace, this
+ # is so that we don't confuse our namespace checker. The
+ # missing spaces will be flagged by CheckSpacing.
+ namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line)
+ if not namespace_decl_match:
+ break
+
+ new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum)
+ self.stack.append(new_namespace)
+
+ line = namespace_decl_match.group(2)
+ if line.find('{') != -1:
+ new_namespace.seen_open_brace = True
+ line = line[line.find('{') + 1:]
+
+ # Look for a class declaration in whatever is left of the line
+ # after parsing namespaces. The regexp accounts for decorated classes
+ # such as in:
+ # class LOCKABLE API Object {
+ # };
+ class_decl_match = Match(
+ r'^(\s*(?:template\s*<[\w\s<>,:]*>\s*)?'
+ r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))'
+ r'(.*)$', line)
+ if (class_decl_match and
+ (not self.stack or self.stack[-1].open_parentheses == 0)):
+ # We do not want to accept classes that are actually template arguments:
+ # template <class Ignore1,
+ # class Ignore2 = Default<Args>,
+ # template <Args> class Ignore3>
+ # void Function() {};
+ #
+ # To avoid template argument cases, we scan forward and look for
+ # an unmatched '>'. If we see one, assume we are inside a
+ # template argument list.
+ end_declaration = len(class_decl_match.group(1))
+ if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration):
+ self.stack.append(_ClassInfo(
+ class_decl_match.group(3), class_decl_match.group(2),
+ clean_lines, linenum))
+ line = class_decl_match.group(4)
+
+ # If we have not yet seen the opening brace for the innermost block,
+ # run checks here.
+ if not self.SeenOpenBrace():
+ self.stack[-1].CheckBegin(filename, clean_lines, linenum, error)
+
+ # Update access control if we are inside a class/struct
+ if self.stack and isinstance(self.stack[-1], _ClassInfo):
+ classinfo = self.stack[-1]
+ access_match = Match(
+ r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?'
+ r':(?:[^:]|$)',
+ line)
+ if access_match:
+ classinfo.access = access_match.group(2)
+
+ # Check that access keywords are indented +1 space. Skip this
+ # check if the keywords are not preceded by whitespaces.
+ indent = access_match.group(1)
+ if (len(indent) != classinfo.class_indent + 2 and
+ Match(r'^\s*$', indent)):
+ if classinfo.is_struct:
+ parent = 'struct ' + classinfo.name
+ else:
+ parent = 'class ' + classinfo.name
+ slots = ''
+ if access_match.group(3):
+ slots = access_match.group(3)
+ error(filename, linenum, 'whitespace/indent', 3,
+ '%s%s: should be indented +2 space inside %s' % (
+ access_match.group(2), slots, parent))
+
+ # Consume braces or semicolons from what's left of the line
+ while True:
+ # Match first brace, semicolon, or closed parenthesis.
+ matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line)
+ if not matched:
+ break
+
+ token = matched.group(1)
+ if token == '{':
+ # If namespace or class hasn't seen a opening brace yet, mark
+ # namespace/class head as complete. Push a new block onto the
+ # stack otherwise.
+ if not self.SeenOpenBrace():
+ self.stack[-1].seen_open_brace = True
+ elif Match(r'^extern\s*"[^"]*"\s*\{', line):
+ self.stack.append(_ExternCInfo())
+ else:
+ self.stack.append(_BlockInfo(True))
+ if _MATCH_ASM.match(line):
+ self.stack[-1].inline_asm = _BLOCK_ASM
+
+ elif token == ';' or token == ')':
+ # If we haven't seen an opening brace yet, but we already saw
+ # a semicolon, this is probably a forward declaration. Pop
+ # the stack for these.
+ #
+ # Similarly, if we haven't seen an opening brace yet, but we
+ # already saw a closing parenthesis, then these are probably
+ # function arguments with extra "class" or "struct" keywords.
+ # Also pop these stack for these.
+ if not self.SeenOpenBrace():
+ self.stack.pop()
+ else: # token == '}'
+ # Perform end of block checks and pop the stack.
+ if self.stack:
+ self.stack[-1].CheckEnd(filename, clean_lines, linenum, error)
+ self.stack.pop()
+ line = matched.group(2)
+
+ def InnermostClass(self):
+ """Get class info on the top of the stack.
+
+ Returns:
+ A _ClassInfo object if we are inside a class, or None otherwise.
+ """
+ for i in range(len(self.stack), 0, -1):
+ classinfo = self.stack[i - 1]
+ if isinstance(classinfo, _ClassInfo):
+ return classinfo
+ return None
+
+ def CheckCompletedBlocks(self, filename, error):
+ """Checks that all classes and namespaces have been completely parsed.
+
+ Call this when all lines in a file have been processed.
+ Args:
+ filename: The name of the current file.
+ error: The function to call with any errors found.
+ """
+ # Note: This test can result in false positives if #ifdef constructs
+ # get in the way of brace matching. See the testBuildClass test in
+ # cpplint_unittest.py for an example of this.
+ for obj in self.stack:
+ if isinstance(obj, _ClassInfo):
+ error(filename, obj.starting_linenum, 'build/class', 5,
+ 'Failed to find complete declaration of class %s' %
+ obj.name)
+ elif isinstance(obj, _NamespaceInfo):
+ error(filename, obj.starting_linenum, 'build/namespaces', 5,
+ 'Failed to find complete declaration of namespace %s' %
+ obj.name)
+
+
+def CheckForNonStandardConstructs(filename, clean_lines, linenum,
+ nesting_state, error):
+ r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2.
+
+ Complain about several constructs which gcc-2 accepts, but which are
+ not standard C++. Warning about these in lint is one way to ease the
+ transition to new compilers.
+ - put storage class first (e.g. "static const" instead of "const static").
+ - "%lld" instead of %qd" in printf-type functions.
+ - "%1$d" is non-standard in printf-type functions.
+ - "\%" is an undefined character escape sequence.
+ - text after #endif is not allowed.
+ - invalid inner-style forward declaration.
+ - >? and <? operators, and their >?= and <?= cousins.
+
+ Additionally, check for constructor/destructor style violations and reference
+ members, as it is very convenient to do so while checking for
+ gcc-2 compliance.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: A callable to which errors are reported, which takes 4 arguments:
+ filename, line number, error level, and message
+ """
+
+ # Remove comments from the line, but leave in strings for now.
+ line = clean_lines.lines[linenum]
+
+ if Search(r'printf\s*\(.*".*%[-+ ]?\d*q', line):
+ error(filename, linenum, 'runtime/printf_format', 3,
+ '%q in format strings is deprecated. Use %ll instead.')
+
+ if Search(r'printf\s*\(.*".*%\d+\$', line):
+ error(filename, linenum, 'runtime/printf_format', 2,
+ '%N$ formats are unconventional. Try rewriting to avoid them.')
+
+ # Remove escaped backslashes before looking for undefined escapes.
+ line = line.replace('\\\\', '')
+
+ if Search(r'("|\').*\\(%|\[|\(|{)', line):
+ error(filename, linenum, 'build/printf_format', 3,
+ '%, [, (, and { are undefined character escapes. Unescape them.')
+
+ # For the rest, work with both comments and strings removed.
+ line = clean_lines.elided[linenum]
+
+ if Search(r'\b(const|volatile|void|char|short|int|long'
+ r'|float|double|signed|unsigned'
+ r'|schar|u?int8|u?int16|u?int32|u?int64)'
+ r'\s+(register|static|extern|typedef)\b',
+ line):
+ error(filename, linenum, 'build/storage_class', 5,
+ 'Storage class (static, extern, typedef, etc) should be first.')
+
+ if Match(r'\s*#\s*endif\s*[^/\s]+', line):
+ error(filename, linenum, 'build/endif_comment', 5,
+ 'Uncommented text after #endif is non-standard. Use a comment.')
+
+ if Match(r'\s*class\s+(\w+\s*::\s*)+\w+\s*;', line):
+ error(filename, linenum, 'build/forward_decl', 5,
+ 'Inner-style forward declarations are invalid. Remove this line.')
+
+ if Search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?',
+ line):
+ error(filename, linenum, 'build/deprecated', 3,
+ '>? and <? (max and min) operators are non-standard and deprecated.')
+
+ if Search(r'^\s*const\s*string\s*&\s*\w+\s*;', line):
+ # TODO(unknown): Could it be expanded safely to arbitrary references,
+ # without triggering too many false positives? The first
+ # attempt triggered 5 warnings for mostly benign code in the regtest, hence
+ # the restriction.
+ # Here's the original regexp, for the reference:
+ # type_name = r'\w+((\s*::\s*\w+)|(\s*<\s*\w+?\s*>))?'
+ # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;'
+ error(filename, linenum, 'runtime/member_string_references', 2,
+ 'const string& members are dangerous. It is much better to use '
+ 'alternatives, such as pointers or simple constants.')
+
+ # Everything else in this function operates on class declarations.
+ # Return early if the top of the nesting stack is not a class, or if
+ # the class head is not completed yet.
+ classinfo = nesting_state.InnermostClass()
+ if not classinfo or not classinfo.seen_open_brace:
+ return
+
+ # The class may have been declared with namespace or classname qualifiers.
+ # The constructor and destructor will not have those qualifiers.
+ base_classname = classinfo.name.split('::')[-1]
+
+ # Look for single-argument constructors that aren't marked explicit.
+ # Technically a valid construct, but against style.
+ args = Match(r'\s+(?:inline\s+)?%s\s*\(([^,()]+)\)'
+ % re.escape(base_classname),
+ line)
+ if (args and
+ args.group(1) != 'void' and
+ not Search(r'\bstd::initializer_list\b', args.group(1)) and
+ not Match(r'(const\s+)?%s(\s+const)?\s*(?:<\w+>\s*)?&'
+ % re.escape(base_classname), args.group(1).strip())):
+ error(filename, linenum, 'runtime/explicit', 5,
+ 'Single-argument constructors should be marked explicit.')
+
+
+def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error):
+ """Checks for the correctness of various spacing around function calls.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Since function calls often occur inside if/for/while/switch
+ # expressions - which have their own, more liberal conventions - we
+ # first see if we should be looking inside such an expression for a
+ # function call, to which we can apply more strict standards.
+ fncall = line # if there's no control flow construct, look at whole line
+ for pattern in (r'\bif\s*\((.*)\)\s*{',
+ r'\bfor\s*\((.*)\)\s*{',
+ r'\bwhile\s*\((.*)\)\s*[{;]',
+ r'\bswitch\s*\((.*)\)\s*{'):
+ match = Search(pattern, line)
+ if match:
+ fncall = match.group(1) # look inside the parens for function calls
+ break
+
+ # Except in if/for/while/switch, there should never be space
+ # immediately inside parens (eg "f( 3, 4 )"). We make an exception
+ # for nested parens ( (a+b) + c ). Likewise, there should never be
+ # a space before a ( when it's a function argument. I assume it's a
+ # function argument when the char before the whitespace is legal in
+ # a function name (alnum + _) and we're not starting a macro. Also ignore
+ # pointers and references to arrays and functions coz they're too tricky:
+ # we use a very simple way to recognize these:
+ # " (something)(maybe-something)" or
+ # " (something)(maybe-something," or
+ # " (something)[something]"
+ # Note that we assume the contents of [] to be short enough that
+ # they'll never need to wrap.
+ if ( # Ignore control structures.
+ not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b',
+ fncall) and
+ # Ignore pointers/references to functions.
+ not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and
+ # Ignore pointers/references to arrays.
+ not Search(r' \([^)]+\)\[[^\]]+\]', fncall)):
+ if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call
+ error(filename, linenum, 'whitespace/parens', 4,
+ 'Extra space after ( in function call')
+ elif Search(r'\(\s+(?!(\s*\\)|\()', fncall):
+ error(filename, linenum, 'whitespace/parens', 2,
+ 'Extra space after (')
+ if (Search(r'\w\s+\(', fncall) and
+ not Search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and
+ not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall)):
+ # TODO(unknown): Space after an operator function seem to be a common
+ # error, silence those for now by restricting them to highest verbosity.
+ if Search(r'\boperator_*\b', line):
+ error(filename, linenum, 'whitespace/parens', 0,
+ 'Extra space before ( in function call')
+ else:
+ error(filename, linenum, 'whitespace/parens', 4,
+ 'Extra space before ( in function call')
+ # If the ) is followed only by a newline or a { + newline, assume it's
+ # part of a control statement (if/while/etc), and don't complain
+ if Search(r'[^)]\s+\)\s*[^{\s]', fncall):
+ # If the closing parenthesis is preceded by only whitespaces,
+ # try to give a more descriptive error message.
+ if Search(r'^\s+\)', fncall):
+ error(filename, linenum, 'whitespace/parens', 2,
+ 'Closing ) should be moved to the previous line')
+ else:
+ error(filename, linenum, 'whitespace/parens', 2,
+ 'Extra space before )')
+
+
+def IsBlankLine(line):
+ """Returns true if the given line is blank.
+
+ We consider a line to be blank if the line is empty or consists of
+ only white spaces.
+
+ Args:
+ line: A line of a string.
+
+ Returns:
+ True, if the given line is blank.
+ """
+ return not line or line.isspace()
+
+
+def CheckForFunctionLengths(filename, clean_lines, linenum,
+ function_state, error):
+ """Reports for long function bodies.
+
+ For an overview why this is done, see:
+ http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions
+
+ Uses a simplistic algorithm assuming other style guidelines
+ (especially spacing) are followed.
+ Only checks unindented functions, so class members are unchecked.
+ Trivial bodies are unchecked, so constructors with huge initializer lists
+ may be missed.
+ Blank/comment lines are not counted so as to avoid encouraging the removal
+ of vertical space and comments just to get through a lint check.
+ NOLINT *on the last line of a function* disables this check.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ function_state: Current function name and lines in body so far.
+ error: The function to call with any errors found.
+ """
+ lines = clean_lines.lines
+ line = lines[linenum]
+ joined_line = ''
+
+ starting_func = False
+ regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ...
+ match_result = Match(regexp, line)
+ if match_result:
+ # If the name is all caps and underscores, figure it's a macro and
+ # ignore it, unless it's TEST or TEST_F.
+ function_name = match_result.group(1).split()[-1]
+ if function_name == 'TEST' or function_name == 'TEST_F' or (
+ not Match(r'[A-Z_]+$', function_name)):
+ starting_func = True
+
+ if starting_func:
+ body_found = False
+ for start_linenum in xrange(linenum, clean_lines.NumLines()):
+ start_line = lines[start_linenum]
+ joined_line += ' ' + start_line.lstrip()
+ if Search(r'(;|})', start_line): # Declarations and trivial functions
+ body_found = True
+ break # ... ignore
+ elif Search(r'{', start_line):
+ body_found = True
+ function = Search(r'((\w|:)*)\(', line).group(1)
+ if Match(r'TEST', function): # Handle TEST... macros
+ parameter_regexp = Search(r'(\(.*\))', joined_line)
+ if parameter_regexp: # Ignore bad syntax
+ function += parameter_regexp.group(1)
+ else:
+ function += '()'
+ function_state.Begin(function)
+ break
+ if not body_found:
+ # No body for the function (or evidence of a non-function) was found.
+ error(filename, linenum, 'readability/fn_size', 5,
+ 'Lint failed to find start of function body.')
+ elif Match(r'^\}\s*$', line): # function end
+ function_state.Check(error, filename, linenum)
+ function_state.End()
+ elif not Match(r'^\s*$', line):
+ function_state.Count() # Count non-blank/non-comment lines.
+
+
+_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?')
+
+
+def CheckComment(line, filename, linenum, next_line_start, error):
+ """Checks for common mistakes in comments.
+
+ Args:
+ line: The line in question.
+ filename: The name of the current file.
+ linenum: The number of the line to check.
+ next_line_start: The first non-whitespace column of the next line.
+ error: The function to call with any errors found.
+ """
+ commentpos = line.find('//')
+ if commentpos != -1:
+ # Check if the // may be in quotes. If so, ignore it
+ # Comparisons made explicit for clarity -- pylint: disable=g-explicit-bool-comparison
+ if (line.count('"', 0, commentpos) -
+ line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes
+ # Allow one space for new scopes, two spaces otherwise:
+ if (not (Match(r'^.*{ *//', line) and next_line_start == commentpos) and
+ ((commentpos >= 1 and
+ line[commentpos-1] not in string.whitespace) or
+ (commentpos >= 2 and
+ line[commentpos-2] not in string.whitespace))):
+ error(filename, linenum, 'whitespace/comments', 2,
+ 'At least two spaces is best between code and comments')
+
+ # Checks for common mistakes in TODO comments.
+ comment = line[commentpos:]
+ match = _RE_PATTERN_TODO.match(comment)
+ if match:
+ # One whitespace is correct; zero whitespace is handled elsewhere.
+ leading_whitespace = match.group(1)
+ if len(leading_whitespace) > 1:
+ error(filename, linenum, 'whitespace/todo', 2,
+ 'Too many spaces before TODO')
+
+ username = match.group(2)
+ # if not username:
+ # error(filename, linenum, 'readability/todo', 2,
+ # 'Missing username in TODO; it should look like '
+ # '"// TODO(my_username): Stuff."')
+
+ middle_whitespace = match.group(3)
+ # Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison
+ if middle_whitespace != ' ' and middle_whitespace != '':
+ error(filename, linenum, 'whitespace/todo', 2,
+ 'TODO(my_username) should be followed by a space')
+
+ # If the comment contains an alphanumeric character, there
+ # should be a space somewhere between it and the //.
+ if Match(r'//[^ ]*\w', comment):
+ error(filename, linenum, 'whitespace/comments', 4,
+ 'Should have a space between // and comment')
+
+def CheckAccess(filename, clean_lines, linenum, nesting_state, error):
+ """Checks for improper use of DISALLOW* macros.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum] # get rid of comments and strings
+
+ matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|'
+ r'DISALLOW_EVIL_CONSTRUCTORS|'
+ r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line)
+ if not matched:
+ return
+ if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo):
+ if nesting_state.stack[-1].access != 'private':
+ error(filename, linenum, 'readability/constructors', 3,
+ '%s must be in the private: section' % matched.group(1))
+
+ else:
+ # Found DISALLOW* macro outside a class declaration, or perhaps it
+ # was used inside a function when it should have been part of the
+ # class declaration. We could issue a warning here, but it
+ # probably resulted in a compiler error already.
+ pass
+
+
+def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
+ """Checks for the correctness of various spacing issues in the code.
+
+ Things we check for: spaces around operators, spaces after
+ if/for/while/switch, no spaces around parens in function calls, two
+ spaces between code and comment, don't start a block with a blank
+ line, don't end a function with a blank line, don't add a blank line
+ after public/protected/private, don't have too many blank lines in a row.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+
+ # Don't use "elided" lines here, otherwise we can't check commented lines.
+ # Don't want to use "raw" either, because we don't want to check inside C++11
+ # raw strings,
+ raw = clean_lines.lines_without_raw_strings
+ line = raw[linenum]
+
+ # Before nixing comments, check if the line is blank for no good
+ # reason. This includes the first line after a block is opened, and
+ # blank lines at the end of a function (ie, right before a line like '}'
+ #
+ # Skip all the blank line checks if we are immediately inside a
+ # namespace body. In other words, don't issue blank line warnings
+ # for this block:
+ # namespace {
+ #
+ # }
+ #
+ # A warning about missing end of namespace comments will be issued instead.
+ #
+ # Also skip blank line checks for 'extern "C"' blocks, which are formatted
+ # like namespaces.
+ if (IsBlankLine(line) and
+ not nesting_state.InNamespaceBody() and
+ not nesting_state.InExternC()):
+ elided = clean_lines.elided
+ prev_line = elided[linenum - 1]
+ prevbrace = prev_line.rfind('{')
+ # TODO(unknown): Don't complain if line before blank line, and line after,
+ # both start with alnums and are indented the same amount.
+ # This ignores whitespace at the start of a namespace block
+ # because those are not usually indented.
+ if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1:
+ # OK, we have a blank line at the start of a code block. Before we
+ # complain, we check if it is an exception to the rule: The previous
+ # non-empty line has the parameters of a function header that are indented
+ # 4 spaces (because they did not fit in a 80 column line when placed on
+ # the same line as the function name). We also check for the case where
+ # the previous line is indented 6 spaces, which may happen when the
+ # initializers of a constructor do not fit into a 80 column line.
+ exception = False
+ if Match(r' {6}\w', prev_line): # Initializer list?
+ # We are looking for the opening column of initializer list, which
+ # should be indented 4 spaces to cause 6 space indentation afterwards.
+ search_position = linenum-2
+ while (search_position >= 0
+ and Match(r' {6}\w', elided[search_position])):
+ search_position -= 1
+ exception = (search_position >= 0
+ and elided[search_position][:5] == ' :')
+ else:
+ # Search for the function arguments or an initializer list. We use a
+ # simple heuristic here: If the line is indented 4 spaces; and we have a
+ # closing paren, without the opening paren, followed by an opening brace
+ # or colon (for initializer lists) we assume that it is the last line of
+ # a function header. If we have a colon indented 4 spaces, it is an
+ # initializer list.
+ exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)',
+ prev_line)
+ or Match(r' {4}:', prev_line))
+
+ if not exception:
+ error(filename, linenum, 'whitespace/blank_line', 2,
+ 'Redundant blank line at the start of a code block '
+ 'should be deleted.')
+ # Ignore blank lines at the end of a block in a long if-else
+ # chain, like this:
+ # if (condition1) {
+ # // Something followed by a blank line
+ #
+ # } else if (condition2) {
+ # // Something else
+ # }
+ if linenum + 1 < clean_lines.NumLines():
+ next_line = raw[linenum + 1]
+ if (next_line
+ and Match(r'\s*}', next_line)
+ and next_line.find('} else ') == -1):
+ error(filename, linenum, 'whitespace/blank_line', 3,
+ 'Redundant blank line at the end of a code block '
+ 'should be deleted.')
+
+ # matched = Match(r'\s*(public|protected|private):', prev_line)
+ # if matched:
+ # error(filename, linenum, 'whitespace/blank_line', 3,
+ # 'Do not leave a blank line after "%s:"' % matched.group(1))
+
+ # Next, check comments
+ next_line_start = 0
+ if linenum + 1 < clean_lines.NumLines():
+ next_line = raw[linenum + 1]
+ next_line_start = len(next_line) - len(next_line.lstrip())
+ CheckComment(line, filename, linenum, next_line_start, error)
+
+ # get rid of comments and strings
+ line = clean_lines.elided[linenum]
+
+ # You shouldn't have spaces before your brackets, except maybe after
+ # 'delete []' or 'return []() {};'
+ if Search(r'\w\s+\[', line) and not Search(r'(?:delete|return)\s+\[', line):
+ error(filename, linenum, 'whitespace/braces', 5,
+ 'Extra space before [')
+
+ # In range-based for, we wanted spaces before and after the colon, but
+ # not around "::" tokens that might appear.
+ if (Search(r'for *\(.*[^:]:[^: ]', line) or
+ Search(r'for *\(.*[^: ]:[^:]', line)):
+ error(filename, linenum, 'whitespace/forcolon', 2,
+ 'Missing space around colon in range-based for loop')
+
+
+def CheckOperatorSpacing(filename, clean_lines, linenum, error):
+ """Checks for horizontal spacing around operators.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Don't try to do spacing checks for operator methods. Do this by
+ # replacing the troublesome characters with something else,
+ # preserving column position for all other characters.
+ #
+ # The replacement is done repeatedly to avoid false positives from
+ # operators that call operators.
+ while True:
+ match = Match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line)
+ if match:
+ line = match.group(1) + ('_' * len(match.group(2))) + match.group(3)
+ else:
+ break
+
+ # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )".
+ # Otherwise not. Note we only check for non-spaces on *both* sides;
+ # sometimes people put non-spaces on one side when aligning ='s among
+ # many lines (not that this is behavior that I approve of...)
+ if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line):
+ error(filename, linenum, 'whitespace/operators', 4,
+ 'Missing spaces around =')
+
+ # It's ok not to have spaces around binary operators like + - * /, but if
+ # there's too little whitespace, we get concerned. It's hard to tell,
+ # though, so we punt on this one for now. TODO.
+
+ # You should always have whitespace around binary operators.
+ #
+ # Check <= and >= first to avoid false positives with < and >, then
+ # check non-include lines for spacing around < and >.
+ #
+ # If the operator is followed by a comma, assume it's be used in a
+ # macro context and don't do any checks. This avoids false
+ # positives.
+ #
+ # Note that && is not included here. Those are checked separately
+ # in CheckRValueReference
+ match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line)
+ if match:
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around %s' % match.group(1))
+ elif not Match(r'#.*include', line):
+ # Look for < that is not surrounded by spaces. This is only
+ # triggered if both sides are missing spaces, even though
+ # technically should should flag if at least one side is missing a
+ # space. This is done to avoid some false positives with shifts.
+ match = Match(r'^(.*[^\s<])<[^\s=<,]', line)
+ if match:
+ (_, _, end_pos) = CloseExpression(
+ clean_lines, linenum, len(match.group(1)))
+ if end_pos <= -1:
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around <')
+
+ # Look for > that is not surrounded by spaces. Similar to the
+ # above, we only trigger if both sides are missing spaces to avoid
+ # false positives with shifts.
+ match = Match(r'^(.*[^-\s>])>[^\s=>,]', line)
+ if match:
+ (_, _, start_pos) = ReverseCloseExpression(
+ clean_lines, linenum, len(match.group(1)))
+ if start_pos <= -1:
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around >')
+
+ # We allow no-spaces around << when used like this: 10<<20, but
+ # not otherwise (particularly, not when used as streams)
+ # We also allow operators following an opening parenthesis, since
+ # those tend to be macros that deal with operators.
+ match = Search(r'(operator|\S)(?:L|UL|ULL|l|ul|ull)?<<([^\s,=])', line)
+ if (match and match.group(1) != '(' and
+ not (match.group(1).isdigit() and match.group(2).isdigit()) and
+ not (match.group(1) == 'operator' and match.group(2) == ';')):
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around <<')
+
+ # We allow no-spaces around >> for almost anything. This is because
+ # C++11 allows ">>" to close nested templates, which accounts for
+ # most cases when ">>" is not followed by a space.
+ #
+ # We still warn on ">>" followed by alpha character, because that is
+ # likely due to ">>" being used for right shifts, e.g.:
+ # value >> alpha
+ #
+ # When ">>" is used to close templates, the alphanumeric letter that
+ # follows would be part of an identifier, and there should still be
+ # a space separating the template type and the identifier.
+ # type<type<type>> alpha
+ match = Search(r'>>[a-zA-Z_]', line)
+ if match:
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around >>')
+
+ # There shouldn't be space around unary operators
+ match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line)
+ if match:
+ error(filename, linenum, 'whitespace/operators', 4,
+ 'Extra space for operator %s' % match.group(1))
+
+
+def CheckParenthesisSpacing(filename, clean_lines, linenum, error):
+ """Checks for horizontal spacing around parentheses.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # No spaces after an if, while, switch, or for
+ match = Search(r' (if\(|for\(|while\(|switch\()', line)
+ if match:
+ error(filename, linenum, 'whitespace/parens', 5,
+ 'Missing space before ( in %s' % match.group(1))
+
+ # For if/for/while/switch, the left and right parens should be
+ # consistent about how many spaces are inside the parens, and
+ # there should either be zero or one spaces inside the parens.
+ # We don't want: "if ( foo)" or "if ( foo )".
+ # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed.
+ match = Search(r'\b(if|for|while|switch)\s*'
+ r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$',
+ line)
+ if match:
+ if len(match.group(2)) != len(match.group(4)):
+ if not (match.group(3) == ';' and
+ len(match.group(2)) == 1 + len(match.group(4)) or
+ not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)):
+ error(filename, linenum, 'whitespace/parens', 5,
+ 'Mismatching spaces inside () in %s' % match.group(1))
+ if len(match.group(2)) not in [0, 1]:
+ error(filename, linenum, 'whitespace/parens', 5,
+ 'Should have zero or one spaces inside ( and ) in %s' %
+ match.group(1))
+
+
+def CheckCommaSpacing(filename, clean_lines, linenum, error):
+ """Checks for horizontal spacing near commas and semicolons.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ raw = clean_lines.lines_without_raw_strings
+ line = clean_lines.elided[linenum]
+
+ # You should always have a space after a comma (either as fn arg or operator)
+ #
+ # This does not apply when the non-space character following the
+ # comma is another comma, since the only time when that happens is
+ # for empty macro arguments.
+ #
+ # We run this check in two passes: first pass on elided lines to
+ # verify that lines contain missing whitespaces, second pass on raw
+ # lines to confirm that those missing whitespaces are not due to
+ # elided comments.
+ if Search(r',[^,\s]', line) and Search(r',[^,\s]', raw[linenum]):
+ error(filename, linenum, 'whitespace/comma', 3,
+ 'Missing space after ,')
+
+ # You should always have a space after a semicolon
+ # except for few corner cases
+ # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more
+ # space after ;
+ if Search(r';[^\s};\\)/]', line):
+ error(filename, linenum, 'whitespace/semicolon', 3,
+ 'Missing space after ;')
+
+
+def CheckBracesSpacing(filename, clean_lines, linenum, error):
+ """Checks for horizontal spacing near commas.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Except after an opening paren, or after another opening brace (in case of
+ # an initializer list, for instance), you should have spaces before your
+ # braces. And since you should never have braces at the beginning of a line,
+ # this is an easy test.
+ match = Match(r'^(.*[^ ({]){', line)
+ if match:
+ # Try a bit harder to check for brace initialization. This
+ # happens in one of the following forms:
+ # Constructor() : initializer_list_{} { ... }
+ # Constructor{}.MemberFunction()
+ # Type variable{};
+ # FunctionCall(type{}, ...);
+ # LastArgument(..., type{});
+ # LOG(INFO) << type{} << " ...";
+ # map_of_type[{...}] = ...;
+ # ternary = expr ? new type{} : nullptr;
+ # OuterTemplate<InnerTemplateConstructor<Type>{}>
+ #
+ # We check for the character following the closing brace, and
+ # silence the warning if it's one of those listed above, i.e.
+ # "{.;,)<>]:".
+ #
+ # To account for nested initializer list, we allow any number of
+ # closing braces up to "{;,)<". We can't simply silence the
+ # warning on first sight of closing brace, because that would
+ # cause false negatives for things that are not initializer lists.
+ # Silence this: But not this:
+ # Outer{ if (...) {
+ # Inner{...} if (...){ // Missing space before {
+ # }; }
+ #
+ # There is a false negative with this approach if people inserted
+ # spurious semicolons, e.g. "if (cond){};", but we will catch the
+ # spurious semicolon with a separate check.
+ (endline, endlinenum, endpos) = CloseExpression(
+ clean_lines, linenum, len(match.group(1)))
+ trailing_text = ''
+ if endpos > -1:
+ trailing_text = endline[endpos:]
+ for offset in xrange(endlinenum + 1,
+ min(endlinenum + 3, clean_lines.NumLines() - 1)):
+ trailing_text += clean_lines.elided[offset]
+ if not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text):
+ error(filename, linenum, 'whitespace/braces', 5,
+ 'Missing space before {')
+
+ # Make sure '} else {' has spaces.
+ if Search(r'}else', line):
+ error(filename, linenum, 'whitespace/braces', 5,
+ 'Missing space before else')
+
+ # You shouldn't have a space before a semicolon at the end of the line.
+ # There's a special case for "for" since the style guide allows space before
+ # the semicolon there.
+ if Search(r':\s*;\s*$', line):
+ error(filename, linenum, 'whitespace/semicolon', 5,
+ 'Semicolon defining empty statement. Use {} instead.')
+ elif Search(r'^\s*;\s*$', line):
+ error(filename, linenum, 'whitespace/semicolon', 5,
+ 'Line contains only semicolon. If this should be an empty statement, '
+ 'use {} instead.')
+ elif (Search(r'\s+;\s*$', line) and
+ not Search(r'\bfor\b', line)):
+ error(filename, linenum, 'whitespace/semicolon', 5,
+ 'Extra space before last semicolon. If this should be an empty '
+ 'statement, use {} instead.')
+
+
+def IsDecltype(clean_lines, linenum, column):
+ """Check if the token ending on (linenum, column) is decltype().
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: the number of the line to check.
+ column: end column of the token to check.
+ Returns:
+ True if this token is decltype() expression, False otherwise.
+ """
+ (text, _, start_col) = ReverseCloseExpression(clean_lines, linenum, column)
+ if start_col < 0:
+ return False
+ if Search(r'\bdecltype\s*$', text[0:start_col]):
+ return True
+ return False
+
+
+def IsTemplateParameterList(clean_lines, linenum, column):
+ """Check if the token ending on (linenum, column) is the end of template<>.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: the number of the line to check.
+ column: end column of the token to check.
+ Returns:
+ True if this token is end of a template parameter list, False otherwise.
+ """
+ (_, startline, startpos) = ReverseCloseExpression(
+ clean_lines, linenum, column)
+ if (startpos > -1 and
+ Search(r'\btemplate\s*$', clean_lines.elided[startline][0:startpos])):
+ return True
+ return False
+
+
+def IsRValueType(clean_lines, nesting_state, linenum, column):
+ """Check if the token ending on (linenum, column) is a type.
+
+ Assumes that text to the right of the column is "&&" or a function
+ name.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ linenum: the number of the line to check.
+ column: end column of the token to check.
+ Returns:
+ True if this token is a type, False if we are not sure.
+ """
+ prefix = clean_lines.elided[linenum][0:column]
+
+ # Get one word to the left. If we failed to do so, this is most
+ # likely not a type, since it's unlikely that the type name and "&&"
+ # would be split across multiple lines.
+ match = Match(r'^(.*)(\b\w+|[>*)&])\s*$', prefix)
+ if not match:
+ return False
+
+ # Check text following the token. If it's "&&>" or "&&," or "&&...", it's
+ # most likely a rvalue reference used inside a template.
+ suffix = clean_lines.elided[linenum][column:]
+ if Match(r'&&\s*(?:[>,]|\.\.\.)', suffix):
+ return True
+
+ # Check for simple type and end of templates:
+ # int&& variable
+ # vector<int>&& variable
+ #
+ # Because this function is called recursively, we also need to
+ # recognize pointer and reference types:
+ # int* Function()
+ # int& Function()
+ if match.group(2) in ['char', 'char16_t', 'char32_t', 'wchar_t', 'bool',
+ 'short', 'int', 'long', 'signed', 'unsigned',
+ 'float', 'double', 'void', 'auto', '>', '*', '&']:
+ return True
+
+ # If we see a close parenthesis, look for decltype on the other side.
+ # decltype would unambiguously identify a type, anything else is
+ # probably a parenthesized expression and not a type.
+ if match.group(2) == ')':
+ return IsDecltype(
+ clean_lines, linenum, len(match.group(1)) + len(match.group(2)) - 1)
+
+ # Check for casts and cv-qualifiers.
+ # match.group(1) remainder
+ # -------------- ---------
+ # const_cast< type&&
+ # const type&&
+ # type const&&
+ if Search(r'\b(?:const_cast\s*<|static_cast\s*<|dynamic_cast\s*<|'
+ r'reinterpret_cast\s*<|\w+\s)\s*$',
+ match.group(1)):
+ return True
+
+ # Look for a preceding symbol that might help differentiate the context.
+ # These are the cases that would be ambiguous:
+ # match.group(1) remainder
+ # -------------- ---------
+ # Call ( expression &&
+ # Declaration ( type&&
+ # sizeof ( type&&
+ # if ( expression &&
+ # while ( expression &&
+ # for ( type&&
+ # for( ; expression &&
+ # statement ; type&&
+ # block { type&&
+ # constructor { expression &&
+ start = linenum
+ line = match.group(1)
+ match_symbol = None
+ while start >= 0:
+ # We want to skip over identifiers and commas to get to a symbol.
+ # Commas are skipped so that we can find the opening parenthesis
+ # for function parameter lists.
+ match_symbol = Match(r'^(.*)([^\w\s,])[\w\s,]*$', line)
+ if match_symbol:
+ break
+ start -= 1
+ line = clean_lines.elided[start]
+
+ if not match_symbol:
+ # Probably the first statement in the file is an rvalue reference
+ return True
+
+ if match_symbol.group(2) == '}':
+ # Found closing brace, probably an indicate of this:
+ # block{} type&&
+ return True
+
+ if match_symbol.group(2) == ';':
+ # Found semicolon, probably one of these:
+ # for(; expression &&
+ # statement; type&&
+
+ # Look for the previous 'for(' in the previous lines.
+ before_text = match_symbol.group(1)
+ for i in xrange(start - 1, max(start - 6, 0), -1):
+ before_text = clean_lines.elided[i] + before_text
+ if Search(r'for\s*\([^{};]*$', before_text):
+ # This is the condition inside a for-loop
+ return False
+
+ # Did not find a for-init-statement before this semicolon, so this
+ # is probably a new statement and not a condition.
+ return True
+
+ if match_symbol.group(2) == '{':
+ # Found opening brace, probably one of these:
+ # block{ type&& = ... ; }
+ # constructor{ expression && expression }
+
+ # Look for a closing brace or a semicolon. If we see a semicolon
+ # first, this is probably a rvalue reference.
+ line = clean_lines.elided[start][0:len(match_symbol.group(1)) + 1]
+ end = start
+ depth = 1
+ while True:
+ for ch in line:
+ if ch == ';':
+ return True
+ elif ch == '{':
+ depth += 1
+ elif ch == '}':
+ depth -= 1
+ if depth == 0:
+ return False
+ end += 1
+ if end >= clean_lines.NumLines():
+ break
+ line = clean_lines.elided[end]
+ # Incomplete program?
+ return False
+
+ if match_symbol.group(2) == '(':
+ # Opening parenthesis. Need to check what's to the left of the
+ # parenthesis. Look back one extra line for additional context.
+ before_text = match_symbol.group(1)
+ if linenum > 1:
+ before_text = clean_lines.elided[linenum - 1] + before_text
+ before_text = match_symbol.group(1)
+
+ # Patterns that are likely to be types:
+ # [](type&&
+ # for (type&&
+ # sizeof(type&&
+ # operator=(type&&
+ #
+ if Search(r'(?:\]|\bfor|\bsizeof|\boperator\s*\S+\s*)\s*$', before_text):
+ return True
+
+ # Patterns that are likely to be expressions:
+ # if (expression &&
+ # while (expression &&
+ # : initializer(expression &&
+ # , initializer(expression &&
+ # ( FunctionCall(expression &&
+ # + FunctionCall(expression &&
+ # + (expression &&
+ #
+ # The last '+' represents operators such as '+' and '-'.
+ if Search(r'(?:\bif|\bwhile|[-+=%^(<!?:,&*]\s*)$', before_text):
+ return False
+
+ # Something else. Check that tokens to the left look like
+ # return_type function_name
+ match_func = Match(r'^(.*)\s+\w(?:\w|::)*(?:<[^<>]*>)?\s*$',
+ match_symbol.group(1))
+ if match_func:
+ # Check for constructors, which don't have return types.
+ if Search(r'\bexplicit$', match_func.group(1)):
+ return True
+ implicit_constructor = Match(r'\s*(\w+)\((?:const\s+)?(\w+)', prefix)
+ if (implicit_constructor and
+ implicit_constructor.group(1) == implicit_constructor.group(2)):
+ return True
+ return IsRValueType(clean_lines, nesting_state, linenum,
+ len(match_func.group(1)))
+
+ # Nothing before the function name. If this is inside a block scope,
+ # this is probably a function call.
+ return not (nesting_state.previous_stack_top and
+ nesting_state.previous_stack_top.IsBlockInfo())
+
+ if match_symbol.group(2) == '>':
+ # Possibly a closing bracket, check that what's on the other side
+ # looks like the start of a template.
+ return IsTemplateParameterList(
+ clean_lines, start, len(match_symbol.group(1)))
+
+ # Some other symbol, usually something like "a=b&&c". This is most
+ # likely not a type.
+ return False
+
+
+def IsRValueAllowed(clean_lines, linenum):
+ """Check if RValue reference is allowed within some range of lines.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ Returns:
+ True if line is within the region where RValue references are allowed.
+ """
+ for i in xrange(linenum, 0, -1):
+ line = clean_lines.elided[i]
+ if Match(r'GOOGLE_ALLOW_RVALUE_REFERENCES_(?:PUSH|POP)', line):
+ if not line.endswith('PUSH'):
+ return False
+ for j in xrange(linenum, clean_lines.NumLines(), 1):
+ line = clean_lines.elided[j]
+ if Match(r'GOOGLE_ALLOW_RVALUE_REFERENCES_(?:PUSH|POP)', line):
+ return line.endswith('POP')
+ return False
+
+
+def CheckRValueReference(filename, clean_lines, linenum, nesting_state, error):
+ """Check for rvalue references.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+ # Find lines missing spaces around &&.
+ # TODO(unknown): currently we don't check for rvalue references
+ # with spaces surrounding the && to avoid false positives with
+ # boolean expressions.
+ line = clean_lines.elided[linenum]
+ match = Match(r'^(.*\S)&&', line)
+ if not match:
+ match = Match(r'(.*)&&\S', line)
+ if (not match) or '(&&)' in line or Search(r'\boperator\s*$', match.group(1)):
+ return
+
+ # Either poorly formed && or an rvalue reference, check the context
+ # to get a more accurate error message. Mostly we want to determine
+ # if what's to the left of "&&" is a type or not.
+ and_pos = len(match.group(1))
+ if IsRValueType(clean_lines, nesting_state, linenum, and_pos):
+ if not IsRValueAllowed(clean_lines, linenum):
+ error(filename, linenum, 'build/c++11', 3,
+ 'RValue references are an unapproved C++ feature.')
+ else:
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around &&')
+
+
+def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error):
+ """Checks for additional blank line issues related to sections.
+
+ Currently the only thing checked here is blank line before protected/private.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ class_info: A _ClassInfo objects.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ # Skip checks if the class is small, where small means 25 lines or less.
+ # 25 lines seems like a good cutoff since that's the usual height of
+ # terminals, and any class that can't fit in one screen can't really
+ # be considered "small".
+ #
+ # Also skip checks if we are on the first line. This accounts for
+ # classes that look like
+ # class Foo { public: ... };
+ #
+ # If we didn't find the end of the class, last_line would be zero,
+ # and the check will be skipped by the first condition.
+ if (class_info.last_line - class_info.starting_linenum <= 24 or
+ linenum <= class_info.starting_linenum):
+ return
+
+ matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum])
+ if matched:
+ # Issue warning if the line before public/protected/private was
+ # not a blank line, but don't do this if the previous line contains
+ # "class" or "struct". This can happen two ways:
+ # - We are at the beginning of the class.
+ # - We are forward-declaring an inner class that is semantically
+ # private, but needed to be public for implementation reasons.
+ # Also ignores cases where the previous line ends with a backslash as can be
+ # common when defining classes in C macros.
+ prev_line = clean_lines.lines[linenum - 1]
+ if (not IsBlankLine(prev_line) and
+ not Search(r'\b(class|struct)\b', prev_line) and
+ not Search(r'\\$', prev_line)):
+ # Try a bit harder to find the beginning of the class. This is to
+ # account for multi-line base-specifier lists, e.g.:
+ # class Derived
+ # : public Base {
+ end_class_head = class_info.starting_linenum
+ for i in range(class_info.starting_linenum, linenum):
+ if Search(r'\{\s*$', clean_lines.lines[i]):
+ end_class_head = i
+ break
+ if end_class_head < linenum - 1:
+ error(filename, linenum, 'whitespace/blank_line', 3,
+ '"%s:" should be preceded by a blank line' % matched.group(1))
+
+
+def GetPreviousNonBlankLine(clean_lines, linenum):
+ """Return the most recent non-blank line and its line number.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file contents.
+ linenum: The number of the line to check.
+
+ Returns:
+ A tuple with two elements. The first element is the contents of the last
+ non-blank line before the current line, or the empty string if this is the
+ first non-blank line. The second is the line number of that line, or -1
+ if this is the first non-blank line.
+ """
+
+ prevlinenum = linenum - 1
+ while prevlinenum >= 0:
+ prevline = clean_lines.elided[prevlinenum]
+ if not IsBlankLine(prevline): # if not a blank line...
+ return (prevline, prevlinenum)
+ prevlinenum -= 1
+ return ('', -1)
+
+
+def CheckBraces(filename, clean_lines, linenum, error):
+ """Looks for misplaced braces (e.g. at the end of line).
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+
+ line = clean_lines.elided[linenum] # get rid of comments and strings
+
+ #if Match(r'\s*{\s*$', line):
+ # # We allow an open brace to start a line in the case where someone is using
+ # # braces in a block to explicitly create a new scope, which is commonly used
+ # # to control the lifetime of stack-allocated variables. Braces are also
+ # # used for brace initializers inside function calls. We don't detect this
+ # # perfectly: we just don't complain if the last non-whitespace character on
+ # # the previous non-blank line is ',', ';', ':', '(', '{', or '}', or if the
+ # # previous line starts a preprocessor block.
+ # prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
+ # if (not Search(r'[,;:}{()]\s*$', prevline) and
+ # not Match(r'\s*#', prevline) and
+ # os.path.splitext(filename)[1] != ".hh"):
+ # #not Match(r'\s*}\s*', prevline) and
+ # #not Match(r'^\s*{\s*&', prevline)):
+ # error(filename, linenum, 'whitespace/braces', 4,
+ # '{ should never be at the end of the previous line')
+
+ # An else clause should not be on the same line as the preceding closing brace.
+ if Match(r'\s*}\s*else\b\s*(?:if\b|\{|$)', line):
+ # prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
+ #if Match(r'\s*}\s*$', line):
+ error(filename, linenum, 'whitespace/newline', 4,
+ 'An else should not appear on the same line as the preceding }')
+ if Match(r'\s*else.*{$', line):
+ error(filename, linenum, 'whitespace/newline', 4,
+ 'An else should not appear on the same line as the next {')
+
+ # If braces come on one side of an else, they should be on both.
+ # However, we have to worry about "else if" that spans multiple lines!
+ # if Search(r'else if\s*\(', line): # could be multi-line if
+ # brace_on_left = bool(Search(r'}\s*else if\s*\(', line))
+ # # find the ( after the if
+ # pos = line.find('else if')
+ # pos = line.find('(', pos)
+ # if pos > 0:
+ # (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos)
+ # brace_on_right = endline[endpos:].find('{') != -1
+ # if brace_on_left != brace_on_right: # must be brace after if
+ # error(filename, linenum, 'readability/braces', 5,
+ # 'If an else has a brace on one side, it should have it on both')
+ # elif Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line):
+ # error(filename, linenum, 'readability/braces', 5,
+ # 'If an else has a brace on one side, it should have it on both')
+
+ # Likewise, an else should never have the else clause on the same line
+ if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line):
+ error(filename, linenum, 'whitespace/newline', 4,
+ 'Else clause should never be on same line as else (use 2 lines)')
+
+ # In the same way, a do/while should never be on one line
+ if Match(r'\s*do [^\s{]', line):
+ error(filename, linenum, 'whitespace/newline', 4,
+ 'do/while clauses should not be on a single line')
+
+ # Check single-line if/else bodies. The style guide says 'curly braces are not
+ # required for single-line statements'. We additionally allow multi-line,
+ # single statements, but we reject anything with more than one semicolon in
+ # it. This means that the first semicolon after the if should be at the end of
+ # its line, and the line after that should have an indent level equal to or
+ # lower than the if. We also check for ambiguous if/else nesting without
+ # braces.
+ if_else_match = Search(r'\b(if\s*\(|else\b)', line)
+ if if_else_match and not Match(r'\s*#', line):
+ if_indent = GetIndentLevel(line)
+ endline, endlinenum, endpos = line, linenum, if_else_match.end()
+ if_match = Search(r'\bif\s*\(', line)
+ if if_match:
+ # This could be a multiline if condition, so find the end first.
+ pos = if_match.end() - 1
+ (endline, endlinenum, endpos) = CloseExpression(clean_lines, linenum, pos)
+ # Check for an opening brace, either directly after the if or on the next
+ # line. If found, this isn't a single-statement conditional.
+ if (not Match(r'\s*{', endline[endpos:])
+ and not (Match(r'\s*$', endline[endpos:])
+ and endlinenum < (len(clean_lines.elided) - 1)
+ and Match(r'\s*{', clean_lines.elided[endlinenum + 1]))):
+ while (endlinenum < len(clean_lines.elided)
+ and ';' not in clean_lines.elided[endlinenum][endpos:]):
+ endlinenum += 1
+ endpos = 0
+ if endlinenum < len(clean_lines.elided):
+ endline = clean_lines.elided[endlinenum]
+ # We allow a mix of whitespace and closing braces (e.g. for one-liner
+ # methods) and a single \ after the semicolon (for macros)
+ endpos = endline.find(';')
+ if not Match(r';[\s}]*(\\?)$', endline[endpos:]):
+ # Semicolon isn't the last character, there's something trailing
+ error(filename, linenum, 'readability/braces', 4,
+ 'If/else bodies with multiple statements require braces')
+ elif endlinenum < len(clean_lines.elided) - 1:
+ # Make sure the next line is dedented
+ next_line = clean_lines.elided[endlinenum + 1]
+ next_indent = GetIndentLevel(next_line)
+ # With ambiguous nested if statements, this will error out on the
+ # if that *doesn't* match the else, regardless of whether it's the
+ # inner one or outer one.
+ if (if_match and Match(r'\s*else\b', next_line)
+ and next_indent != if_indent):
+ error(filename, linenum, 'readability/braces', 4,
+ 'Else clause should be indented at the same level as if. '
+ 'Ambiguous nested if/else chains require braces.')
+ elif next_indent > if_indent:
+ error(filename, linenum, 'readability/braces', 4,
+ 'If/else bodies with multiple statements require braces')
+
+
+def CheckTrailingSemicolon(filename, clean_lines, linenum, error):
+ """Looks for redundant trailing semicolon.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+
+ line = clean_lines.elided[linenum]
+
+ # Block bodies should not be followed by a semicolon. Due to C++11
+ # brace initialization, there are more places where semicolons are
+ # required than not, so we use a whitelist approach to check these
+ # rather than a blacklist. These are the places where "};" should
+ # be replaced by just "}":
+ # 1. Some flavor of block following closing parenthesis:
+ # for (;;) {};
+ # while (...) {};
+ # switch (...) {};
+ # Function(...) {};
+ # if (...) {};
+ # if (...) else if (...) {};
+ #
+ # 2. else block:
+ # if (...) else {};
+ #
+ # 3. const member function:
+ # Function(...) const {};
+ #
+ # 4. Block following some statement:
+ # x = 42;
+ # {};
+ #
+ # 5. Block at the beginning of a function:
+ # Function(...) {
+ # {};
+ # }
+ #
+ # Note that naively checking for the preceding "{" will also match
+ # braces inside multi-dimensional arrays, but this is fine since
+ # that expression will not contain semicolons.
+ #
+ # 6. Block following another block:
+ # while (true) {}
+ # {};
+ #
+ # 7. End of namespaces:
+ # namespace {};
+ #
+ # These semicolons seems far more common than other kinds of
+ # redundant semicolons, possibly due to people converting classes
+ # to namespaces. For now we do not warn for this case.
+ #
+ # Try matching case 1 first.
+ match = Match(r'^(.*\)\s*)\{', line)
+ if match:
+ # Matched closing parenthesis (case 1). Check the token before the
+ # matching opening parenthesis, and don't warn if it looks like a
+ # macro. This avoids these false positives:
+ # - macro that defines a base class
+ # - multi-line macro that defines a base class
+ # - macro that defines the whole class-head
+ #
+ # But we still issue warnings for macros that we know are safe to
+ # warn, specifically:
+ # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P
+ # - TYPED_TEST
+ # - INTERFACE_DEF
+ # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED:
+ #
+ # We implement a whitelist of safe macros instead of a blacklist of
+ # unsafe macros, even though the latter appears less frequently in
+ # google code and would have been easier to implement. This is because
+ # the downside for getting the whitelist wrong means some extra
+ # semicolons, while the downside for getting the blacklist wrong
+ # would result in compile errors.
+ #
+ # In addition to macros, we also don't want to warn on compound
+ # literals and lambdas.
+ closing_brace_pos = match.group(1).rfind(')')
+ opening_parenthesis = ReverseCloseExpression(
+ clean_lines, linenum, closing_brace_pos)
+ if opening_parenthesis[2] > -1:
+ line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]]
+ macro = Search(r'\b([A-Z_]+)\s*$', line_prefix)
+ func = Match(r'^(.*\])\s*$', line_prefix)
+ if ((macro and
+ macro.group(1) not in (
+ 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST',
+ 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED',
+ 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or
+ (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or
+ Search(r'\s+=\s*$', line_prefix)):
+ match = None
+ if (match and
+ opening_parenthesis[1] > 1 and
+ Search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])):
+ # Multi-line lambda-expression
+ match = None
+
+ else:
+ # Try matching cases 2-3.
+ match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line)
+ if not match:
+ # Try matching cases 4-6. These are always matched on separate lines.
+ #
+ # Note that we can't simply concatenate the previous line to the
+ # current line and do a single match, otherwise we may output
+ # duplicate warnings for the blank line case:
+ # if (cond) {
+ # // blank line
+ # }
+ prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
+ if prevline and Search(r'[;{}]\s*$', prevline):
+ match = Match(r'^(\s*)\{', line)
+
+ # Check matching closing brace
+ if match:
+ (endline, endlinenum, endpos) = CloseExpression(
+ clean_lines, linenum, len(match.group(1)))
+ if endpos > -1 and Match(r'^\s*;', endline[endpos:]):
+ # Current {} pair is eligible for semicolon check, and we have found
+ # the redundant semicolon, output warning here.
+ #
+ # Note: because we are scanning forward for opening braces, and
+ # outputting warnings for the matching closing brace, if there are
+ # nested blocks with trailing semicolons, we will get the error
+ # messages in reversed order.
+ error(filename, endlinenum, 'readability/braces', 4,
+ "You don't need a ; after a }")
+
+
+def CheckEmptyBlockBody(filename, clean_lines, linenum, error):
+ """Look for empty loop/conditional body with only a single semicolon.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+
+ # Search for loop keywords at the beginning of the line. Because only
+ # whitespaces are allowed before the keywords, this will also ignore most
+ # do-while-loops, since those lines should start with closing brace.
+ #
+ # We also check "if" blocks here, since an empty conditional block
+ # is likely an error.
+ line = clean_lines.elided[linenum]
+ matched = Match(r'\s*(for|while|if)\s*\(', line)
+ if matched:
+ # Find the end of the conditional expression
+ (end_line, end_linenum, end_pos) = CloseExpression(
+ clean_lines, linenum, line.find('('))
+
+ # Output warning if what follows the condition expression is a semicolon.
+ # No warning for all other cases, including whitespace or newline, since we
+ # have a separate check for semicolons preceded by whitespace.
+ if end_pos >= 0 and Match(r';', end_line[end_pos:]):
+ if matched.group(1) == 'if':
+ error(filename, end_linenum, 'whitespace/empty_conditional_body', 5,
+ 'Empty conditional bodies should use {}')
+ elif matched.group(1) != "while":
+ error(filename, end_linenum, 'whitespace/empty_loop_body', 5,
+ 'Empty loop bodies should use {} or continue')
+
+
+def FindCheckMacro(line):
+ """Find a replaceable CHECK-like macro.
+
+ Args:
+ line: line to search on.
+ Returns:
+ (macro name, start position), or (None, -1) if no replaceable
+ macro is found.
+ """
+ for macro in _CHECK_MACROS:
+ i = line.find(macro)
+ if i >= 0:
+ # Find opening parenthesis. Do a regular expression match here
+ # to make sure that we are matching the expected CHECK macro, as
+ # opposed to some other macro that happens to contain the CHECK
+ # substring.
+ matched = Match(r'^(.*\b' + macro + r'\s*)\(', line)
+ if not matched:
+ continue
+ return (macro, len(matched.group(1)))
+ return (None, -1)
+
+
+def CheckCheck(filename, clean_lines, linenum, error):
+ """Checks the use of CHECK and EXPECT macros.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+
+ # Decide the set of replacement macros that should be suggested
+ lines = clean_lines.elided
+ (check_macro, start_pos) = FindCheckMacro(lines[linenum])
+ if not check_macro:
+ return
+
+ # Find end of the boolean expression by matching parentheses
+ (last_line, end_line, end_pos) = CloseExpression(
+ clean_lines, linenum, start_pos)
+ if end_pos < 0:
+ return
+ if linenum == end_line:
+ expression = lines[linenum][start_pos + 1:end_pos - 1]
+ else:
+ expression = lines[linenum][start_pos + 1:]
+ for i in xrange(linenum + 1, end_line):
+ expression += lines[i]
+ expression += last_line[0:end_pos - 1]
+
+ # Parse expression so that we can take parentheses into account.
+ # This avoids false positives for inputs like "CHECK((a < 4) == b)",
+ # which is not replaceable by CHECK_LE.
+ lhs = ''
+ rhs = ''
+ operator = None
+ while expression:
+ matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||'
+ r'==|!=|>=|>|<=|<|\()(.*)$', expression)
+ if matched:
+ token = matched.group(1)
+ if token == '(':
+ # Parenthesized operand
+ expression = matched.group(2)
+ (end, _) = FindEndOfExpressionInLine(expression, 0, ['('])
+ if end < 0:
+ return # Unmatched parenthesis
+ lhs += '(' + expression[0:end]
+ expression = expression[end:]
+ elif token in ('&&', '||'):
+ # Logical and/or operators. This means the expression
+ # contains more than one term, for example:
+ # CHECK(42 < a && a < b);
+ #
+ # These are not replaceable with CHECK_LE, so bail out early.
+ return
+ elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'):
+ # Non-relational operator
+ lhs += token
+ expression = matched.group(2)
+ else:
+ # Relational operator
+ operator = token
+ rhs = matched.group(2)
+ break
+ else:
+ # Unparenthesized operand. Instead of appending to lhs one character
+ # at a time, we do another regular expression match to consume several
+ # characters at once if possible. Trivial benchmark shows that this
+ # is more efficient when the operands are longer than a single
+ # character, which is generally the case.
+ matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression)
+ if not matched:
+ matched = Match(r'^(\s*\S)(.*)$', expression)
+ if not matched:
+ break
+ lhs += matched.group(1)
+ expression = matched.group(2)
+
+ # Only apply checks if we got all parts of the boolean expression
+ if not (lhs and operator and rhs):
+ return
+
+ # Check that rhs do not contain logical operators. We already know
+ # that lhs is fine since the loop above parses out && and ||.
+ if rhs.find('&&') > -1 or rhs.find('||') > -1:
+ return
+
+ # At least one of the operands must be a constant literal. This is
+ # to avoid suggesting replacements for unprintable things like
+ # CHECK(variable != iterator)
+ #
+ # The following pattern matches decimal, hex integers, strings, and
+ # characters (in that order).
+ lhs = lhs.strip()
+ rhs = rhs.strip()
+ match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$'
+ if Match(match_constant, lhs) or Match(match_constant, rhs):
+ # Note: since we know both lhs and rhs, we can provide a more
+ # descriptive error message like:
+ # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42)
+ # Instead of:
+ # Consider using CHECK_EQ instead of CHECK(a == b)
+ #
+ # We are still keeping the less descriptive message because if lhs
+ # or rhs gets long, the error message might become unreadable.
+ error(filename, linenum, 'readability/check', 2,
+ 'Consider using %s instead of %s(a %s b)' % (
+ _CHECK_REPLACEMENT[check_macro][operator],
+ check_macro, operator))
+
+
+def CheckAltTokens(filename, clean_lines, linenum, error):
+ """Check alternative keywords being used in boolean expressions.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Avoid preprocessor lines
+ if Match(r'^\s*#', line):
+ return
+
+ # Last ditch effort to avoid multi-line comments. This will not help
+ # if the comment started before the current line or ended after the
+ # current line, but it catches most of the false positives. At least,
+ # it provides a way to workaround this warning for people who use
+ # multi-line comments in preprocessor macros.
+ #
+ # TODO(unknown): remove this once cpplint has better support for
+ # multi-line comments.
+ if line.find('/*') >= 0 or line.find('*/') >= 0:
+ return
+
+ for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line):
+ error(filename, linenum, 'readability/alt_tokens', 2,
+ 'Use operator %s instead of %s' % (
+ _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1)))
+
+
+def GetLineWidth(line):
+ """Determines the width of the line in column positions.
+
+ Args:
+ line: A string, which may be a Unicode string.
+
+ Returns:
+ The width of the line in column positions, accounting for Unicode
+ combining characters and wide characters.
+ """
+ if isinstance(line, unicode):
+ width = 0
+ for uc in unicodedata.normalize('NFC', line):
+ if unicodedata.east_asian_width(uc) in ('W', 'F'):
+ width += 2
+ elif not unicodedata.combining(uc):
+ width += 1
+ return width
+ else:
+ return len(line)
+
+
+def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
+ error):
+ """Checks rules from the 'C++ style rules' section of cppguide.html.
+
+ Most of these rules are hard to test (naming, comment style), but we
+ do what we can. In particular we check for 2-space indents, line lengths,
+ tab usage, spaces inside code, etc.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ file_extension: The extension (without the dot) of the filename.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+
+ # Don't use "elided" lines here, otherwise we can't check commented lines.
+ # Don't want to use "raw" either, because we don't want to check inside C++11
+ # raw strings,
+ raw_lines = clean_lines.lines_without_raw_strings
+ line = raw_lines[linenum]
+
+ if line.find('\t') != -1:
+ error(filename, linenum, 'whitespace/tab', 1,
+ 'Tab found; better to use spaces')
+
+ # One or three blank spaces at the beginning of the line is weird; it's
+ # hard to reconcile that with 2-space indents.
+ # NOTE: here are the conditions rob pike used for his tests. Mine aren't
+ # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces
+ # if(RLENGTH > 20) complain = 0;
+ # if(match($0, " +(error|private|public|protected):")) complain = 0;
+ # if(match(prev, "&& *$")) complain = 0;
+ # if(match(prev, "\\|\\| *$")) complain = 0;
+ # if(match(prev, "[\",=><] *$")) complain = 0;
+ # if(match($0, " <<")) complain = 0;
+ # if(match(prev, " +for \\(")) complain = 0;
+ # if(prevodd && match(prevprev, " +for \\(")) complain = 0;
+ scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$'
+ classinfo = nesting_state.InnermostClass()
+ initial_spaces = 0
+ cleansed_line = clean_lines.elided[linenum]
+ while initial_spaces < len(line) and line[initial_spaces] == ' ':
+ initial_spaces += 1
+ if line and line[-1].isspace():
+ error(filename, linenum, 'whitespace/end_of_line', 4,
+ 'Line ends in whitespace. Consider deleting these extra spaces.')
+ # There are certain situations we allow one space, notably for
+ # section labels, and also lines containing multi-line raw strings.
+ elif ((initial_spaces == 1 or initial_spaces == 3) and
+ not Match(scope_or_label_pattern, cleansed_line) and
+ not (clean_lines.raw_lines[linenum] != line and
+ Match(r'^\s*""', line))):
+ error(filename, linenum, 'whitespace/indent', 3,
+ 'Weird number of spaces at line-start. '
+ 'Are you using a 2-space indent?')
+
+ # Check if the line is a header guard.
+ is_header_guard = False
+ if file_extension == 'h':
+ cppvar = GetHeaderGuardCPPVariable(filename)
+ if (line.startswith('#ifndef %s' % cppvar) or
+ line.startswith('#define %s' % cppvar) or
+ line.startswith('#endif // %s' % cppvar)):
+ is_header_guard = True
+ # #include lines and header guards can be long, since there's no clean way to
+ # split them.
+ #
+ # URLs can be long too. It's possible to split these, but it makes them
+ # harder to cut&paste.
+ #
+ # The "$Id:...$" comment may also get very long without it being the
+ # developers fault.
+ if (not line.startswith('#include') and not is_header_guard and
+ not Match(r'^\s*//.*http(s?)://\S*$', line) and
+ not Match(r'^// \$Id:.*#[0-9]+ \$$', line)):
+ line_width = GetLineWidth(line)
+ extended_length = int((_line_length * 1.25))
+ if line_width > extended_length:
+ error(filename, linenum, 'whitespace/line_length', 4,
+ 'Lines should very rarely be longer than %i characters' %
+ extended_length)
+ elif line_width > _line_length:
+ error(filename, linenum, 'whitespace/line_length', 2,
+ 'Lines should be <= %i characters long' % _line_length)
+
+ if (cleansed_line.count(';') > 1 and
+ # for loops are allowed two ;'s (and may run over two lines).
+ cleansed_line.find('for') == -1 and
+ (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or
+ GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and
+ # It's ok to have many commands in a switch case that fits in 1 line
+ not ((cleansed_line.find('case ') != -1 or
+ cleansed_line.find('default:') != -1) and
+ cleansed_line.find('break;') != -1)):
+ error(filename, linenum, 'whitespace/newline', 0,
+ 'More than one command on the same line')
+
+ # Some more style checks
+ CheckBraces(filename, clean_lines, linenum, error)
+ CheckTrailingSemicolon(filename, clean_lines, linenum, error)
+ CheckEmptyBlockBody(filename, clean_lines, linenum, error)
+ CheckAccess(filename, clean_lines, linenum, nesting_state, error)
+ CheckSpacing(filename, clean_lines, linenum, nesting_state, error)
+ CheckOperatorSpacing(filename, clean_lines, linenum, error)
+ CheckParenthesisSpacing(filename, clean_lines, linenum, error)
+ CheckCommaSpacing(filename, clean_lines, linenum, error)
+ CheckBracesSpacing(filename, clean_lines, linenum, error)
+ CheckSpacingForFunctionCall(filename, clean_lines, linenum, error)
+ CheckRValueReference(filename, clean_lines, linenum, nesting_state, error)
+ CheckCheck(filename, clean_lines, linenum, error)
+ CheckAltTokens(filename, clean_lines, linenum, error)
+ classinfo = nesting_state.InnermostClass()
+ if classinfo:
+ CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error)
+
+
+_RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"')
+_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$')
+# Matches the first component of a filename delimited by -s and _s. That is:
+# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo'
+# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo'
+# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo'
+# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo'
+_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+')
+
+
+def _DropCommonSuffixes(filename):
+ """Drops common suffixes like _test.cc or -inl.h from filename.
+
+ For example:
+ >>> _DropCommonSuffixes('foo/foo-inl.h')
+ 'foo/foo'
+ >>> _DropCommonSuffixes('foo/bar/foo.cc')
+ 'foo/bar/foo'
+ >>> _DropCommonSuffixes('foo/foo_internal.h')
+ 'foo/foo'
+ >>> _DropCommonSuffixes('foo/foo_unusualinternal.h')
+ 'foo/foo_unusualinternal'
+
+ Args:
+ filename: The input filename.
+
+ Returns:
+ The filename with the common suffix removed.
+ """
+ for suffix in ('test.cc', 'regtest.cc', 'unittest.cc',
+ 'inl.h', 'impl.h', 'internal.h'):
+ if (filename.endswith(suffix) and len(filename) > len(suffix) and
+ filename[-len(suffix) - 1] in ('-', '_')):
+ return filename[:-len(suffix) - 1]
+ return os.path.splitext(filename)[0]
+
+
+def _IsTestFilename(filename):
+ """Determines if the given filename has a suffix that identifies it as a test.
+
+ Args:
+ filename: The input filename.
+
+ Returns:
+ True if 'filename' looks like a test, False otherwise.
+ """
+ if (filename.endswith('_test.cc') or
+ filename.endswith('_unittest.cc') or
+ filename.endswith('_regtest.cc')):
+ return True
+ else:
+ return False
+
+
+def _ClassifyInclude(fileinfo, include, is_system):
+ """Figures out what kind of header 'include' is.
+
+ Args:
+ fileinfo: The current file cpplint is running over. A FileInfo instance.
+ include: The path to a #included file.
+ is_system: True if the #include used <> rather than "".
+
+ Returns:
+ One of the _XXX_HEADER constants.
+
+ For example:
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True)
+ _C_SYS_HEADER
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True)
+ _CPP_SYS_HEADER
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False)
+ _LIKELY_MY_HEADER
+ >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'),
+ ... 'bar/foo_other_ext.h', False)
+ _POSSIBLE_MY_HEADER
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False)
+ _OTHER_HEADER
+ """
+ # This is a list of all standard c++ header files, except
+ # those already checked for above.
+ is_cpp_h = include in _CPP_HEADERS or include.find(".hh") > 0
+
+ if is_system:
+ if is_cpp_h:
+ return _CPP_SYS_HEADER
+ else:
+ return _C_SYS_HEADER
+
+ # If the target file and the include we're checking share a
+ # basename when we drop common extensions, and the include
+ # lives in . , then it's likely to be owned by the target file.
+ target_dir, target_base = (
+ os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName())))
+ include_dir, include_base = os.path.split(_DropCommonSuffixes(include))
+ if target_base == include_base and (
+ include_dir == target_dir or
+ include_dir == os.path.normpath(target_dir + '/../public')):
+ return _LIKELY_MY_HEADER
+
+ # If the target and include share some initial basename
+ # component, it's possible the target is implementing the
+ # include, so it's allowed to be first, but we'll never
+ # complain if it's not there.
+ target_first_component = _RE_FIRST_COMPONENT.match(target_base)
+ include_first_component = _RE_FIRST_COMPONENT.match(include_base)
+ if (target_first_component and include_first_component and
+ target_first_component.group(0) ==
+ include_first_component.group(0)):
+ return _POSSIBLE_MY_HEADER
+
+ return _OTHER_HEADER
+
+
+
+def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
+ """Check rules that are applicable to #include lines.
+
+ Strings on #include lines are NOT removed from elided line, to make
+ certain tasks easier. However, to prevent false positives, checks
+ applicable to #include lines in CheckLanguage must be put here.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ include_state: An _IncludeState instance in which the headers are inserted.
+ error: The function to call with any errors found.
+ """
+ fileinfo = FileInfo(filename)
+ line = clean_lines.lines[linenum]
+
+ # "include" should use the new style "foo/bar.h" instead of just "bar.h"
+ if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line):
+ error(filename, linenum, 'build/include', 4,
+ 'Include the directory when naming .h files')
+
+ # we shouldn't include a file more than once. actually, there are a
+ # handful of instances where doing so is okay, but in general it's
+ # not.
+ match = _RE_PATTERN_INCLUDE.search(line)
+ if match:
+ include = match.group(2)
+ is_system = (match.group(1) == '<')
+ if include in include_state:
+ error(filename, linenum, 'build/include', 4,
+ '"%s" already included at %s:%s' %
+ (include, filename, include_state[include]))
+ else:
+ include_state[include] = linenum
+
+ # We want to ensure that headers appear in the right order:
+ # 1) for foo.cc, foo.h (preferred location)
+ # 2) c system files
+ # 3) cpp system files
+ # 4) for foo.cc, foo.h (deprecated location)
+ # 5) other google headers
+ #
+ # We classify each include statement as one of those 5 types
+ # using a number of techniques. The include_state object keeps
+ # track of the highest type seen, and complains if we see a
+ # lower type after that.
+ error_message = include_state.CheckNextIncludeOrder(
+ _ClassifyInclude(fileinfo, include, is_system))
+ if error_message:
+ error(filename, linenum, 'build/include_order', 4,
+ '%s. Should be: %s.h, c system, c++ system, other.' %
+ (error_message, fileinfo.BaseName()))
+ canonical_include = include_state.CanonicalizeAlphabeticalOrder(include)
+ if not include_state.IsInAlphabeticalOrder(
+ clean_lines, linenum, canonical_include):
+ error(filename, linenum, 'build/include_alpha', 4,
+ 'Include "%s" not in alphabetical order' % include)
+ include_state.SetLastHeader(canonical_include)
+
+ # Look for any of the stream classes that are part of standard C++.
+ match = _RE_PATTERN_INCLUDE.match(line)
+ if match:
+ include = match.group(2)
+ if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include):
+ # Many unit tests use cout, so we exempt them.
+ if not _IsTestFilename(filename):
+ # Suggest a different header for ostream
+ if include == 'ostream':
+ error(filename, linenum, 'readability/streams', 3,
+ 'For logging, include "base/logging.h" instead of <ostream>.')
+ # else:
+ # error(filename, linenum, 'readability/streams', 3,
+ # 'Streams are highly discouraged.')
+
+
+def _GetTextInside(text, start_pattern):
+ r"""Retrieves all the text between matching open and close parentheses.
+
+ Given a string of lines and a regular expression string, retrieve all the text
+ following the expression and between opening punctuation symbols like
+ (, [, or {, and the matching close-punctuation symbol. This properly nested
+ occurrences of the punctuations, so for the text like
+ printf(a(), b(c()));
+ a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'.
+ start_pattern must match string having an open punctuation symbol at the end.
+
+ Args:
+ text: The lines to extract text. Its comments and strings must be elided.
+ It can be single line and can span multiple lines.
+ start_pattern: The regexp string indicating where to start extracting
+ the text.
+ Returns:
+ The extracted text.
+ None if either the opening string or ending punctuation could not be found.
+ """
+ # TODO(unknown): Audit cpplint.py to see what places could be profitably
+ # rewritten to use _GetTextInside (and use inferior regexp matching today).
+
+ # Give opening punctuations to get the matching close-punctuations.
+ matching_punctuation = {'(': ')', '{': '}', '[': ']'}
+ closing_punctuation = set(matching_punctuation.itervalues())
+
+ # Find the position to start extracting text.
+ match = re.search(start_pattern, text, re.M)
+ if not match: # start_pattern not found in text.
+ return None
+ start_position = match.end(0)
+
+ assert start_position > 0, (
+ 'start_pattern must ends with an opening punctuation.')
+ assert text[start_position - 1] in matching_punctuation, (
+ 'start_pattern must ends with an opening punctuation.')
+ # Stack of closing punctuations we expect to have in text after position.
+ punctuation_stack = [matching_punctuation[text[start_position - 1]]]
+ position = start_position
+ while punctuation_stack and position < len(text):
+ if text[position] == punctuation_stack[-1]:
+ punctuation_stack.pop()
+ elif text[position] in closing_punctuation:
+ # A closing punctuation without matching opening punctuations.
+ return None
+ elif text[position] in matching_punctuation:
+ punctuation_stack.append(matching_punctuation[text[position]])
+ position += 1
+ if punctuation_stack:
+ # Opening punctuations left without matching close-punctuations.
+ return None
+ # punctuations match.
+ return text[start_position:position - 1]
+
+
+# Patterns for matching call-by-reference parameters.
+#
+# Supports nested templates up to 2 levels deep using this messy pattern:
+# < (?: < (?: < [^<>]*
+# >
+# | [^<>] )*
+# >
+# | [^<>] )*
+# >
+_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]*
+_RE_PATTERN_TYPE = (
+ r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?'
+ r'(?:\w|'
+ r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|'
+ r'::)+')
+# A call-by-reference parameter ends with '& identifier'.
+_RE_PATTERN_REF_PARAM = re.compile(
+ r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*'
+ r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]')
+# A call-by-const-reference parameter either ends with 'const& identifier'
+# or looks like 'const type& identifier' when 'type' is atomic.
+_RE_PATTERN_CONST_REF_PARAM = (
+ r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT +
+ r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')')
+
+
+def CheckLanguage(filename, clean_lines, linenum, file_extension,
+ include_state, nesting_state, error):
+ """Checks rules from the 'C++ language rules' section of cppguide.html.
+
+ Some of these rules are hard to test (function overloading, using
+ uint32 inappropriately), but we do the best we can.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ file_extension: The extension (without the dot) of the filename.
+ include_state: An _IncludeState instance in which the headers are inserted.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+ # If the line is empty or consists of entirely a comment, no need to
+ # check it.
+ line = clean_lines.elided[linenum]
+ if not line:
+ return
+
+ match = _RE_PATTERN_INCLUDE.search(line)
+ if match:
+ CheckIncludeLine(filename, clean_lines, linenum, include_state, error)
+ return
+
+ # Reset include state across preprocessor directives. This is meant
+ # to silence warnings for conditional includes.
+ if Match(r'^\s*#\s*(?:ifdef|elif|else|endif)\b', line):
+ include_state.ResetSection()
+
+ # Make Windows paths like Unix.
+ fullname = os.path.abspath(filename).replace('\\', '/')
+
+ # Perform other checks now that we are sure that this is not an include line
+ CheckCasts(filename, clean_lines, linenum, error)
+ CheckGlobalStatic(filename, clean_lines, linenum, error)
+ CheckPrintf(filename, clean_lines, linenum, error)
+
+ if file_extension == 'h':
+ # TODO(unknown): check that 1-arg constructors are explicit.
+ # How to tell it's a constructor?
+ # (handled in CheckForNonStandardConstructs for now)
+ # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS
+ # (level 1 error)
+ pass
+
+ # Check if people are using the verboten C basic types. The only exception
+ # we regularly allow is "unsigned short port" for port.
+ if Search(r'\bshort port\b', line):
+ if not Search(r'\bunsigned short port\b', line):
+ error(filename, linenum, 'runtime/int', 4,
+ 'Use "unsigned short" for ports, not "short"')
+ else:
+ match = Search(r'\b(short|long(?! +double)|long long)\b', line)
+ if match:
+ error(filename, linenum, 'runtime/int', 4,
+ 'Use int16/int64/etc, rather than the C type %s' % match.group(1))
+
+ # Check if some verboten operator overloading is going on
+ # TODO(unknown): catch out-of-line unary operator&:
+ # class X {};
+ # int operator&(const X& x) { return 42; } // unary operator&
+ # The trick is it's hard to tell apart from binary operator&:
+ # class Y { int operator&(const Y& x) { return 23; } }; // binary operator&
+ if Search(r'\boperator\s*&\s*\(\s*\)', line):
+ error(filename, linenum, 'runtime/operator', 4,
+ 'Unary operator& is dangerous. Do not use it.')
+
+ # Check for suspicious usage of "if" like
+ # } if (a == b) {
+ if Search(r'\}\s*if\s*\(', line):
+ error(filename, linenum, 'readability/braces', 4,
+ 'Did you mean "else if"? If not, start a new line for "if".')
+
+ # Check for potential format string bugs like printf(foo).
+ # We constrain the pattern not to pick things like DocidForPrintf(foo).
+ # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str())
+ # TODO(unknown): Catch the following case. Need to change the calling
+ # convention of the whole function to process multiple line to handle it.
+ # printf(
+ # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line);
+ printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(')
+ if printf_args:
+ match = Match(r'([\w.\->()]+)$', printf_args)
+ if match and match.group(1) != '__VA_ARGS__':
+ function_name = re.search(r'\b((?:string)?printf)\s*\(',
+ line, re.I).group(1)
+ error(filename, linenum, 'runtime/printf', 4,
+ 'Potential format string bug. Do %s("%%s", %s) instead.'
+ % (function_name, match.group(1)))
+
+ # Check for potential memset bugs like memset(buf, sizeof(buf), 0).
+ match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line)
+ if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)):
+ error(filename, linenum, 'runtime/memset', 4,
+ 'Did you mean "memset(%s, 0, %s)"?'
+ % (match.group(1), match.group(2)))
+
+ # if Search(r'\busing namespace\b', line):
+ # error(filename, linenum, 'build/namespaces', 5,
+ # 'Do not use namespace using-directives. '
+ # 'Use using-declarations instead.')
+
+ # Detect variable-length arrays.
+ match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line)
+ if (match and match.group(2) != 'return' and match.group(2) != 'delete' and
+ match.group(3).find(']') == -1):
+ # Split the size using space and arithmetic operators as delimiters.
+ # If any of the resulting tokens are not compile time constants then
+ # report the error.
+ tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3))
+ is_const = True
+ skip_next = False
+ for tok in tokens:
+ if skip_next:
+ skip_next = False
+ continue
+
+ if Search(r'sizeof\(.+\)', tok): continue
+ if Search(r'arraysize\(\w+\)', tok): continue
+
+ tok = tok.lstrip('(')
+ tok = tok.rstrip(')')
+ if not tok: continue
+ if Match(r'\d+', tok): continue
+ if Match(r'0[xX][0-9a-fA-F]+', tok): continue
+ if Match(r'k[A-Z0-9]\w*', tok): continue
+ if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue
+ if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue
+ # A catch all for tricky sizeof cases, including 'sizeof expression',
+ # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)'
+ # requires skipping the next token because we split on ' ' and '*'.
+ if tok.startswith('sizeof'):
+ skip_next = True
+ continue
+ is_const = False
+ break
+ if not is_const:
+ error(filename, linenum, 'runtime/arrays', 1,
+ 'Do not use variable-length arrays. Use an appropriately named '
+ "('k' followed by CamelCase) compile-time constant for the size.")
+
+ # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or
+ # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing
+ # in the class declaration.
+ match = Match(
+ (r'\s*'
+ r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))'
+ r'\(.*\);$'),
+ line)
+ if match and linenum + 1 < clean_lines.NumLines():
+ next_line = clean_lines.elided[linenum + 1]
+ # We allow some, but not all, declarations of variables to be present
+ # in the statement that defines the class. The [\w\*,\s]* fragment of
+ # the regular expression below allows users to declare instances of
+ # the class or pointers to instances, but not less common types such
+ # as function pointers or arrays. It's a tradeoff between allowing
+ # reasonable code and avoiding trying to parse more C++ using regexps.
+ if not Search(r'^\s*}[\w\*,\s]*;', next_line):
+ error(filename, linenum, 'readability/constructors', 3,
+ match.group(1) + ' should be the last thing in the class')
+
+ # Check for use of unnamed namespaces in header files. Registration
+ # macros are typically OK, so we allow use of "namespace {" on lines
+ # that end with backslashes.
+ if (file_extension == 'h'
+ and Search(r'\bnamespace\s*{', line)
+ and line[-1] != '\\'):
+ error(filename, linenum, 'build/namespaces', 4,
+ 'Do not use unnamed namespaces in header files. See '
+ 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces'
+ ' for more information.')
+
+
+def CheckGlobalStatic(filename, clean_lines, linenum, error):
+ """Check for unsafe global or static objects.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Check for people declaring static/global STL strings at the top level.
+ # This is dangerous because the C++ language does not guarantee that
+ # globals with constructors are initialized before the first access.
+ match = Match(
+ r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)',
+ line)
+ # Remove false positives:
+ # - String pointers (as opposed to values).
+ # string *pointer
+ # const string *pointer
+ # string const *pointer
+ # string *const pointer
+ #
+ # - Functions and template specializations.
+ # string Function<Type>(...
+ # string Class<Type>::Method(...
+ #
+ # - Operators. These are matched separately because operator names
+ # cross non-word boundaries, and trying to match both operators
+ # and functions at the same time would decrease accuracy of
+ # matching identifiers.
+ # string Class::operator*()
+ if (match and
+ not Search(r'\bstring\b(\s+const)?\s*\*\s*(const\s+)?\w', line) and
+ not Search(r'\boperator\W', line) and
+ not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', match.group(3))):
+ error(filename, linenum, 'runtime/string', 4,
+ 'For a static/global string constant, use a C style string instead: '
+ '"%schar %s[]".' %
+ (match.group(1), match.group(2)))
+
+ if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line):
+ error(filename, linenum, 'runtime/init', 4,
+ 'You seem to be initializing a member variable with itself.')
+
+
+def CheckPrintf(filename, clean_lines, linenum, error):
+ """Check for printf related issues.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # When snprintf is used, the second argument shouldn't be a literal.
+ match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line)
+ if match and match.group(2) != '0':
+ # If 2nd arg is zero, snprintf is used to calculate size.
+ error(filename, linenum, 'runtime/printf', 3,
+ 'If you can, use sizeof(%s) instead of %s as the 2nd arg '
+ 'to snprintf.' % (match.group(1), match.group(2)))
+
+ # Check if some verboten C functions are being used.
+ if Search(r'\bsprintf\b', line):
+ error(filename, linenum, 'runtime/printf', 5,
+ 'Never use sprintf. Use snprintf instead.')
+ match = Search(r'\b(strcpy|strcat)\b', line)
+ if match:
+ error(filename, linenum, 'runtime/printf', 4,
+ 'Almost always, snprintf is better than %s' % match.group(1))
+
+
+def IsDerivedFunction(clean_lines, linenum):
+ """Check if current line contains an inherited function.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ Returns:
+ True if current line contains a function with "override"
+ virt-specifier.
+ """
+ # Look for leftmost opening parenthesis on current line
+ opening_paren = clean_lines.elided[linenum].find('(')
+ if opening_paren < 0: return False
+
+ # Look for "override" after the matching closing parenthesis
+ line, _, closing_paren = CloseExpression(clean_lines, linenum, opening_paren)
+ return closing_paren >= 0 and Search(r'\boverride\b', line[closing_paren:])
+
+
+def IsInitializerList(clean_lines, linenum):
+ """Check if current line is inside constructor initializer list.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ Returns:
+ True if current line appears to be inside constructor initializer
+ list, False otherwise.
+ """
+ for i in xrange(linenum, 1, -1):
+ line = clean_lines.elided[i]
+ if i == linenum:
+ remove_function_body = Match(r'^(.*)\{\s*$', line)
+ if remove_function_body:
+ line = remove_function_body.group(1)
+
+ if Search(r'\s:\s*\w+[({]', line):
+ # A lone colon tend to indicate the start of a constructor
+ # initializer list. It could also be a ternary operator, which
+ # also tend to appear in constructor initializer lists as
+ # opposed to parameter lists.
+ return True
+ if Search(r'\}\s*,\s*$', line):
+ # A closing brace followed by a comma is probably the end of a
+ # brace-initialized member in constructor initializer list.
+ return True
+ if Search(r'[{};]\s*$', line):
+ # Found one of the following:
+ # - A closing brace or semicolon, probably the end of the previous
+ # function.
+ # - An opening brace, probably the start of current class or namespace.
+ #
+ # Current line is probably not inside an initializer list since
+ # we saw one of those things without seeing the starting colon.
+ return False
+
+ # Got to the beginning of the file without seeing the start of
+ # constructor initializer list.
+ return False
+
+
+def CheckForNonConstReference(filename, clean_lines, linenum,
+ nesting_state, error):
+ """Check for non-const references.
+
+ Separate from CheckLanguage since it scans backwards from current
+ line, instead of scanning forward.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+ # Do nothing if there is no '&' on current line.
+ line = clean_lines.elided[linenum]
+ if '&' not in line:
+ return
+
+ # If a function is inherited, current function doesn't have much of
+ # a choice, so any non-const references should not be blamed on
+ # derived function.
+ if IsDerivedFunction(clean_lines, linenum):
+ return
+
+ # Long type names may be broken across multiple lines, usually in one
+ # of these forms:
+ # LongType
+ # ::LongTypeContinued &identifier
+ # LongType::
+ # LongTypeContinued &identifier
+ # LongType<
+ # ...>::LongTypeContinued &identifier
+ #
+ # If we detected a type split across two lines, join the previous
+ # line to current line so that we can match const references
+ # accordingly.
+ #
+ # Note that this only scans back one line, since scanning back
+ # arbitrary number of lines would be expensive. If you have a type
+ # that spans more than 2 lines, please use a typedef.
+ if linenum > 1:
+ previous = None
+ if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line):
+ # previous_line\n + ::current_line
+ previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$',
+ clean_lines.elided[linenum - 1])
+ elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line):
+ # previous_line::\n + current_line
+ previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$',
+ clean_lines.elided[linenum - 1])
+ if previous:
+ line = previous.group(1) + line.lstrip()
+ else:
+ # Check for templated parameter that is split across multiple lines
+ endpos = line.rfind('>')
+ if endpos > -1:
+ (_, startline, startpos) = ReverseCloseExpression(
+ clean_lines, linenum, endpos)
+ if startpos > -1 and startline < linenum:
+ # Found the matching < on an earlier line, collect all
+ # pieces up to current line.
+ line = ''
+ for i in xrange(startline, linenum + 1):
+ line += clean_lines.elided[i].strip()
+
+ # Check for non-const references in function parameters. A single '&' may
+ # found in the following places:
+ # inside expression: binary & for bitwise AND
+ # inside expression: unary & for taking the address of something
+ # inside declarators: reference parameter
+ # We will exclude the first two cases by checking that we are not inside a
+ # function body, including one that was just introduced by a trailing '{'.
+ # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare].
+ if (nesting_state.previous_stack_top and
+ not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or
+ isinstance(nesting_state.previous_stack_top, _NamespaceInfo))):
+ # Not at toplevel, not within a class, and not within a namespace
+ return
+
+ # Avoid preprocessors
+ if Search(r'\\\s*$', line):
+ return
+
+ # Avoid constructor initializer lists
+ if IsInitializerList(clean_lines, linenum):
+ return
+
+ # We allow non-const references in a few standard places, like functions
+ # called "swap()" or iostream operators like "<<" or ">>". Do not check
+ # those function parameters.
+ #
+ # We also accept & in static_assert, which looks like a function but
+ # it's actually a declaration expression.
+ whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|'
+ r'operator\s*[<>][<>]|'
+ r'static_assert|COMPILE_ASSERT'
+ r')\s*\(')
+ if Search(whitelisted_functions, line):
+ return
+ elif not Search(r'\S+\([^)]*$', line):
+ # Don't see a whitelisted function on this line. Actually we
+ # didn't see any function name on this line, so this is likely a
+ # multi-line parameter list. Try a bit harder to catch this case.
+ for i in xrange(2):
+ if (linenum > i and
+ Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])):
+ return
+
+ decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body
+ # for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls):
+ # if not Match(_RE_PATTERN_CONST_REF_PARAM, parameter):
+ # error(filename, linenum, 'runtime/references', 2,
+ # 'Is this a non-const reference? '
+ # 'If so, make const or use a pointer: ' +
+ # ReplaceAll(' *<', '<', parameter))
+
+
+def CheckCasts(filename, clean_lines, linenum, error):
+ """Various cast related checks.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Check to see if they're using an conversion function cast.
+ # I just try to capture the most common basic types, though there are more.
+ # Parameterless conversion functions, such as bool(), are allowed as they are
+ # probably a member operator declaration or default constructor.
+ match = Search(
+ r'(\bnew\s+|\S<\s*(?:const\s+)?)?\b'
+ r'(int|float|double|bool|char|int32|uint32|int64|uint64)'
+ r'(\([^)].*)', line)
+ expecting_function = ExpectingFunctionArgs(clean_lines, linenum)
+ if match and not expecting_function:
+ matched_type = match.group(2)
+
+ # matched_new_or_template is used to silence two false positives:
+ # - New operators
+ # - Template arguments with function types
+ #
+ # For template arguments, we match on types immediately following
+ # an opening bracket without any spaces. This is a fast way to
+ # silence the common case where the function type is the first
+ # template argument. False negative with less-than comparison is
+ # avoided because those operators are usually followed by a space.
+ #
+ # function<double(double)> // bracket + no space = false positive
+ # value < double(42) // bracket + space = true positive
+ matched_new_or_template = match.group(1)
+
+ # Other things to ignore:
+ # - Function pointers
+ # - Casts to pointer types
+ # - Placement new
+ # - Alias declarations
+ matched_funcptr = match.group(3)
+ if (matched_new_or_template is None and
+ not (matched_funcptr and
+ (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(',
+ matched_funcptr) or
+ matched_funcptr.startswith('(*)'))) and
+ not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and
+ not Search(r'new\(\S+\)\s*' + matched_type, line)):
+ error(filename, linenum, 'readability/casting', 4,
+ 'Using deprecated casting style. '
+ 'Use static_cast<%s>(...) instead' %
+ matched_type)
+
+ if not expecting_function:
+ CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum],
+ 'static_cast',
+ r'\((int|float|double|bool|char|u?int(16|32|64))\)', error)
+
+ # This doesn't catch all cases. Consider (const char * const)"hello".
+ #
+ # (char *) "foo" should always be a const_cast (reinterpret_cast won't
+ # compile).
+ if CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum],
+ 'const_cast', r'\((char\s?\*+\s?)\)\s*"', error):
+ pass
+ else:
+ # Check pointer casts for other than string constants
+ CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum],
+ 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error)
+
+ # In addition, we look for people taking the address of a cast. This
+ # is dangerous -- casts can assign to temporaries, so the pointer doesn't
+ # point where you think.
+ match = Search(
+ r'(?:&\(([^)]+)\)[\w(])|'
+ r'(?:&(static|dynamic|down|reinterpret)_cast\b)', line)
+ if match and match.group(1) != '*':
+ # Try a better error message when the & is bound to something
+ # dereferenced by the casted pointer, as opposed to the casted
+ # pointer itself.
+ parenthesis_error = False
+ match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line)
+ if match:
+ _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1)))
+ if x1 >= 0 and clean_lines.elided[y1][x1] == '(':
+ _, y2, x2 = CloseExpression(clean_lines, y1, x1)
+ if x2 >= 0:
+ extended_line = clean_lines.elided[y2][x2:]
+ if y2 < clean_lines.NumLines() - 1:
+ extended_line += clean_lines.elided[y2 + 1]
+ if Match(r'\s*(?:->|\[)', extended_line):
+ parenthesis_error = True
+
+ if parenthesis_error:
+ error(filename, linenum, 'readability/casting', 4,
+ ('Are you taking an address of something dereferenced '
+ 'from a cast? Wrapping the dereferenced expression in '
+ 'parentheses will make the binding more obvious'))
+ else:
+ error(filename, linenum, 'runtime/casting', 4,
+ ('Are you taking an address of a cast? '
+ 'This is dangerous: could be a temp var. '
+ 'Take the address before doing the cast, rather than after'))
+
+
+def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern,
+ error):
+ """Checks for a C-style cast by looking for the pattern.
+
+ Args:
+ filename: The name of the current file.
+ linenum: The number of the line to check.
+ line: The line of code to check.
+ raw_line: The raw line of code to check, with comments.
+ cast_type: The string for the C++ cast to recommend. This is either
+ reinterpret_cast, static_cast, or const_cast, depending.
+ pattern: The regular expression used to find C-style casts.
+ error: The function to call with any errors found.
+
+ Returns:
+ True if an error was emitted.
+ False otherwise.
+ """
+ match = Search(pattern, line)
+ if not match:
+ return False
+
+ # Exclude lines with keywords that tend to look like casts, and also
+ # macros which are generally troublesome.
+ if Match(r'.*\b(?:sizeof|alignof|alignas|[A-Z_]+)\s*$',
+ line[0:match.start(1) - 1]):
+ return False
+
+ # operator++(int) and operator--(int)
+ if (line[0:match.start(1) - 1].endswith(' operator++') or
+ line[0:match.start(1) - 1].endswith(' operator--')):
+ return False
+
+ # A single unnamed argument for a function tends to look like old
+ # style cast. If we see those, don't issue warnings for deprecated
+ # casts, instead issue warnings for unnamed arguments where
+ # appropriate.
+ #
+ # These are things that we want warnings for, since the style guide
+ # explicitly require all parameters to be named:
+ # Function(int);
+ # Function(int) {
+ # ConstMember(int) const;
+ # ConstMember(int) const {
+ # ExceptionMember(int) throw (...);
+ # ExceptionMember(int) throw (...) {
+ # PureVirtual(int) = 0;
+ #
+ # These are functions of some sort, where the compiler would be fine
+ # if they had named parameters, but people often omit those
+ # identifiers to reduce clutter:
+ # (FunctionPointer)(int);
+ # (FunctionPointer)(int) = value;
+ # Function((function_pointer_arg)(int))
+ # <TemplateArgument(int)>;
+ # <(FunctionPointerTemplateArgument)(int)>;
+ remainder = line[match.end(0):]
+ if Match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|=|>|\{|\))',
+ remainder):
+ # Looks like an unnamed parameter.
+
+ # Don't warn on any kind of template arguments.
+ if Match(r'^\s*>', remainder):
+ return False
+
+ # Don't warn on assignments to function pointers, but keep warnings for
+ # unnamed parameters to pure virtual functions. Note that this pattern
+ # will also pass on assignments of "0" to function pointers, but the
+ # preferred values for those would be "nullptr" or "NULL".
+ matched_zero = Match(r'^\s=\s*(\S+)\s*;', remainder)
+ if matched_zero and matched_zero.group(1) != '0':
+ return False
+
+ # Don't warn on function pointer declarations. For this we need
+ # to check what came before the "(type)" string.
+ if Match(r'.*\)\s*$', line[0:match.start(0)]):
+ return False
+
+ # Don't warn if the parameter is named with block comments, e.g.:
+ # Function(int /*unused_param*/);
+ if '/*' in raw_line:
+ return False
+
+ # Passed all filters, issue warning here.
+ error(filename, linenum, 'readability/function', 3,
+ 'All parameters should be named in a function')
+ return True
+
+ # At this point, all that should be left is actual casts.
+ error(filename, linenum, 'readability/casting', 4,
+ 'Using C-style cast. Use %s<%s>(...) instead' %
+ (cast_type, match.group(1)))
+
+ return True
+
+
+def ExpectingFunctionArgs(clean_lines, linenum):
+ """Checks whether where function type arguments are expected.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+
+ Returns:
+ True if the line at 'linenum' is inside something that expects arguments
+ of function types.
+ """
+ line = clean_lines.elided[linenum]
+ return (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or
+ (linenum >= 2 and
+ (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$',
+ clean_lines.elided[linenum - 1]) or
+ Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$',
+ clean_lines.elided[linenum - 2]) or
+ Search(r'\bstd::m?function\s*\<\s*$',
+ clean_lines.elided[linenum - 1]))))
+
+
+_HEADERS_CONTAINING_TEMPLATES = (
+ ('<deque>', ('deque',)),
+ ('<functional>', ('unary_function', 'binary_function',
+ 'plus', 'minus', 'multiplies', 'divides', 'modulus',
+ 'negate',
+ 'equal_to', 'not_equal_to', 'greater', 'less',
+ 'greater_equal', 'less_equal',
+ 'logical_and', 'logical_or', 'logical_not',
+ 'unary_negate', 'not1', 'binary_negate', 'not2',
+ 'bind1st', 'bind2nd',
+ 'pointer_to_unary_function',
+ 'pointer_to_binary_function',
+ 'ptr_fun',
+ 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t',
+ 'mem_fun_ref_t',
+ 'const_mem_fun_t', 'const_mem_fun1_t',
+ 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t',
+ 'mem_fun_ref',
+ )),
+ ('<limits>', ('numeric_limits',)),
+ ('<list>', ('list',)),
+ ('<map>', ('map', 'multimap',)),
+ ('<memory>', ('allocator',)),
+ ('<queue>', ('queue', 'priority_queue',)),
+ ('<set>', ('set', 'multiset',)),
+ ('<stack>', ('stack',)),
+ ('<string>', ('char_traits', 'basic_string',)),
+ ('<utility>', ('pair',)),
+ ('<vector>', ('vector',)),
+
+ # gcc extensions.
+ # Note: std::hash is their hash, ::hash is our hash
+ ('<hash_map>', ('hash_map', 'hash_multimap',)),
+ ('<hash_set>', ('hash_set', 'hash_multiset',)),
+ ('<slist>', ('slist',)),
+ )
+
+_RE_PATTERN_STRING = re.compile(r'\bstring\b')
+
+_re_pattern_algorithm_header = []
+for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap',
+ 'transform'):
+ # Match max<type>(..., ...), max(..., ...), but not foo->max, foo.max or
+ # type::max().
+ _re_pattern_algorithm_header.append(
+ (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'),
+ _template,
+ '<algorithm>'))
+
+_re_pattern_templates = []
+for _header, _templates in _HEADERS_CONTAINING_TEMPLATES:
+ for _template in _templates:
+ _re_pattern_templates.append(
+ (re.compile(r'(\<|\b)' + _template + r'\s*\<'),
+ _template + '<>',
+ _header))
+
+
+def FilesBelongToSameModule(filename_cc, filename_h):
+ """Check if these two filenames belong to the same module.
+
+ The concept of a 'module' here is a as follows:
+ foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the
+ same 'module' if they are in the same directory.
+ some/path/public/xyzzy and some/path/internal/xyzzy are also considered
+ to belong to the same module here.
+
+ If the filename_cc contains a longer path than the filename_h, for example,
+ '/absolute/path/to/base/sysinfo.cc', and this file would include
+ 'base/sysinfo.h', this function also produces the prefix needed to open the
+ header. This is used by the caller of this function to more robustly open the
+ header file. We don't have access to the real include paths in this context,
+ so we need this guesswork here.
+
+ Known bugs: tools/base/bar.cc and base/bar.h belong to the same module
+ according to this implementation. Because of this, this function gives
+ some false positives. This should be sufficiently rare in practice.
+
+ Args:
+ filename_cc: is the path for the .cc file
+ filename_h: is the path for the header path
+
+ Returns:
+ Tuple with a bool and a string:
+ bool: True if filename_cc and filename_h belong to the same module.
+ string: the additional prefix needed to open the header file.
+ """
+
+ if not filename_cc.endswith('.cc'):
+ return (False, '')
+ filename_cc = filename_cc[:-len('.cc')]
+ if filename_cc.endswith('_unittest'):
+ filename_cc = filename_cc[:-len('_unittest')]
+ elif filename_cc.endswith('_test'):
+ filename_cc = filename_cc[:-len('_test')]
+ filename_cc = filename_cc.replace('/public/', '/')
+ filename_cc = filename_cc.replace('/internal/', '/')
+
+ if not filename_h.endswith('.h'):
+ return (False, '')
+ filename_h = filename_h[:-len('.h')]
+ if filename_h.endswith('-inl'):
+ filename_h = filename_h[:-len('-inl')]
+ filename_h = filename_h.replace('/public/', '/')
+ filename_h = filename_h.replace('/internal/', '/')
+
+ files_belong_to_same_module = filename_cc.endswith(filename_h)
+ common_path = ''
+ if files_belong_to_same_module:
+ common_path = filename_cc[:-len(filename_h)]
+ return files_belong_to_same_module, common_path
+
+
+def UpdateIncludeState(filename, include_state, io=codecs):
+ """Fill up the include_state with new includes found from the file.
+
+ Args:
+ filename: the name of the header to read.
+ include_state: an _IncludeState instance in which the headers are inserted.
+ io: The io factory to use to read the file. Provided for testability.
+
+ Returns:
+ True if a header was succesfully added. False otherwise.
+ """
+ headerfile = None
+ try:
+ headerfile = io.open(filename, 'r', 'utf8', 'replace')
+ except IOError:
+ return False
+ linenum = 0
+ for line in headerfile:
+ linenum += 1
+ clean_line = CleanseComments(line)
+ match = _RE_PATTERN_INCLUDE.search(clean_line)
+ if match:
+ include = match.group(2)
+ # The value formatting is cute, but not really used right now.
+ # What matters here is that the key is in include_state.
+ include_state.setdefault(include, '%s:%d' % (filename, linenum))
+ return True
+
+
+def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
+ io=codecs):
+ """Reports for missing stl includes.
+
+ This function will output warnings to make sure you are including the headers
+ necessary for the stl containers and functions that you use. We only give one
+ reason to include a header. For example, if you use both equal_to<> and
+ less<> in a .h file, only one (the latter in the file) of these will be
+ reported as a reason to include the <functional>.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ include_state: An _IncludeState instance.
+ error: The function to call with any errors found.
+ io: The IO factory to use to read the header file. Provided for unittest
+ injection.
+ """
+ required = {} # A map of header name to linenumber and the template entity.
+ # Example of required: { '<functional>': (1219, 'less<>') }
+
+ for linenum in xrange(clean_lines.NumLines()):
+ line = clean_lines.elided[linenum]
+ if not line or line[0] == '#':
+ continue
+
+ # String is special -- it is a non-templatized type in STL.
+ matched = _RE_PATTERN_STRING.search(line)
+ if matched:
+ # Don't warn about strings in non-STL namespaces:
+ # (We check only the first match per line; good enough.)
+ prefix = line[:matched.start()]
+ if prefix.endswith('std::') or not prefix.endswith('::'):
+ required['<string>'] = (linenum, 'string')
+
+ for pattern, template, header in _re_pattern_algorithm_header:
+ if pattern.search(line):
+ required[header] = (linenum, template)
+
+ # The following function is just a speed up, no semantics are changed.
+ if not '<' in line: # Reduces the cpu time usage by skipping lines.
+ continue
+
+ for pattern, template, header in _re_pattern_templates:
+ if pattern.search(line):
+ required[header] = (linenum, template)
+
+ # The policy is that if you #include something in foo.h you don't need to
+ # include it again in foo.cc. Here, we will look at possible includes.
+ # Let's copy the include_state so it is only messed up within this function.
+ include_state = include_state.copy()
+
+ # Did we find the header for this file (if any) and succesfully load it?
+ header_found = False
+
+ # Use the absolute path so that matching works properly.
+ abs_filename = FileInfo(filename).FullName()
+
+ # For Emacs's flymake.
+ # If cpplint is invoked from Emacs's flymake, a temporary file is generated
+ # by flymake and that file name might end with '_flymake.cc'. In that case,
+ # restore original file name here so that the corresponding header file can be
+ # found.
+ # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h'
+ # instead of 'foo_flymake.h'
+ abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename)
+
+ # include_state is modified during iteration, so we iterate over a copy of
+ # the keys.
+ header_keys = include_state.keys()
+ for header in header_keys:
+ (same_module, common_path) = FilesBelongToSameModule(abs_filename, header)
+ fullpath = common_path + header
+ if same_module and UpdateIncludeState(fullpath, include_state, io):
+ header_found = True
+
+ # If we can't find the header file for a .cc, assume it's because we don't
+ # know where to look. In that case we'll give up as we're not sure they
+ # didn't include it in the .h file.
+ # TODO(unknown): Do a better job of finding .h files so we are confident that
+ # not having the .h file means there isn't one.
+ if filename.endswith('.cc') and not header_found:
+ return
+
+ # All the lines have been processed, report the errors found.
+ for required_header_unstripped in required:
+ template = required[required_header_unstripped][1]
+ if required_header_unstripped.strip('<>"') not in include_state:
+ error(filename, required[required_header_unstripped][0],
+ 'build/include_what_you_use', 4,
+ 'Add #include ' + required_header_unstripped + ' for ' + template)
+
+
+_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<')
+
+
+def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error):
+ """Check that make_pair's template arguments are deduced.
+
+ G++ 4.6 in C++11 mode fails badly if make_pair's template arguments are
+ specified explicitly, and such use isn't intended in any case.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+ match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line)
+ if match:
+ error(filename, linenum, 'build/explicit_make_pair',
+ 4, # 4 = high confidence
+ 'For C++11-compatibility, omit template arguments from make_pair'
+ ' OR use pair directly OR if appropriate, construct a pair directly')
+def CheckDefaultLambdaCaptures(filename, clean_lines, linenum, error):
+ """Check that default lambda captures are not used.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # A lambda introducer specifies a default capture if it starts with "[="
+ # or if it starts with "[&" _not_ followed by an identifier.
+ match = Match(r'^(.*)\[\s*(?:=|&[^\w])', line)
+ if match:
+ # Found a potential error, check what comes after the lambda-introducer.
+ # If it's not open parenthesis (for lambda-declarator) or open brace
+ # (for compound-statement), it's not a lambda.
+ line, _, pos = CloseExpression(clean_lines, linenum, len(match.group(1)))
+ if pos >= 0 and Match(r'^\s*[{(]', line[pos:]):
+ error(filename, linenum, 'build/c++11',
+ 4, # 4 = high confidence
+ 'Default lambda captures are an unapproved C++ feature.')
+
+
+
+
+def ProcessLine(filename, file_extension, clean_lines, line,
+ include_state, function_state, nesting_state, error,
+ extra_check_functions=[]):
+ """Processes a single line in the file.
+
+ Args:
+ filename: Filename of the file that is being processed.
+ file_extension: The extension (dot not included) of the file.
+ clean_lines: An array of strings, each representing a line of the file,
+ with comments stripped.
+ line: Number of line being processed.
+ include_state: An _IncludeState instance in which the headers are inserted.
+ function_state: A _FunctionState instance which counts function lines, etc.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: A callable to which errors are reported, which takes 4 arguments:
+ filename, line number, error level, and message
+ extra_check_functions: An array of additional check functions that will be
+ run on each source line. Each function takes 4
+ arguments: filename, clean_lines, line, error
+ """
+ raw_lines = clean_lines.raw_lines
+ ParseNolintSuppressions(filename, raw_lines[line], line, error)
+ nesting_state.Update(filename, clean_lines, line, error)
+ if nesting_state.InAsmBlock(): return
+ CheckForFunctionLengths(filename, clean_lines, line, function_state, error)
+ CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error)
+ CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error)
+ CheckLanguage(filename, clean_lines, line, file_extension, include_state,
+ nesting_state, error)
+ CheckForNonConstReference(filename, clean_lines, line, nesting_state, error)
+ CheckForNonStandardConstructs(filename, clean_lines, line,
+ nesting_state, error)
+ CheckVlogArguments(filename, clean_lines, line, error)
+ CheckPosixThreading(filename, clean_lines, line, error)
+ CheckInvalidIncrement(filename, clean_lines, line, error)
+ CheckMakePairUsesDeduction(filename, clean_lines, line, error)
+ # CheckDefaultLambdaCaptures(filename, clean_lines, line, error)
+ for check_fn in extra_check_functions:
+ check_fn(filename, clean_lines, line, error)
+
+def FlagCxx11Features(filename, clean_lines, linenum, error):
+ """Flag those c++11 features that we only allow in certain places.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Flag unapproved C++11 headers.
+ include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line)
+ # if include and include.group(1) in ('cfenv',
+ # 'condition_variable',
+ # 'fenv.h',
+ # 'ratio',
+ # 'regex',
+ # 'system_error',
+ # ):
+ # error(filename, linenum, 'build/c++11', 5,
+ # ('<%s> is an unapproved C++11 header.') % include.group(1))
+
+ # The only place where we need to worry about C++11 keywords and library
+ # features in preprocessor directives is in macro definitions.
+ if Match(r'\s*#', line) and not Match(r'\s*#\s*define\b', line): return
+
+ # These are classes and free functions. The classes are always
+ # mentioned as std::*, but we only catch the free functions if
+ # they're not found by ADL. They're alphabetical by header.
+ for top_name in (
+ # type_traits
+ 'alignment_of',
+ 'aligned_union',
+
+ # utility
+ 'forward',
+ ):
+ if Search(r'\bstd::%s\b' % top_name, line):
+ error(filename, linenum, 'build/c++11', 5,
+ ('std::%s is an unapproved C++11 class or function. Send c-style '
+ 'an example of where it would make your code more readable, and '
+ 'they may let you use it.') % top_name)
+
+
+def ProcessFileData(filename, file_extension, lines, error,
+ extra_check_functions=[]):
+ """Performs lint checks and reports any errors to the given error function.
+
+ Args:
+ filename: Filename of the file that is being processed.
+ file_extension: The extension (dot not included) of the file.
+ lines: An array of strings, each representing a line of the file, with the
+ last element being empty if the file is terminated with a newline.
+ error: A callable to which errors are reported, which takes 4 arguments:
+ filename, line number, error level, and message
+ extra_check_functions: An array of additional check functions that will be
+ run on each source line. Each function takes 4
+ arguments: filename, clean_lines, line, error
+ """
+ lines = (['// marker so line numbers and indices both start at 1'] + lines +
+ ['// marker so line numbers end in a known way'])
+
+ include_state = _IncludeState()
+ function_state = _FunctionState()
+ nesting_state = NestingState()
+
+ ResetNolintSuppressions()
+
+ CheckForCopyright(filename, lines, error)
+
+ if file_extension == 'h':
+ CheckForHeaderGuard(filename, lines, error)
+
+ RemoveMultiLineComments(filename, lines, error)
+ clean_lines = CleansedLines(lines)
+ for line in xrange(clean_lines.NumLines()):
+ ProcessLine(filename, file_extension, clean_lines, line,
+ include_state, function_state, nesting_state, error,
+ extra_check_functions)
+ FlagCxx11Features(filename, clean_lines, line, error)
+ nesting_state.CheckCompletedBlocks(filename, error)
+
+ CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error)
+
+ # We check here rather than inside ProcessLine so that we see raw
+ # lines rather than "cleaned" lines.
+ CheckForBadCharacters(filename, lines, error)
+
+ CheckForNewlineAtEOF(filename, lines, error)
+
+
+def ProcessFile(filename, vlevel, extra_check_functions=[]):
+ """Does google-lint on a single file.
+
+ Args:
+ filename: The name of the file to parse.
+
+ vlevel: The level of errors to report. Every error of confidence
+ >= verbose_level will be reported. 0 is a good default.
+
+ extra_check_functions: An array of additional check functions that will be
+ run on each source line. Each function takes 4
+ arguments: filename, clean_lines, line, error
+ """
+
+ _SetVerboseLevel(vlevel)
+
+ lf_lines = []
+ crlf_lines = []
+ try:
+ # Support the UNIX convention of using "-" for stdin. Note that
+ # we are not opening the file with universal newline support
+ # (which codecs doesn't support anyway), so the resulting lines do
+ # contain trailing '\r' characters if we are reading a file that
+ # has CRLF endings.
+ # If after the split a trailing '\r' is present, it is removed
+ # below.
+ if filename == '-':
+ lines = codecs.StreamReaderWriter(sys.stdin,
+ codecs.getreader('utf8'),
+ codecs.getwriter('utf8'),
+ 'replace').read().split('\n')
+ else:
+ lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
+
+ # Remove trailing '\r'.
+ # The -1 accounts for the extra trailing blank line we get from split()
+ for linenum in range(len(lines) - 1):
+ if lines[linenum].endswith('\r'):
+ lines[linenum] = lines[linenum].rstrip('\r')
+ crlf_lines.append(linenum + 1)
+ else:
+ lf_lines.append(linenum + 1)
+
+ except IOError:
+ sys.stderr.write(
+ "Skipping input '%s': Can't open for reading\n" % filename)
+ return
+
+ # Note, if no dot is found, this will give the entire filename as the ext.
+ file_extension = filename[filename.rfind('.') + 1:]
+
+ # When reading from stdin, the extension is unknown, so no cpplint tests
+ # should rely on the extension.
+ if filename != '-' and file_extension not in _valid_extensions:
+ sys.stderr.write('Ignoring %s; not a valid file name '
+ '(%s)\n' % (filename, ', '.join(_valid_extensions)))
+ else:
+ ProcessFileData(filename, file_extension, lines, Error,
+ extra_check_functions)
+
+ # If end-of-line sequences are a mix of LF and CR-LF, issue
+ # warnings on the lines with CR.
+ #
+ # Don't issue any warnings if all lines are uniformly LF or CR-LF,
+ # since critique can handle these just fine, and the style guide
+ # doesn't dictate a particular end of line sequence.
+ #
+ # We can't depend on os.linesep to determine what the desired
+ # end-of-line sequence should be, since that will return the
+ # server-side end-of-line sequence.
+ if lf_lines and crlf_lines:
+ # Warn on every line with CR. An alternative approach might be to
+ # check whether the file is mostly CRLF or just LF, and warn on the
+ # minority, we bias toward LF here since most tools prefer LF.
+ for linenum in crlf_lines:
+ Error(filename, linenum, 'whitespace/newline', 1,
+ 'Unexpected \\r (^M) found; better to use only \\n')
+
+ # sys.stderr.write('Done processing %s\n' % filename)
+
+
+def PrintUsage(message):
+ """Prints a brief usage string and exits, optionally with an error message.
+
+ Args:
+ message: The optional error message.
+ """
+ sys.stderr.write(_USAGE)
+ if message:
+ sys.exit('\nFATAL ERROR: ' + message)
+ else:
+ sys.exit(1)
+
+
+def PrintCategories():
+ """Prints a list of all the error-categories used by error messages.
+
+ These are the categories used to filter messages via --filter.
+ """
+ sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES))
+ sys.exit(0)
+
+
+def ParseArguments(args):
+ """Parses the command line arguments.
+
+ This may set the output format and verbosity level as side-effects.
+
+ Args:
+ args: The command line arguments:
+
+ Returns:
+ The list of filenames to lint.
+ """
+ try:
+ (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=',
+ 'counting=',
+ 'filter=',
+ 'root=',
+ 'linelength=',
+ 'extensions='])
+ except getopt.GetoptError:
+ PrintUsage('Invalid arguments.')
+
+ verbosity = _VerboseLevel()
+ output_format = _OutputFormat()
+ filters = ''
+ counting_style = ''
+
+ for (opt, val) in opts:
+ if opt == '--help':
+ PrintUsage(None)
+ elif opt == '--output':
+ if val not in ('emacs', 'vs7', 'eclipse'):
+ PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.')
+ output_format = val
+ elif opt == '--verbose':
+ verbosity = int(val)
+ elif opt == '--filter':
+ filters = val
+ if not filters:
+ PrintCategories()
+ elif opt == '--counting':
+ if val not in ('total', 'toplevel', 'detailed'):
+ PrintUsage('Valid counting options are total, toplevel, and detailed')
+ counting_style = val
+ elif opt == '--root':
+ global _root
+ _root = val
+ elif opt == '--linelength':
+ global _line_length
+ try:
+ _line_length = int(val)
+ except ValueError:
+ PrintUsage('Line length must be digits.')
+ elif opt == '--extensions':
+ global _valid_extensions
+ try:
+ _valid_extensions = set(val.split(','))
+ except ValueError:
+ PrintUsage('Extensions must be comma seperated list.')
+
+ if not filenames:
+ PrintUsage('No files were specified.')
+
+ _SetOutputFormat(output_format)
+ _SetVerboseLevel(verbosity)
+ _SetFilters(filters)
+ _SetCountingStyle(counting_style)
+
+ return filenames
+
+
+def main():
+ filenames = ParseArguments(sys.argv[1:])
+
+ # Change stderr to write with replacement characters so we don't die
+ # if we try to print something containing non-ASCII characters.
+ sys.stderr = codecs.StreamReaderWriter(sys.stderr,
+ codecs.getreader('utf8'),
+ codecs.getwriter('utf8'),
+ 'replace')
+
+ _cpplint_state.ResetErrorCounts()
+ for filename in filenames:
+ ProcessFile(filename, _cpplint_state.verbose_level)
+ _cpplint_state.PrintErrorCounts()
+
+ sys.exit(_cpplint_state.error_count > 0)
+
+
+if __name__ == '__main__':
+ main()
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/ignition-math4.git
More information about the debian-science-commits
mailing list