Squashed 'third_party/allwpilib_2019/' changes from 99e4f7dd2..c36bbcc9a

936627bd9 wpilibc: Remove direct CameraServer dependency (#1989)
8e333c0aa Use FPGA Time instead of wall clock time for odometry (#1996)
d4430b765 Gearsbot example: Use standard argument order (#1995)
75438ab2c Add RamseteCommand (#1951)
989df1b46 Bump Native Utils and OpenCV dependencies (#1993)
dbc33b61e Fix Timer usage in TrapezoidProfileCommand (#1992)
79f8c5644 Add TrapezoidProfileCommand (#1962)
9440edf2b Refactor TrajectoryGenerator (#1972)
73a30182c Add frc2::Timer (#1968)
36ea865ed Add toString for geometry and trajectory classes (#1991)
cbe05e7e8 Update ProfiledPIDController API (#1967)
d04eb3546 Deprecate old PID classes (#1964)
02264db69 Add JNI dependencies to myRobotCpp (#1980)
2a76c996e Use VID/PID detection for PS3Eye (#1977)
a3820bbdf Remove HAL_BaseInitialize (#1981)
a83fb4793 Update to 2020v5 image (#1983)
4b0ed910e Make SwerveDriveKinematics.toChassisSpeeds() public (#1976)
103c1b121 Remove DS caching from the HAL level (#1971)
6635ea75e Fix NPE in SendableRegistry.foreachLiveWindow() (#1974)
cfe23c5cd Fix grammar error in comment for configureButtonBindings (#1969)
4bde2654e Fix mac azure build (#1973)
4f034e6c1 generateTrajectory: default reversed param to false (#1953)
acf960f72 Sim GUI: Add option to disable outputs on DS disable
2d3dac99f Sim GUI: Handle low resolutions and scale default window positions
07c86e0cd Sim GUI: Support High DPI monitors
46ad95512 SimDeviceData: Add missing null check
5bce489b9 Add ProggyDotted font to imgui (both cmake and gradle)
55af553ac Simulation GUI: Map gamepad the same way as DS
c59f9cea5 CameraServer: Add VID/PID support for Linux USB devices (#1960)
3fc89c84d Make splinePointsFromSplines public (#1963)
2c5093797 Fix implicitly deleted move constructors (#1954)
f3ad927f4 Update Java SmartDashboard and LiveWindow to match C++
05c25deb7 Fix move handling of C++ Sendable in SmartDashboard and LiveWindow
d726591ce Fix Gazebo sim plugin build (#1959)
2ff694fa4 Unbreak gradle build when other compilers installed (#1958)
53816155b Improve command decorator names (#1945)
a38f183a9 Fix GenResources.cmake so it's usable in a submodule (#1956)
b3398dca3 Set gradlebase correctly for all examples (#1950)
2c311013d Add Aarch64Bionic platform detection (#1922)
c10f2003c Add generateTrajectory overload (#1944)
63cfa64fb Add getters for pose in odometry classes (#1943)
2402c2bad Fix C++ command group recursive constructor bug (#1941)
f4eedf597 Fix ConcurrentModificationException in CommandScheduler (#1938)
bb0b207d2 Fix array out of bounds exception caused by parallel race group (#1935)
7bd69e591 Fix typo in temperature (#1940)
ec9738245 Bump to 2020 v4 image (#1931)
46303a822 Add messaging to extension loading in the HAL (#1926)
d169d6be9 Set extract_static for Doxygen config so that static members show up (#1930)
4e183eb10 Bump to 2020 v3 image (#1929)
84c185803 LiveWindow: catch errors in callback/builder functions (#1921)
0e3b0f3da Remove deprecated free() calls (#1925)
7f839b87c Remove timeouts from azure builds (#1924)
45b766a5d Fix main thread ID being potentially incorrect for simulation (#1923)
56d782b16 Add secondary camera name for PS3Eye (#1920)
2b4894038 Add simulation GUI plugin
f97d16073 Add imgui build to cmake
55a844a3e HAL sim: Add encoder channel B access
10deba854 Remove sendables from SendableRegistry when close() is called (#1917)
a9f0e4668 Implement sim devices for ADXL345, ADXL362, ADXRS450, Ultrasonic
aa9064586 Add ability to associate other devices with a SimDevice
81c2c8a7d Add simulation generic device/value support
e8d6f8a2c Move mockdata/HAL_Value.h to hal/Value.h
1b266717a Add simulation module support to cmake build (#1906)
fb8f3bd06 Add testbench yaml file (#1915)
846d8def0 Update to 2020 v2 image (#1913)
d6ac6e512 Fix PortForwarder package declaration (#1912)
227157086 Fix PS3Eye exposure setting (#1911)
885744d7e Add myRobot C++ version to cmake build (#1907)
366091fa8 Document that ConditionalCommand requires all subsystems (#1909)
c58b072c8 Fix Drive usage reporting order (#1908)
762c88adb Update compiler versions in readme (#1905)
af8ce568d Add Ramsete unicycle controller (#1790)
b2c2934d0 Fix javadoc warnings about invalid usage of ">" (#1904)
cce26ec78 Replace CRLF line endings with LF (#1902)
cb54602d4 Add support for writing RTR CAN Frames to the CAN API (#1900)
9f740e590 Use OS for serial port instead of NI VISA (#1875)
b23baf611 Add ability to run robot main loop in a separate thread (#1895)
457f94ba2 Add trajectory generation using hermite splines (#1843)
fd612052f Update native utils to use new frchome directory (#1884)
8858ec55c Remove periodic can read call (#1868)
41efb8015 Update CANAPITypes.h (#1860)
c93be1b2d Remove LabVIEW HAL support (#1901)
680f8919e Remove eigen, units and libuv from doxygen generation (#1898)
c5812524f Bump GradleJNI plugin version (#1899)
971303da8 Add PortForwarder class (#1890)
50db77bf2 Fix wpiutil cmake eigen install source directory (#1891)
85d42c199 C++ PIDCommand: Add GetMeasurement() and UseOutput() (#1892)
2dfbb855d wpilibj: Fix SwerveDriveKinematics ctor parameter name (#1889)
471f375a3 Simplify Sendable interface (#1864)
1d8c4d016 Replace ::value and ::type with _v and _t suffixes (#1885)
a5650b943 Add Units Utility class for Java (#1829)
904479ad4 Deprecate GearTooth class for removal (#1878)
86b666bba Add equality comparator to geometry classes (#1882)
62f07c182 Make one-arg Rotation2d constructor implicit (#1883)
f405582f8 Add kinematics suite (#1787)
561cbbd14 Deprecate Filter class for removal (#1876)
84e2973aa Remove unused include from Filesystem.h (#1877)
f49859ebf Remove NI VISA headers, as they are now included in NI Libraries (#1879)
bc59db5e6 Rename DEBUG macro to DEBUG0 (#1871)
dd928b4cb Remove JNI logging (#1872)
3e0f7d099 Use units for new NotifierCommand (#1869)
5ffe15d5f Remove ability to build all combined artifacts (#1867)
516cbef2c  Remove RoboRIO ifdef from simulation headers (#1859)
9b6ffc201 Replace SetOutputRange() with SetIntegratorRange()
ff8b8f0a8 Remove percent tolerance from PID controller
0ca8d667d Clean up AutoCloseable and other Java warnings (#1866)
7112add67 Watchdog: use units::second_t instead of double (#1863)
761bc3ef8 Change C++ WaitCommand to use units (#1865)
1fb301123 Add MathUtils.clamp() for Java (#1861)
eb3e0c9c9 Fix cmake Eigen include directory (#1862)
2250b7fbe Rename GearsBotNew example to GearsBot
c9f9feff1 Replace deprecated API usage in C++ examples
d6b9c7e14 CONTRIBUTING.md: Point to frc-docs instead of screensteps (#1858)
d10a1a797 Fix eigen build in vcpkg (#1856)
2bdb44325 Add frc2 includes to list of "other lib" regexes (#1855)
4b2b21d24 Replace outdated Java collections (#508)
8993ce5bf Move Eigen headers out of main include folder (#1854)
0f532a117 Add PWMSparkMax (#1751)
f7ad363d8 Add jni cross compile options for aarch64 (#1853)
9afea3340 Add support for aarch64 jetson bionic builds (#1844)
d787b5d60 Add more items to .gitignore (#1850)
5dd0d1b7d Use units in SPI
07ac711b3 Fix units deprecated warning in IterativeRobot
decfd858b Correctly report -1 for POV on disconnected joystick (#1852)
076ed7770 Add new C++ Command framework (#1785)
a0be07c37 Refactor HAL handle move construction/assignment (#1845)
558c38308 Add new Java Command framework (#1682)
1379735af Delete RobotState and SensorUtil constructors (#1847)
e3d86fee4 Move circular buffer class from wpilib to wpiutil (#1840)
4cd8a5667 TimedRobot.cpp: Fix deprecation warning (#1846)
b2861f894 Use 2020 artifacts and artifactory server (#1838)
98cc32703 Update to use artifactory to publish artifacts (#1833)
fa0640300 Move drive integration tests into wpilibj/src/test (#1836)
e716c36b8 Fix Nat.java generation to be incremental (#1831)
9fd2b5e3f Fix MSVC builds on cmake windows in vcpkg (#1835)
7e95010a2 Add compile-time EJML matrix wrapper to wpiutil (#1804)
3ebc5a6d3 Add ProfiledPIDController
fc98a79db Clean up PIDController interface in preparation for ProfiledPIDController
fdc098267 Fix compilation error in elevator trapezoid profile example (#1826)
a3dd84e85 Make XBoxController Button enum public (#1823)
a216b9e9e Add TrapezoidProfile example (#1814)
8f386f6bb wpilibc: Add unit-safety to C++ geometry classes (#1811)
c07ac2353 wpilibc: Add overloads for units (#1815)
f1d71da8a Move GetStackTrace and Demangle to wpiutil, add Windows support (#1819)
ef037457e Make LinearFilter copyable and moveable (#1789)
76930250c Remove objective-cpp support (#1816)
1c246418f Move TrapezoidProfileTest to trajectory folder (#1812)
95a54a0f2 Add java arcade drive example (#1810)
a4530243e HAL sim: Fix incorrectly setting dio port to initialized on cleanup (#1813)
09d00a622 Update Java examples to use new PIDController (#1809)
ba9b51742 Add missing Java examples (#841)
6411bd79c InterruptableSensorBase: Fix callback function deletion (#1807)
810e58ea8 I2C: Add tip about writeBulk() to transaction() (#1806)
607d6c148 Fix wpilibj integration tests jar name (#1808)
c9873e81b Remove PIDControllerRunner and mutex from new PIDController (#1795)
98d0706de Fix cscore build with OpenCV 4 (#1803)
fbe67c90c Make Sendable setters synchronous (#1799)
c67a488a0 Format SendableBuilderImpl javadocs (#1802)
8e93ce892 Fix PIDControllerRunner member destruction order (#1801)
c98ca7310 Add EJML dependency to wpiutil (#1769)
3b12276bc SendableBase: remove unnecessary synchronization (#1797)
e6d348f38 Fix missing default name in Java PIDController (#1792)
df12fc2a8 Java cleanups (#1776)
39561751f Update GradleVSCode version (#1786)
37d316aa0 Add C++20 std::math constants shim (#1788)
dd4310959 Deprecate frc/WPILib.h (#1779)
823174f30 Update native utils to 2020.0.4 (#1783)
37c695266 Squelch -Wdeprecated-copy for Eigen with GCC >= 9
04c9b000f Revert "Fix build of Eigen 3.3.7 with GCC 9"
ca3e71e21 wpiutil: Fix Process::Spawn() (#1778)
d946d5a2b Fix Eigen compilation errors and add tests (#1777)
8b1b9ac75 Fix build of Eigen 3.3.7 with GCC 9
2f680ba99 Add Eigen linear algebra library
a885db7d4 Make MotorEncoderTest use LinearFilter (#1775)
ee2410169 Add geometry classes (#1766)
48fe54271 Add HALSIM_SetSendError implementation (#1773)
dff58c87f Fix unused warning in release build (#1771)
dde61aad3 Remove TimerEventHandler typedef from Notifier class (#1767)
0f6ef80ab Add RobotState#IsEStopped and DriverStation#IsEStopped (#952)
e48886187 Move unit tests from integration test suite (#1170)
dffa1a5cb Make null checks more descriptive (#1688)
fe59d854d Notifier: add null check (#1684)
10731f3d6 Update uv Udp wrapper for latest features
89f7b72b6 Update libuv to 1.30.1 release
85f2f8740 wpiutil: Add unique_function (#1761)
73ec94078 Remove SampleRobot (#1658)
62be0392b Replace std::lock_guard and std::lock with std::scoped_lock (#1758)
24d31df55 Make sure move constructor is generated for TrapezoidProfile (#1757)
841ef5d73 Remove template types from lock RAII wrapper usages (#1756)
e582518ba Fix some move constructors (#1754)
8757bc471 Remove pre-C++17 shims (#1752)
ea9512977 Add replacement PIDController class (#1300)
9b798d228 Add TrapezoidProfile class (#1673)
804926fb5 Unconditionally skip athena builds for sim (#1748)
118e9d29d Add C++14 units library (#1749)
c705953d7 Add usage reporting to LinearFilter (#1750)
852d1b9ca Don't cross-build gazebo for raspbian (#1747)
eedb3a1ad Fix GCC 9 warnings (#1730)
60dce66a4 Remove wpi::ArrayRef std::initializer_list constructor (#1745)
9e19b29c3 Use base azure image for primary wpilib build (#1744)
299425071 Update jni library, fix cross builds of the jni symbol check (#1742)
a6b0e9b85 Only disable execution of cross compile google tests (#1741)
3c2093119 Use docker container to run wpiformat (#1740)
5fe2eebce Revert "Don't build halsim_gazebo on raspbian (#1737)" (#1743)
4b1b92bb7 Replace wpi::optional with C++17 std::optional (#1732)
0fbb0d989 Update to 2020 compilers (#1733)
2dc94e605 Disable google tests on cross compilers (#1738)
d9cb57a42 Don't build halsim_gazebo on raspbian (#1737)
f7cfdd7ce Replace crlf line endings with lf (#1731)
b6d5d90d9 Add JaCoCo Support (#1734)
c7ab2baa6 Add way to disable the jni check tasks from a property (#1736)
0c45c5b7e Fix skip athena and skip raspbian flags (#1735)
3dfb01d45 Update to new Native Utils (#1696)
30e936837 Clean up LinearDigitalFilter class (#782)
311e2de4c Remove deprecated Joystick constants (#1715)
c08fd6682 Update CAN manufacturer list (#1706)
258bba0c2 ErrorBase and WPIError improvements (#1727)
372ca4f45 cmake: Enable googletest unit tests (#1720)
223d47af0 HALSIM: support mocking of HAL_SendError() (#1728)
55cb683db Change compiler flags to C++17 (#1723)
ee8a33c56 wpiutil: SafeThread: Add thread id, support getting shared_ptr (#1722)
61426d08d wpiutil: Signal: make operator() const (#1721)
b630b63ef Remove functions in LiveWindow deprecated since 2018 (#1716)
1d0c05d4f Styleguide fixes for #1718 (#1719)
f07569df1 Fix newer GCC/clang compiler warnings (#1718)
0120f3124 C++ SPI: Fix SetClockRate to take int (#1717)
c2829ed98 Configure gradle to ignore unresolved headers (#1711)
221e66f46 Allow disabling static init of JNI libraries (#1672)
738852e11 cmake: Add cross toolchain files for Rio and Pi (#1710)
27b697b08 Remove frc directory include shims (#1714)
9e45373a7 Remove functions and classes deprecated for 2018 season (#1059)
eeb1025ac SPI: Report port as instance for usage reporting (#1704)
bc6f1e246 Windows compiler options improvements (#1709)
bb48ae391 cmake: Move example programs into folders (#1654)
221011494 Update for C++17 and fix MSVC warnings (#1694)
fb1239a2a Add raw sources and sinks to cscore (#1670)
7de947734 Add lambda overloads for interrupts (#1636)
90957aeea Move libuv to its own subfolder in build (#1661)
47aae502a Styleguide fixes (#1702)
0bff98b5e Correct DifferentialDrive::ArcadeDrive param docs (#1698)
b52e40b80 Allow widgets to be added by passing value suppliers (#1690)
4a00cd77b Add usage reporting for the Shuffleboard API (#1685)
e25e515f2  Publish artifacts on azure (#1678)
322ef9b96 Force Java 11, fix javadoc generation (#1695)
d42ef5df0 Fix Watchdog print formatting (#1693)
f432f65be Update copyright year in license to 2019 (#1524)
1726b77ac wpiutil: uv: Remove copy from SimpleBufferPool (#1680)
620bec9ca wpiutil: uv: Add LoopClosing status to Handle (#1647)
7cd6e2e7f UsbCamera: Solve race in windows initialization (#1638)
7732836bd Completely disable watchdog tests on mac (#1679)
698edfda9 Remove framework load, disable mac timeout test (#1676)
1c454b000 Add Shuffleboard calls to IterativeRobotBase in C++ (#1607)
f42905b32 Include missing headers in HAL.h (#1660)
bdc822fad Only generate passthrough URLs for RoboRIO (#1624)
d3affb16b Make failure of HAL_GetFPGATime() more descriptive (#1633)
2de3bf7f5 Update LLVM from stable upstream (#1653)
3cf4f38f5 Fix build on macos10.14.4 (#1648)
4e0c10f48 Fix CAN Clean using wrong ID (#1668)
3b0631324 Fix Gray to BGR conversion in CameraServer (#1665)
6cd1c73ef Fix GUID comparison creating weird symbol (#1659)
063bbab6f MavenArtifacts.md: update links to HTTPS (#1674)
aab4c494d Fix type in build.gradle (#1604)
bf46af260 Disable extraneous data warnings in libjpeg (#1630)
655763a9a Limit length of message sent to DS SendError call (#1618)
a095ec2d8 Fix linker errors with free functions in Threads.h (#1625)
12ab035aa Fix receive side of LabVIEW USB streams (#1621)

Change-Id: Ibd382e1a48925c200850cf90a8121e35c0fcffe3
git-subtree-dir: third_party/allwpilib_2019
git-subtree-split: c36bbcc9a9095489fc078229db4fba3ecd0f9b78
diff --git a/wpiutil/src/test/native/cpp/Base64Test.cpp b/wpiutil/src/test/native/cpp/Base64Test.cpp
index 99aecb9..caa35aa 100644
--- a/wpiutil/src/test/native/cpp/Base64Test.cpp
+++ b/wpiutil/src/test/native/cpp/Base64Test.cpp
@@ -1,5 +1,5 @@
 /*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved.                        */
+/* Copyright (c) 2015-2019 FIRST. All Rights Reserved.                        */
 /* Open Source Software - may be modified and shared by FRC teams. The code   */
 /* must be accompanied by the FIRST BSD license file in the root directory of */
 /* the project.                                                               */
@@ -85,8 +85,7 @@
      "mQgc28gb24uLi4K"},
 };
 
-INSTANTIATE_TEST_CASE_P(Base64Sample, Base64Test,
-                        ::testing::ValuesIn(sample), );
+INSTANTIATE_TEST_SUITE_P(Base64Sample, Base64Test, ::testing::ValuesIn(sample));
 
 static Base64TestParam standard[] = {
     {0, "", ""},
@@ -99,7 +98,7 @@
     {2, "\xff\xef", "/+8="},
 };
 
-INSTANTIATE_TEST_CASE_P(Base64Standard, Base64Test,
-                        ::testing::ValuesIn(standard), );
+INSTANTIATE_TEST_SUITE_P(Base64Standard, Base64Test,
+                         ::testing::ValuesIn(standard));
 
 }  // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/CircularBufferTest.cpp b/wpiutil/src/test/native/cpp/CircularBufferTest.cpp
new file mode 100644
index 0000000..7fe9e03
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/CircularBufferTest.cpp
@@ -0,0 +1,209 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2015-2019 FIRST. All Rights Reserved.                        */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project.                                                               */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/circular_buffer.h"  // NOLINT(build/include_order)
+
+#include <array>
+
+#include "gtest/gtest.h"
+
+static const std::array<double, 10> values = {
+    {751.848, 766.366, 342.657, 234.252, 716.126, 132.344, 445.697, 22.727,
+     421.125, 799.913}};
+
+static const std::array<double, 8> pushFrontOut = {
+    {799.913, 421.125, 22.727, 445.697, 132.344, 716.126, 234.252, 342.657}};
+
+static const std::array<double, 8> pushBackOut = {
+    {342.657, 234.252, 716.126, 132.344, 445.697, 22.727, 421.125, 799.913}};
+
+TEST(CircularBufferTest, PushFrontTest) {
+  wpi::circular_buffer<double> queue(8);
+
+  for (auto& value : values) {
+    queue.push_front(value);
+  }
+
+  for (size_t i = 0; i < pushFrontOut.size(); i++) {
+    EXPECT_EQ(pushFrontOut[i], queue[i]);
+  }
+}
+
+TEST(CircularBufferTest, PushBackTest) {
+  wpi::circular_buffer<double> queue(8);
+
+  for (auto& value : values) {
+    queue.push_back(value);
+  }
+
+  for (size_t i = 0; i < pushBackOut.size(); i++) {
+    EXPECT_EQ(pushBackOut[i], queue[i]);
+  }
+}
+
+TEST(CircularBufferTest, PushPopTest) {
+  wpi::circular_buffer<double> queue(3);
+
+  // Insert three elements into the buffer
+  queue.push_back(1.0);
+  queue.push_back(2.0);
+  queue.push_back(3.0);
+
+  EXPECT_EQ(1.0, queue[0]);
+  EXPECT_EQ(2.0, queue[1]);
+  EXPECT_EQ(3.0, queue[2]);
+
+  /*
+   * The buffer is full now, so pushing subsequent elements will overwrite the
+   * front-most elements.
+   */
+
+  queue.push_back(4.0);  // Overwrite 1 with 4
+
+  // The buffer now contains 2, 3 and 4
+  EXPECT_EQ(2.0, queue[0]);
+  EXPECT_EQ(3.0, queue[1]);
+  EXPECT_EQ(4.0, queue[2]);
+
+  queue.push_back(5.0);  // Overwrite 2 with 5
+
+  // The buffer now contains 3, 4 and 5
+  EXPECT_EQ(3.0, queue[0]);
+  EXPECT_EQ(4.0, queue[1]);
+  EXPECT_EQ(5.0, queue[2]);
+
+  EXPECT_EQ(5.0, queue.pop_back());  // 5 is removed
+
+  // The buffer now contains 3 and 4
+  EXPECT_EQ(3.0, queue[0]);
+  EXPECT_EQ(4.0, queue[1]);
+
+  EXPECT_EQ(3.0, queue.pop_front());  // 3 is removed
+
+  // Leaving only one element with value == 4
+  EXPECT_EQ(4.0, queue[0]);
+}
+
+TEST(CircularBufferTest, ResetTest) {
+  wpi::circular_buffer<double> queue(5);
+
+  for (size_t i = 1; i < 6; i++) {
+    queue.push_back(i);
+  }
+
+  queue.reset();
+
+  for (size_t i = 0; i < 5; i++) {
+    EXPECT_EQ(0.0, queue[i]);
+  }
+}
+
+TEST(CircularBufferTest, ResizeTest) {
+  wpi::circular_buffer<double> queue(5);
+
+  /* Buffer contains {1, 2, 3, _, _}
+   *                  ^ front
+   */
+  queue.push_back(1.0);
+  queue.push_back(2.0);
+  queue.push_back(3.0);
+
+  queue.resize(2);
+  EXPECT_EQ(1.0, queue[0]);
+  EXPECT_EQ(2.0, queue[1]);
+
+  queue.resize(5);
+  EXPECT_EQ(1.0, queue[0]);
+  EXPECT_EQ(2.0, queue[1]);
+
+  queue.reset();
+
+  /* Buffer contains {_, 1, 2, 3, _}
+   *                     ^ front
+   */
+  queue.push_back(0.0);
+  queue.push_back(1.0);
+  queue.push_back(2.0);
+  queue.push_back(3.0);
+  queue.pop_front();
+
+  queue.resize(2);
+  EXPECT_EQ(1.0, queue[0]);
+  EXPECT_EQ(2.0, queue[1]);
+
+  queue.resize(5);
+  EXPECT_EQ(1.0, queue[0]);
+  EXPECT_EQ(2.0, queue[1]);
+
+  queue.reset();
+
+  /* Buffer contains {_, _, 1, 2, 3}
+   *                        ^ front
+   */
+  queue.push_back(0.0);
+  queue.push_back(0.0);
+  queue.push_back(1.0);
+  queue.push_back(2.0);
+  queue.push_back(3.0);
+  queue.pop_front();
+  queue.pop_front();
+
+  queue.resize(2);
+  EXPECT_EQ(1.0, queue[0]);
+  EXPECT_EQ(2.0, queue[1]);
+
+  queue.resize(5);
+  EXPECT_EQ(1.0, queue[0]);
+  EXPECT_EQ(2.0, queue[1]);
+
+  queue.reset();
+
+  /* Buffer contains {3, _, _, 1, 2}
+   *                           ^ front
+   */
+  queue.push_back(3.0);
+  queue.push_front(2.0);
+  queue.push_front(1.0);
+
+  queue.resize(2);
+  EXPECT_EQ(1.0, queue[0]);
+  EXPECT_EQ(2.0, queue[1]);
+
+  queue.resize(5);
+  EXPECT_EQ(1.0, queue[0]);
+  EXPECT_EQ(2.0, queue[1]);
+
+  queue.reset();
+
+  /* Buffer contains {2, 3, _, _, 1}
+   *                              ^ front
+   */
+  queue.push_back(2.0);
+  queue.push_back(3.0);
+  queue.push_front(1.0);
+
+  queue.resize(2);
+  EXPECT_EQ(1.0, queue[0]);
+  EXPECT_EQ(2.0, queue[1]);
+
+  queue.resize(5);
+  EXPECT_EQ(1.0, queue[0]);
+  EXPECT_EQ(2.0, queue[1]);
+
+  // Test push_back() after resize
+  queue.push_back(3.0);
+  EXPECT_EQ(1.0, queue[0]);
+  EXPECT_EQ(2.0, queue[1]);
+  EXPECT_EQ(3.0, queue[2]);
+
+  // Test push_front() after resize
+  queue.push_front(4.0);
+  EXPECT_EQ(4.0, queue[0]);
+  EXPECT_EQ(1.0, queue[1]);
+  EXPECT_EQ(2.0, queue[2]);
+  EXPECT_EQ(3.0, queue[3]);
+}
diff --git a/wpiutil/src/test/native/cpp/EigenTest.cpp b/wpiutil/src/test/native/cpp/EigenTest.cpp
new file mode 100644
index 0000000..04a0bc2
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/EigenTest.cpp
@@ -0,0 +1,61 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2019 FIRST. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project.                                                               */
+/*----------------------------------------------------------------------------*/
+
+#include <Eigen/Core>
+#include <Eigen/LU>
+
+#include "gtest/gtest.h"
+
+TEST(EigenTest, MultiplicationTest) {
+  Eigen::Matrix<double, 2, 2> m1;
+  m1 << 2, 1, 0, 1;
+
+  Eigen::Matrix<double, 2, 2> m2;
+  m2 << 3, 0, 0, 2.5;
+
+  const auto result = m1 * m2;
+
+  Eigen::Matrix<double, 2, 2> expectedResult;
+  expectedResult << 6.0, 2.5, 0.0, 2.5;
+
+  EXPECT_TRUE(expectedResult.isApprox(result));
+
+  Eigen::Matrix<double, 2, 3> m3;
+  m3 << 1.0, 3.0, 0.5, 2.0, 4.3, 1.2;
+
+  Eigen::Matrix<double, 3, 4> m4;
+  m4 << 3.0, 1.5, 2.0, 4.5, 2.3, 1.0, 1.6, 3.1, 5.2, 2.1, 2.0, 1.0;
+
+  const auto result2 = m3 * m4;
+
+  Eigen::Matrix<double, 2, 4> expectedResult2;
+  expectedResult2 << 12.5, 5.55, 7.8, 14.3, 22.13, 9.82, 13.28, 23.53;
+
+  EXPECT_TRUE(expectedResult2.isApprox(result2));
+}
+
+TEST(EigenTest, TransposeTest) {
+  Eigen::Matrix<double, 3, 1> vec;
+  vec << 1, 2, 3;
+
+  const auto transpose = vec.transpose();
+
+  Eigen::Matrix<double, 1, 3> expectedTranspose;
+  expectedTranspose << 1, 2, 3;
+
+  EXPECT_TRUE(expectedTranspose.isApprox(transpose));
+}
+
+TEST(EigenTest, InverseTest) {
+  Eigen::Matrix<double, 3, 3> mat;
+  mat << 1.0, 3.0, 2.0, 5.0, 2.0, 1.5, 0.0, 1.3, 2.5;
+
+  const auto inverse = mat.inverse();
+  const auto identity = Eigen::MatrixXd::Identity(3, 3);
+
+  EXPECT_TRUE(identity.isApprox(mat * inverse));
+}
diff --git a/wpiutil/src/test/native/cpp/UnitsTest.cpp b/wpiutil/src/test/native/cpp/UnitsTest.cpp
new file mode 100644
index 0000000..8e0823a
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/UnitsTest.cpp
@@ -0,0 +1,3379 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2019 FIRST. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project.                                                               */
+/*----------------------------------------------------------------------------*/
+
+#include <array>
+#include <chrono>
+#include <string>
+#include <type_traits>
+
+#include "gtest/gtest.h"
+#include "units/units.h"
+
+using namespace units;
+using namespace units::dimensionless;
+using namespace units::length;
+using namespace units::mass;
+using namespace units::angle;
+using namespace units::time;
+using namespace units::frequency;
+using namespace units::area;
+using namespace units::velocity;
+using namespace units::angular_velocity;
+using namespace units::temperature;
+using namespace units::luminous_intensity;
+using namespace units::solid_angle;
+using namespace units::frequency;
+using namespace units::acceleration;
+using namespace units::pressure;
+using namespace units::charge;
+using namespace units::energy;
+using namespace units::power;
+using namespace units::voltage;
+using namespace units::capacitance;
+using namespace units::impedance;
+using namespace units::conductance;
+using namespace units::magnetic_flux;
+using namespace units::magnetic_field_strength;
+using namespace units::inductance;
+using namespace units::luminous_flux;
+using namespace units::illuminance;
+using namespace units::radiation;
+using namespace units::torque;
+using namespace units::volume;
+using namespace units::density;
+using namespace units::concentration;
+using namespace units::data;
+using namespace units::data_transfer_rate;
+using namespace units::math;
+
+#if !defined(_MSC_VER) || _MSC_VER > 1800
+using namespace units::literals;
+#endif
+
+namespace {
+
+class TypeTraits : public ::testing::Test {
+ protected:
+  TypeTraits() {}
+  virtual ~TypeTraits() {}
+  virtual void SetUp() {}
+  virtual void TearDown() {}
+};
+
+class UnitManipulators : public ::testing::Test {
+ protected:
+  UnitManipulators() {}
+  virtual ~UnitManipulators() {}
+  virtual void SetUp() {}
+  virtual void TearDown() {}
+};
+
+class UnitContainer : public ::testing::Test {
+ protected:
+  UnitContainer() {}
+  virtual ~UnitContainer() {}
+  virtual void SetUp() {}
+  virtual void TearDown() {}
+};
+
+class UnitConversion : public ::testing::Test {
+ protected:
+  UnitConversion() {}
+  virtual ~UnitConversion() {}
+  virtual void SetUp() {}
+  virtual void TearDown() {}
+};
+
+class UnitMath : public ::testing::Test {
+ protected:
+  UnitMath() {}
+  virtual ~UnitMath() {}
+  virtual void SetUp() {}
+  virtual void TearDown() {}
+};
+
+class CompileTimeArithmetic : public ::testing::Test {
+ protected:
+  CompileTimeArithmetic() {}
+  virtual ~CompileTimeArithmetic() {}
+  virtual void SetUp() {}
+  virtual void TearDown() {}
+};
+
+class Constexpr : public ::testing::Test {
+ protected:
+  Constexpr() {}
+  virtual ~Constexpr() {}
+  virtual void SetUp() {}
+  virtual void TearDown() {}
+};
+
+class CaseStudies : public ::testing::Test {
+ protected:
+  CaseStudies() {}
+  virtual ~CaseStudies() {}
+  virtual void SetUp() {}
+  virtual void TearDown() {}
+
+  struct RightTriangle {
+    using a = unit_value_t<meters, 3>;
+    using b = unit_value_t<meters, 4>;
+    using c = unit_value_sqrt<
+        unit_value_add<unit_value_power<a, 2>, unit_value_power<b, 2>>>;
+  };
+};
+}  // namespace
+
+TEST_F(TypeTraits, isRatio) {
+  EXPECT_TRUE(traits::is_ratio<std::ratio<1>>::value);
+  EXPECT_FALSE(traits::is_ratio<double>::value);
+}
+
+TEST_F(TypeTraits, ratio_sqrt) {
+  using rt2 = ratio_sqrt<std::ratio<2>>;
+  EXPECT_LT(std::abs(std::sqrt(2 / static_cast<double>(1)) -
+                     rt2::num / static_cast<double>(rt2::den)),
+            5e-9);
+
+  using rt4 = ratio_sqrt<std::ratio<4>>;
+  EXPECT_LT(std::abs(std::sqrt(4 / static_cast<double>(1)) -
+                     rt4::num / static_cast<double>(rt4::den)),
+            5e-9);
+
+  using rt10 = ratio_sqrt<std::ratio<10>>;
+  EXPECT_LT(std::abs(std::sqrt(10 / static_cast<double>(1)) -
+                     rt10::num / static_cast<double>(rt10::den)),
+            5e-9);
+
+  using rt30 = ratio_sqrt<std::ratio<30>>;
+  EXPECT_LT(std::abs(std::sqrt(30 / static_cast<double>(1)) -
+                     rt30::num / static_cast<double>(rt30::den)),
+            5e-9);
+
+  using rt61 = ratio_sqrt<std::ratio<61>>;
+  EXPECT_LT(std::abs(std::sqrt(61 / static_cast<double>(1)) -
+                     rt61::num / static_cast<double>(rt61::den)),
+            5e-9);
+
+  using rt100 = ratio_sqrt<std::ratio<100>>;
+  EXPECT_LT(std::abs(std::sqrt(100 / static_cast<double>(1)) -
+                     rt100::num / static_cast<double>(rt100::den)),
+            5e-9);
+
+  using rt1000 = ratio_sqrt<std::ratio<1000>>;
+  EXPECT_LT(std::abs(std::sqrt(1000 / static_cast<double>(1)) -
+                     rt1000::num / static_cast<double>(rt1000::den)),
+            5e-9);
+
+  using rt10000 = ratio_sqrt<std::ratio<10000>>;
+  EXPECT_LT(std::abs(std::sqrt(10000 / static_cast<double>(1)) -
+                     rt10000::num / static_cast<double>(rt10000::den)),
+            5e-9);
+}
+
+TEST_F(TypeTraits, is_unit) {
+  EXPECT_FALSE(traits::is_unit<std::ratio<1>>::value);
+  EXPECT_FALSE(traits::is_unit<double>::value);
+  EXPECT_TRUE(traits::is_unit<meters>::value);
+  EXPECT_TRUE(traits::is_unit<feet>::value);
+  EXPECT_TRUE(traits::is_unit<degrees_squared>::value);
+  EXPECT_FALSE(traits::is_unit<meter_t>::value);
+}
+
+TEST_F(TypeTraits, is_unit_t) {
+  EXPECT_FALSE(traits::is_unit_t<std::ratio<1>>::value);
+  EXPECT_FALSE(traits::is_unit_t<double>::value);
+  EXPECT_FALSE(traits::is_unit_t<meters>::value);
+  EXPECT_FALSE(traits::is_unit_t<feet>::value);
+  EXPECT_FALSE(traits::is_unit_t<degrees_squared>::value);
+  EXPECT_TRUE(traits::is_unit_t<meter_t>::value);
+}
+
+TEST_F(TypeTraits, unit_traits) {
+  EXPECT_TRUE(
+      (std::is_same<void,
+                    traits::unit_traits<double>::conversion_ratio>::value));
+  EXPECT_FALSE(
+      (std::is_same<void,
+                    traits::unit_traits<meters>::conversion_ratio>::value));
+}
+
+TEST_F(TypeTraits, unit_t_traits) {
+  EXPECT_TRUE(
+      (std::is_same<void,
+                    traits::unit_t_traits<double>::underlying_type>::value));
+  EXPECT_TRUE(
+      (std::is_same<UNIT_LIB_DEFAULT_TYPE,
+                    traits::unit_t_traits<meter_t>::underlying_type>::value));
+  EXPECT_TRUE(
+      (std::is_same<void, traits::unit_t_traits<double>::value_type>::value));
+  EXPECT_TRUE(
+      (std::is_same<UNIT_LIB_DEFAULT_TYPE,
+                    traits::unit_t_traits<meter_t>::value_type>::value));
+}
+
+TEST_F(TypeTraits, all_true) {
+  EXPECT_TRUE(all_true<true>::type::value);
+  EXPECT_TRUE((all_true<true, true>::type::value));
+  EXPECT_TRUE((all_true<true, true, true>::type::value));
+  EXPECT_FALSE(all_true<false>::type::value);
+  EXPECT_FALSE((all_true<true, false>::type::value));
+  EXPECT_FALSE((all_true<true, true, false>::type::value));
+  EXPECT_FALSE((all_true<false, false, false>::type::value));
+}
+
+TEST_F(TypeTraits, is_convertible_unit) {
+  EXPECT_TRUE((traits::is_convertible_unit<meters, meters>::value));
+  EXPECT_TRUE((traits::is_convertible_unit<meters, astronicalUnits>::value));
+  EXPECT_TRUE((traits::is_convertible_unit<meters, parsecs>::value));
+
+  EXPECT_TRUE((traits::is_convertible_unit<meters, meters>::value));
+  EXPECT_TRUE((traits::is_convertible_unit<astronicalUnits, meters>::value));
+  EXPECT_TRUE((traits::is_convertible_unit<parsecs, meters>::value));
+  EXPECT_TRUE((traits::is_convertible_unit<years, weeks>::value));
+
+  EXPECT_FALSE((traits::is_convertible_unit<meters, seconds>::value));
+  EXPECT_FALSE((traits::is_convertible_unit<seconds, meters>::value));
+  EXPECT_FALSE((traits::is_convertible_unit<years, meters>::value));
+}
+
+TEST_F(TypeTraits, inverse) {
+  double test;
+
+  using htz = inverse<seconds>;
+  bool shouldBeTrue = std::is_same<htz, hertz>::value;
+  EXPECT_TRUE(shouldBeTrue);
+
+  test = convert<inverse<celsius>, inverse<fahrenheit>>(1.0);
+  EXPECT_NEAR(5.0 / 9.0, test, 5.0e-5);
+
+  test = convert<inverse<kelvin>, inverse<fahrenheit>>(6.0);
+  EXPECT_NEAR(10.0 / 3.0, test, 5.0e-5);
+}
+
+TEST_F(TypeTraits, base_unit_of) {
+  using base = traits::base_unit_of<years>;
+  bool shouldBeTrue = std::is_same<base, category::time_unit>::value;
+
+  EXPECT_TRUE(shouldBeTrue);
+}
+
+TEST_F(TypeTraits, has_linear_scale) {
+  EXPECT_TRUE((traits::has_linear_scale<scalar_t>::value));
+  EXPECT_TRUE((traits::has_linear_scale<meter_t>::value));
+  EXPECT_TRUE((traits::has_linear_scale<foot_t>::value));
+  EXPECT_TRUE((traits::has_linear_scale<watt_t, scalar_t>::value));
+  EXPECT_TRUE((traits::has_linear_scale<scalar_t, meter_t>::value));
+  EXPECT_TRUE((traits::has_linear_scale<meters_per_second_t>::value));
+  EXPECT_FALSE((traits::has_linear_scale<dB_t>::value));
+  EXPECT_FALSE((traits::has_linear_scale<dB_t, meters_per_second_t>::value));
+}
+
+TEST_F(TypeTraits, has_decibel_scale) {
+  EXPECT_FALSE((traits::has_decibel_scale<scalar_t>::value));
+  EXPECT_FALSE((traits::has_decibel_scale<meter_t>::value));
+  EXPECT_FALSE((traits::has_decibel_scale<foot_t>::value));
+  EXPECT_TRUE((traits::has_decibel_scale<dB_t>::value));
+  EXPECT_TRUE((traits::has_decibel_scale<dBW_t>::value));
+
+  EXPECT_TRUE((traits::has_decibel_scale<dBW_t, dB_t>::value));
+  EXPECT_TRUE((traits::has_decibel_scale<dBW_t, dBm_t>::value));
+  EXPECT_TRUE((traits::has_decibel_scale<dB_t, dB_t>::value));
+  EXPECT_TRUE((traits::has_decibel_scale<dB_t, dB_t, dB_t>::value));
+  EXPECT_FALSE((traits::has_decibel_scale<dB_t, dB_t, meter_t>::value));
+  EXPECT_FALSE((traits::has_decibel_scale<meter_t, dB_t>::value));
+}
+
+TEST_F(TypeTraits, is_same_scale) {
+  EXPECT_TRUE((traits::is_same_scale<scalar_t, dimensionless_t>::value));
+  EXPECT_TRUE((traits::is_same_scale<dB_t, dBW_t>::value));
+  EXPECT_FALSE((traits::is_same_scale<dB_t, scalar_t>::value));
+}
+
+TEST_F(TypeTraits, is_dimensionless_unit) {
+  EXPECT_TRUE((traits::is_dimensionless_unit<scalar_t>::value));
+  EXPECT_TRUE((traits::is_dimensionless_unit<const scalar_t>::value));
+  EXPECT_TRUE((traits::is_dimensionless_unit<const scalar_t&>::value));
+  EXPECT_TRUE((traits::is_dimensionless_unit<dimensionless_t>::value));
+  EXPECT_TRUE((traits::is_dimensionless_unit<dB_t>::value));
+  EXPECT_TRUE((traits::is_dimensionless_unit<dB_t, scalar_t>::value));
+  EXPECT_TRUE((traits::is_dimensionless_unit<ppm_t>::value));
+  EXPECT_FALSE((traits::is_dimensionless_unit<meter_t>::value));
+  EXPECT_FALSE((traits::is_dimensionless_unit<dBW_t>::value));
+  EXPECT_FALSE((traits::is_dimensionless_unit<dBW_t, scalar_t>::value));
+}
+
+TEST_F(TypeTraits, is_length_unit) {
+  EXPECT_TRUE((traits::is_length_unit<meter>::value));
+  EXPECT_TRUE((traits::is_length_unit<cubit>::value));
+  EXPECT_FALSE((traits::is_length_unit<year>::value));
+  EXPECT_FALSE((traits::is_length_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_length_unit<meter_t>::value));
+  EXPECT_TRUE((traits::is_length_unit<const meter_t>::value));
+  EXPECT_TRUE((traits::is_length_unit<const meter_t&>::value));
+  EXPECT_TRUE((traits::is_length_unit<cubit_t>::value));
+  EXPECT_FALSE((traits::is_length_unit<year_t>::value));
+  EXPECT_TRUE((traits::is_length_unit<meter_t, cubit_t>::value));
+  EXPECT_FALSE((traits::is_length_unit<meter_t, year_t>::value));
+}
+
+TEST_F(TypeTraits, is_mass_unit) {
+  EXPECT_TRUE((traits::is_mass_unit<kilogram>::value));
+  EXPECT_TRUE((traits::is_mass_unit<stone>::value));
+  EXPECT_FALSE((traits::is_mass_unit<meter>::value));
+  EXPECT_FALSE((traits::is_mass_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_mass_unit<kilogram_t>::value));
+  EXPECT_TRUE((traits::is_mass_unit<const kilogram_t>::value));
+  EXPECT_TRUE((traits::is_mass_unit<const kilogram_t&>::value));
+  EXPECT_TRUE((traits::is_mass_unit<stone_t>::value));
+  EXPECT_FALSE((traits::is_mass_unit<meter_t>::value));
+  EXPECT_TRUE((traits::is_mass_unit<kilogram_t, stone_t>::value));
+  EXPECT_FALSE((traits::is_mass_unit<kilogram_t, meter_t>::value));
+}
+
+TEST_F(TypeTraits, is_time_unit) {
+  EXPECT_TRUE((traits::is_time_unit<second>::value));
+  EXPECT_TRUE((traits::is_time_unit<year>::value));
+  EXPECT_FALSE((traits::is_time_unit<meter>::value));
+  EXPECT_FALSE((traits::is_time_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_time_unit<second_t>::value));
+  EXPECT_TRUE((traits::is_time_unit<const second_t>::value));
+  EXPECT_TRUE((traits::is_time_unit<const second_t&>::value));
+  EXPECT_TRUE((traits::is_time_unit<year_t>::value));
+  EXPECT_FALSE((traits::is_time_unit<meter_t>::value));
+  EXPECT_TRUE((traits::is_time_unit<second_t, year_t>::value));
+  EXPECT_FALSE((traits::is_time_unit<second_t, meter_t>::value));
+}
+
+TEST_F(TypeTraits, is_angle_unit) {
+  EXPECT_TRUE((traits::is_angle_unit<angle::radian>::value));
+  EXPECT_TRUE((traits::is_angle_unit<angle::degree>::value));
+  EXPECT_FALSE((traits::is_angle_unit<watt>::value));
+  EXPECT_FALSE((traits::is_angle_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_angle_unit<angle::radian_t>::value));
+  EXPECT_TRUE((traits::is_angle_unit<const angle::radian_t>::value));
+  EXPECT_TRUE((traits::is_angle_unit<const angle::radian_t&>::value));
+  EXPECT_TRUE((traits::is_angle_unit<angle::degree_t>::value));
+  EXPECT_FALSE((traits::is_angle_unit<watt_t>::value));
+  EXPECT_TRUE((traits::is_angle_unit<angle::radian_t, angle::degree_t>::value));
+  EXPECT_FALSE((traits::is_angle_unit<angle::radian_t, watt_t>::value));
+}
+
+TEST_F(TypeTraits, is_current_unit) {
+  EXPECT_TRUE((traits::is_current_unit<current::ampere>::value));
+  EXPECT_FALSE((traits::is_current_unit<volt>::value));
+  EXPECT_FALSE((traits::is_current_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_current_unit<current::ampere_t>::value));
+  EXPECT_TRUE((traits::is_current_unit<const current::ampere_t>::value));
+  EXPECT_TRUE((traits::is_current_unit<const current::ampere_t&>::value));
+  EXPECT_FALSE((traits::is_current_unit<volt_t>::value));
+  EXPECT_TRUE((traits::is_current_unit<current::ampere_t,
+                                       current::milliampere_t>::value));
+  EXPECT_FALSE((traits::is_current_unit<current::ampere_t, volt_t>::value));
+}
+
+TEST_F(TypeTraits, is_temperature_unit) {
+  EXPECT_TRUE((traits::is_temperature_unit<fahrenheit>::value));
+  EXPECT_TRUE((traits::is_temperature_unit<kelvin>::value));
+  EXPECT_FALSE((traits::is_temperature_unit<cubit>::value));
+  EXPECT_FALSE((traits::is_temperature_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_temperature_unit<fahrenheit_t>::value));
+  EXPECT_TRUE((traits::is_temperature_unit<const fahrenheit_t>::value));
+  EXPECT_TRUE((traits::is_temperature_unit<const fahrenheit_t&>::value));
+  EXPECT_TRUE((traits::is_temperature_unit<kelvin_t>::value));
+  EXPECT_FALSE((traits::is_temperature_unit<cubit_t>::value));
+  EXPECT_TRUE((traits::is_temperature_unit<fahrenheit_t, kelvin_t>::value));
+  EXPECT_FALSE((traits::is_temperature_unit<cubit_t, fahrenheit_t>::value));
+}
+
+TEST_F(TypeTraits, is_substance_unit) {
+  EXPECT_TRUE((traits::is_substance_unit<substance::mol>::value));
+  EXPECT_FALSE((traits::is_substance_unit<year>::value));
+  EXPECT_FALSE((traits::is_substance_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_substance_unit<substance::mole_t>::value));
+  EXPECT_TRUE((traits::is_substance_unit<const substance::mole_t>::value));
+  EXPECT_TRUE((traits::is_substance_unit<const substance::mole_t&>::value));
+  EXPECT_FALSE((traits::is_substance_unit<year_t>::value));
+  EXPECT_TRUE(
+      (traits::is_substance_unit<substance::mole_t, substance::mole_t>::value));
+  EXPECT_FALSE((traits::is_substance_unit<year_t, substance::mole_t>::value));
+}
+
+TEST_F(TypeTraits, is_luminous_intensity_unit) {
+  EXPECT_TRUE((traits::is_luminous_intensity_unit<candela>::value));
+  EXPECT_FALSE(
+      (traits::is_luminous_intensity_unit<units::radiation::rad>::value));
+  EXPECT_FALSE((traits::is_luminous_intensity_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_luminous_intensity_unit<candela_t>::value));
+  EXPECT_TRUE((traits::is_luminous_intensity_unit<const candela_t>::value));
+  EXPECT_TRUE((traits::is_luminous_intensity_unit<const candela_t&>::value));
+  EXPECT_FALSE((traits::is_luminous_intensity_unit<rad_t>::value));
+  EXPECT_TRUE(
+      (traits::is_luminous_intensity_unit<candela_t, candela_t>::value));
+  EXPECT_FALSE((traits::is_luminous_intensity_unit<rad_t, candela_t>::value));
+}
+
+TEST_F(TypeTraits, is_solid_angle_unit) {
+  EXPECT_TRUE((traits::is_solid_angle_unit<steradian>::value));
+  EXPECT_TRUE((traits::is_solid_angle_unit<degree_squared>::value));
+  EXPECT_FALSE((traits::is_solid_angle_unit<angle::degree>::value));
+  EXPECT_FALSE((traits::is_solid_angle_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_solid_angle_unit<steradian_t>::value));
+  EXPECT_TRUE((traits::is_solid_angle_unit<const steradian_t>::value));
+  EXPECT_TRUE((traits::is_solid_angle_unit<const degree_squared_t&>::value));
+  EXPECT_FALSE((traits::is_solid_angle_unit<angle::degree_t>::value));
+  EXPECT_TRUE(
+      (traits::is_solid_angle_unit<degree_squared_t, steradian_t>::value));
+  EXPECT_FALSE(
+      (traits::is_solid_angle_unit<angle::degree_t, steradian_t>::value));
+}
+
+TEST_F(TypeTraits, is_frequency_unit) {
+  EXPECT_TRUE((traits::is_frequency_unit<hertz>::value));
+  EXPECT_FALSE((traits::is_frequency_unit<second>::value));
+  EXPECT_FALSE((traits::is_frequency_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_frequency_unit<hertz_t>::value));
+  EXPECT_TRUE((traits::is_frequency_unit<const hertz_t>::value));
+  EXPECT_TRUE((traits::is_frequency_unit<const hertz_t&>::value));
+  EXPECT_FALSE((traits::is_frequency_unit<second_t>::value));
+  EXPECT_TRUE((traits::is_frequency_unit<const hertz_t&, gigahertz_t>::value));
+  EXPECT_FALSE((traits::is_frequency_unit<second_t, hertz_t>::value));
+}
+
+TEST_F(TypeTraits, is_velocity_unit) {
+  EXPECT_TRUE((traits::is_velocity_unit<meters_per_second>::value));
+  EXPECT_TRUE((traits::is_velocity_unit<miles_per_hour>::value));
+  EXPECT_FALSE((traits::is_velocity_unit<meters_per_second_squared>::value));
+  EXPECT_FALSE((traits::is_velocity_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_velocity_unit<meters_per_second_t>::value));
+  EXPECT_TRUE((traits::is_velocity_unit<const meters_per_second_t>::value));
+  EXPECT_TRUE((traits::is_velocity_unit<const meters_per_second_t&>::value));
+  EXPECT_TRUE((traits::is_velocity_unit<miles_per_hour_t>::value));
+  EXPECT_FALSE((traits::is_velocity_unit<meters_per_second_squared_t>::value));
+  EXPECT_TRUE(
+      (traits::is_velocity_unit<miles_per_hour_t, meters_per_second_t>::value));
+  EXPECT_FALSE((traits::is_velocity_unit<meters_per_second_squared_t,
+                                         meters_per_second_t>::value));
+}
+
+TEST_F(TypeTraits, is_acceleration_unit) {
+  EXPECT_TRUE((traits::is_acceleration_unit<meters_per_second_squared>::value));
+  EXPECT_TRUE(
+      (traits::is_acceleration_unit<acceleration::standard_gravity>::value));
+  EXPECT_FALSE((traits::is_acceleration_unit<inch>::value));
+  EXPECT_FALSE((traits::is_acceleration_unit<double>::value));
+
+  EXPECT_TRUE(
+      (traits::is_acceleration_unit<meters_per_second_squared_t>::value));
+  EXPECT_TRUE(
+      (traits::is_acceleration_unit<const meters_per_second_squared_t>::value));
+  EXPECT_TRUE((
+      traits::is_acceleration_unit<const meters_per_second_squared_t&>::value));
+  EXPECT_TRUE((traits::is_acceleration_unit<standard_gravity_t>::value));
+  EXPECT_FALSE((traits::is_acceleration_unit<inch_t>::value));
+  EXPECT_TRUE(
+      (traits::is_acceleration_unit<standard_gravity_t,
+                                    meters_per_second_squared_t>::value));
+  EXPECT_FALSE(
+      (traits::is_acceleration_unit<inch_t,
+                                    meters_per_second_squared_t>::value));
+}
+
+TEST_F(TypeTraits, is_force_unit) {
+  EXPECT_TRUE((traits::is_force_unit<units::force::newton>::value));
+  EXPECT_TRUE((traits::is_force_unit<units::force::dynes>::value));
+  EXPECT_FALSE((traits::is_force_unit<meter>::value));
+  EXPECT_FALSE((traits::is_force_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_force_unit<units::force::newton_t>::value));
+  EXPECT_TRUE((traits::is_force_unit<const units::force::newton_t>::value));
+  EXPECT_TRUE((traits::is_force_unit<const units::force::newton_t&>::value));
+  EXPECT_TRUE((traits::is_force_unit<units::force::dyne_t>::value));
+  EXPECT_FALSE((traits::is_force_unit<watt_t>::value));
+  EXPECT_TRUE((traits::is_force_unit<units::force::dyne_t,
+                                     units::force::newton_t>::value));
+  EXPECT_FALSE((traits::is_force_unit<watt_t, units::force::newton_t>::value));
+}
+
+TEST_F(TypeTraits, is_pressure_unit) {
+  EXPECT_TRUE((traits::is_pressure_unit<pressure::pascals>::value));
+  EXPECT_TRUE((traits::is_pressure_unit<atmosphere>::value));
+  EXPECT_FALSE((traits::is_pressure_unit<year>::value));
+  EXPECT_FALSE((traits::is_pressure_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_pressure_unit<pascal_t>::value));
+  EXPECT_TRUE((traits::is_pressure_unit<const pascal_t>::value));
+  EXPECT_TRUE((traits::is_pressure_unit<const pascal_t&>::value));
+  EXPECT_TRUE((traits::is_pressure_unit<atmosphere_t>::value));
+  EXPECT_FALSE((traits::is_pressure_unit<year_t>::value));
+  EXPECT_TRUE(
+      (traits::is_pressure_unit<atmosphere_t, pressure::pascal_t>::value));
+  EXPECT_FALSE((traits::is_pressure_unit<year_t, pressure::pascal_t>::value));
+}
+
+TEST_F(TypeTraits, is_charge_unit) {
+  EXPECT_TRUE((traits::is_charge_unit<coulomb>::value));
+  EXPECT_FALSE((traits::is_charge_unit<watt>::value));
+  EXPECT_FALSE((traits::is_charge_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_charge_unit<coulomb_t>::value));
+  EXPECT_TRUE((traits::is_charge_unit<const coulomb_t>::value));
+  EXPECT_TRUE((traits::is_charge_unit<const coulomb_t&>::value));
+  EXPECT_FALSE((traits::is_charge_unit<watt_t>::value));
+  EXPECT_TRUE((traits::is_charge_unit<const coulomb_t&, coulomb_t>::value));
+  EXPECT_FALSE((traits::is_charge_unit<watt_t, coulomb_t>::value));
+}
+
+TEST_F(TypeTraits, is_energy_unit) {
+  EXPECT_TRUE((traits::is_energy_unit<joule>::value));
+  EXPECT_TRUE((traits::is_energy_unit<calorie>::value));
+  EXPECT_FALSE((traits::is_energy_unit<watt>::value));
+  EXPECT_FALSE((traits::is_energy_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_energy_unit<joule_t>::value));
+  EXPECT_TRUE((traits::is_energy_unit<const joule_t>::value));
+  EXPECT_TRUE((traits::is_energy_unit<const joule_t&>::value));
+  EXPECT_TRUE((traits::is_energy_unit<calorie_t>::value));
+  EXPECT_FALSE((traits::is_energy_unit<watt_t>::value));
+  EXPECT_TRUE((traits::is_energy_unit<calorie_t, joule_t>::value));
+  EXPECT_FALSE((traits::is_energy_unit<watt_t, joule_t>::value));
+}
+
+TEST_F(TypeTraits, is_power_unit) {
+  EXPECT_TRUE((traits::is_power_unit<watt>::value));
+  EXPECT_FALSE((traits::is_power_unit<henry>::value));
+  EXPECT_FALSE((traits::is_power_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_power_unit<watt_t>::value));
+  EXPECT_TRUE((traits::is_power_unit<const watt_t>::value));
+  EXPECT_TRUE((traits::is_power_unit<const watt_t&>::value));
+  EXPECT_FALSE((traits::is_power_unit<henry_t>::value));
+  EXPECT_TRUE((traits::is_power_unit<const watt_t&, watt_t>::value));
+  EXPECT_FALSE((traits::is_power_unit<henry_t, watt_t>::value));
+}
+
+TEST_F(TypeTraits, is_voltage_unit) {
+  EXPECT_TRUE((traits::is_voltage_unit<volt>::value));
+  EXPECT_FALSE((traits::is_voltage_unit<henry>::value));
+  EXPECT_FALSE((traits::is_voltage_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_voltage_unit<volt_t>::value));
+  EXPECT_TRUE((traits::is_voltage_unit<const volt_t>::value));
+  EXPECT_TRUE((traits::is_voltage_unit<const volt_t&>::value));
+  EXPECT_FALSE((traits::is_voltage_unit<henry_t>::value));
+  EXPECT_TRUE((traits::is_voltage_unit<const volt_t&, volt_t>::value));
+  EXPECT_FALSE((traits::is_voltage_unit<henry_t, volt_t>::value));
+}
+
+TEST_F(TypeTraits, is_capacitance_unit) {
+  EXPECT_TRUE((traits::is_capacitance_unit<farad>::value));
+  EXPECT_FALSE((traits::is_capacitance_unit<ohm>::value));
+  EXPECT_FALSE((traits::is_capacitance_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_capacitance_unit<farad_t>::value));
+  EXPECT_TRUE((traits::is_capacitance_unit<const farad_t>::value));
+  EXPECT_TRUE((traits::is_capacitance_unit<const farad_t&>::value));
+  EXPECT_FALSE((traits::is_capacitance_unit<ohm_t>::value));
+  EXPECT_TRUE(
+      (traits::is_capacitance_unit<const farad_t&, millifarad_t>::value));
+  EXPECT_FALSE((traits::is_capacitance_unit<ohm_t, farad_t>::value));
+}
+
+TEST_F(TypeTraits, is_impedance_unit) {
+  EXPECT_TRUE((traits::is_impedance_unit<ohm>::value));
+  EXPECT_FALSE((traits::is_impedance_unit<farad>::value));
+  EXPECT_FALSE((traits::is_impedance_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_impedance_unit<ohm_t>::value));
+  EXPECT_TRUE((traits::is_impedance_unit<const ohm_t>::value));
+  EXPECT_TRUE((traits::is_impedance_unit<const ohm_t&>::value));
+  EXPECT_FALSE((traits::is_impedance_unit<farad_t>::value));
+  EXPECT_TRUE((traits::is_impedance_unit<const ohm_t&, milliohm_t>::value));
+  EXPECT_FALSE((traits::is_impedance_unit<farad_t, ohm_t>::value));
+}
+
+TEST_F(TypeTraits, is_conductance_unit) {
+  EXPECT_TRUE((traits::is_conductance_unit<siemens>::value));
+  EXPECT_FALSE((traits::is_conductance_unit<volt>::value));
+  EXPECT_FALSE((traits::is_conductance_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_conductance_unit<siemens_t>::value));
+  EXPECT_TRUE((traits::is_conductance_unit<const siemens_t>::value));
+  EXPECT_TRUE((traits::is_conductance_unit<const siemens_t&>::value));
+  EXPECT_FALSE((traits::is_conductance_unit<volt_t>::value));
+  EXPECT_TRUE(
+      (traits::is_conductance_unit<const siemens_t&, millisiemens_t>::value));
+  EXPECT_FALSE((traits::is_conductance_unit<volt_t, siemens_t>::value));
+}
+
+TEST_F(TypeTraits, is_magnetic_flux_unit) {
+  EXPECT_TRUE((traits::is_magnetic_flux_unit<weber>::value));
+  EXPECT_TRUE((traits::is_magnetic_flux_unit<maxwell>::value));
+  EXPECT_FALSE((traits::is_magnetic_flux_unit<inch>::value));
+  EXPECT_FALSE((traits::is_magnetic_flux_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_magnetic_flux_unit<weber_t>::value));
+  EXPECT_TRUE((traits::is_magnetic_flux_unit<const weber_t>::value));
+  EXPECT_TRUE((traits::is_magnetic_flux_unit<const weber_t&>::value));
+  EXPECT_TRUE((traits::is_magnetic_flux_unit<maxwell_t>::value));
+  EXPECT_FALSE((traits::is_magnetic_flux_unit<inch_t>::value));
+  EXPECT_TRUE((traits::is_magnetic_flux_unit<maxwell_t, weber_t>::value));
+  EXPECT_FALSE((traits::is_magnetic_flux_unit<inch_t, weber_t>::value));
+}
+
+TEST_F(TypeTraits, is_magnetic_field_strength_unit) {
+  EXPECT_TRUE((traits::is_magnetic_field_strength_unit<
+               units::magnetic_field_strength::tesla>::value));
+  EXPECT_TRUE((traits::is_magnetic_field_strength_unit<gauss>::value));
+  EXPECT_FALSE((traits::is_magnetic_field_strength_unit<volt>::value));
+  EXPECT_FALSE((traits::is_magnetic_field_strength_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_magnetic_field_strength_unit<tesla_t>::value));
+  EXPECT_TRUE((traits::is_magnetic_field_strength_unit<const tesla_t>::value));
+  EXPECT_TRUE((traits::is_magnetic_field_strength_unit<const tesla_t&>::value));
+  EXPECT_TRUE((traits::is_magnetic_field_strength_unit<gauss_t>::value));
+  EXPECT_FALSE((traits::is_magnetic_field_strength_unit<volt_t>::value));
+  EXPECT_TRUE(
+      (traits::is_magnetic_field_strength_unit<gauss_t, tesla_t>::value));
+  EXPECT_FALSE(
+      (traits::is_magnetic_field_strength_unit<volt_t, tesla_t>::value));
+}
+
+TEST_F(TypeTraits, is_inductance_unit) {
+  EXPECT_TRUE((traits::is_inductance_unit<henry>::value));
+  EXPECT_FALSE((traits::is_inductance_unit<farad>::value));
+  EXPECT_FALSE((traits::is_inductance_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_inductance_unit<henry_t>::value));
+  EXPECT_TRUE((traits::is_inductance_unit<const henry_t>::value));
+  EXPECT_TRUE((traits::is_inductance_unit<const henry_t&>::value));
+  EXPECT_FALSE((traits::is_inductance_unit<farad_t>::value));
+  EXPECT_TRUE(
+      (traits::is_inductance_unit<const henry_t&, millihenry_t>::value));
+  EXPECT_FALSE((traits::is_inductance_unit<farad_t, henry_t>::value));
+}
+
+TEST_F(TypeTraits, is_luminous_flux_unit) {
+  EXPECT_TRUE((traits::is_luminous_flux_unit<lumen>::value));
+  EXPECT_FALSE((traits::is_luminous_flux_unit<pound>::value));
+  EXPECT_FALSE((traits::is_luminous_flux_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_luminous_flux_unit<lumen_t>::value));
+  EXPECT_TRUE((traits::is_luminous_flux_unit<const lumen_t>::value));
+  EXPECT_TRUE((traits::is_luminous_flux_unit<const lumen_t&>::value));
+  EXPECT_FALSE((traits::is_luminous_flux_unit<pound_t>::value));
+  EXPECT_TRUE(
+      (traits::is_luminous_flux_unit<const lumen_t&, millilumen_t>::value));
+  EXPECT_FALSE((traits::is_luminous_flux_unit<pound_t, lumen_t>::value));
+}
+
+TEST_F(TypeTraits, is_illuminance_unit) {
+  EXPECT_TRUE((traits::is_illuminance_unit<illuminance::footcandle>::value));
+  EXPECT_TRUE((traits::is_illuminance_unit<illuminance::lux>::value));
+  EXPECT_FALSE((traits::is_illuminance_unit<meter>::value));
+  EXPECT_FALSE((traits::is_illuminance_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_illuminance_unit<footcandle_t>::value));
+  EXPECT_TRUE((traits::is_illuminance_unit<const footcandle_t>::value));
+  EXPECT_TRUE((traits::is_illuminance_unit<const footcandle_t&>::value));
+  EXPECT_TRUE((traits::is_illuminance_unit<lux_t>::value));
+  EXPECT_FALSE((traits::is_illuminance_unit<meter_t>::value));
+  EXPECT_TRUE((traits::is_illuminance_unit<lux_t, footcandle_t>::value));
+  EXPECT_FALSE((traits::is_illuminance_unit<meter_t, footcandle_t>::value));
+}
+
+TEST_F(TypeTraits, is_radioactivity_unit) {
+  EXPECT_TRUE((traits::is_radioactivity_unit<becquerel>::value));
+  EXPECT_FALSE((traits::is_radioactivity_unit<year>::value));
+  EXPECT_FALSE((traits::is_radioactivity_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_radioactivity_unit<becquerel_t>::value));
+  EXPECT_TRUE((traits::is_radioactivity_unit<const becquerel_t>::value));
+  EXPECT_TRUE((traits::is_radioactivity_unit<const becquerel_t&>::value));
+  EXPECT_FALSE((traits::is_radioactivity_unit<year_t>::value));
+  EXPECT_TRUE((traits::is_radioactivity_unit<const becquerel_t&,
+                                             millibecquerel_t>::value));
+  EXPECT_FALSE((traits::is_radioactivity_unit<year_t, becquerel_t>::value));
+}
+
+TEST_F(TypeTraits, is_torque_unit) {
+  EXPECT_TRUE((traits::is_torque_unit<torque::newton_meter>::value));
+  EXPECT_TRUE((traits::is_torque_unit<torque::foot_pound>::value));
+  EXPECT_FALSE((traits::is_torque_unit<volume::cubic_meter>::value));
+  EXPECT_FALSE((traits::is_torque_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_torque_unit<torque::newton_meter_t>::value));
+  EXPECT_TRUE((traits::is_torque_unit<const torque::newton_meter_t>::value));
+  EXPECT_TRUE((traits::is_torque_unit<const torque::newton_meter_t&>::value));
+  EXPECT_TRUE((traits::is_torque_unit<torque::foot_pound_t>::value));
+  EXPECT_FALSE((traits::is_torque_unit<volume::cubic_meter_t>::value));
+  EXPECT_TRUE((traits::is_torque_unit<torque::foot_pound_t,
+                                      torque::newton_meter_t>::value));
+  EXPECT_FALSE((traits::is_torque_unit<volume::cubic_meter_t,
+                                       torque::newton_meter_t>::value));
+}
+
+TEST_F(TypeTraits, is_area_unit) {
+  EXPECT_TRUE((traits::is_area_unit<square_meter>::value));
+  EXPECT_TRUE((traits::is_area_unit<hectare>::value));
+  EXPECT_FALSE((traits::is_area_unit<astronicalUnit>::value));
+  EXPECT_FALSE((traits::is_area_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_area_unit<square_meter_t>::value));
+  EXPECT_TRUE((traits::is_area_unit<const square_meter_t>::value));
+  EXPECT_TRUE((traits::is_area_unit<const square_meter_t&>::value));
+  EXPECT_TRUE((traits::is_area_unit<hectare_t>::value));
+  EXPECT_FALSE((traits::is_area_unit<astronicalUnit_t>::value));
+  EXPECT_TRUE((traits::is_area_unit<hectare_t, square_meter_t>::value));
+  EXPECT_FALSE((traits::is_area_unit<astronicalUnit_t, square_meter_t>::value));
+}
+
+TEST_F(TypeTraits, is_volume_unit) {
+  EXPECT_TRUE((traits::is_volume_unit<cubic_meter>::value));
+  EXPECT_TRUE((traits::is_volume_unit<cubic_foot>::value));
+  EXPECT_FALSE((traits::is_volume_unit<square_feet>::value));
+  EXPECT_FALSE((traits::is_volume_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_volume_unit<cubic_meter_t>::value));
+  EXPECT_TRUE((traits::is_volume_unit<const cubic_meter_t>::value));
+  EXPECT_TRUE((traits::is_volume_unit<const cubic_meter_t&>::value));
+  EXPECT_TRUE((traits::is_volume_unit<cubic_inch_t>::value));
+  EXPECT_FALSE((traits::is_volume_unit<foot_t>::value));
+  EXPECT_TRUE((traits::is_volume_unit<cubic_inch_t, cubic_meter_t>::value));
+  EXPECT_FALSE((traits::is_volume_unit<foot_t, cubic_meter_t>::value));
+}
+
+TEST_F(TypeTraits, is_density_unit) {
+  EXPECT_TRUE((traits::is_density_unit<kilograms_per_cubic_meter>::value));
+  EXPECT_TRUE((traits::is_density_unit<ounces_per_cubic_foot>::value));
+  EXPECT_FALSE((traits::is_density_unit<year>::value));
+  EXPECT_FALSE((traits::is_density_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_density_unit<kilograms_per_cubic_meter_t>::value));
+  EXPECT_TRUE(
+      (traits::is_density_unit<const kilograms_per_cubic_meter_t>::value));
+  EXPECT_TRUE(
+      (traits::is_density_unit<const kilograms_per_cubic_meter_t&>::value));
+  EXPECT_TRUE((traits::is_density_unit<ounces_per_cubic_foot_t>::value));
+  EXPECT_FALSE((traits::is_density_unit<year_t>::value));
+  EXPECT_TRUE((traits::is_density_unit<ounces_per_cubic_foot_t,
+                                       kilograms_per_cubic_meter_t>::value));
+  EXPECT_FALSE(
+      (traits::is_density_unit<year_t, kilograms_per_cubic_meter_t>::value));
+}
+
+TEST_F(TypeTraits, is_data_unit) {
+  EXPECT_TRUE((traits::is_data_unit<bit>::value));
+  EXPECT_TRUE((traits::is_data_unit<byte>::value));
+  EXPECT_TRUE((traits::is_data_unit<exabit>::value));
+  EXPECT_TRUE((traits::is_data_unit<exabyte>::value));
+  EXPECT_FALSE((traits::is_data_unit<year>::value));
+  EXPECT_FALSE((traits::is_data_unit<double>::value));
+
+  EXPECT_TRUE((traits::is_data_unit<bit_t>::value));
+  EXPECT_TRUE((traits::is_data_unit<const bit_t>::value));
+  EXPECT_TRUE((traits::is_data_unit<const bit_t&>::value));
+  EXPECT_TRUE((traits::is_data_unit<byte_t>::value));
+  EXPECT_FALSE((traits::is_data_unit<year_t>::value));
+  EXPECT_TRUE((traits::is_data_unit<bit_t, byte_t>::value));
+  EXPECT_FALSE((traits::is_data_unit<year_t, byte_t>::value));
+}
+
+TEST_F(TypeTraits, is_data_transfer_rate_unit) {
+  EXPECT_TRUE((traits::is_data_transfer_rate_unit<Gbps>::value));
+  EXPECT_TRUE((traits::is_data_transfer_rate_unit<GBps>::value));
+  EXPECT_FALSE((traits::is_data_transfer_rate_unit<year>::value));
+  EXPECT_FALSE((traits::is_data_transfer_rate_unit<double>::value));
+
+  EXPECT_TRUE(
+      (traits::is_data_transfer_rate_unit<gigabits_per_second_t>::value));
+  EXPECT_TRUE((
+      traits::is_data_transfer_rate_unit<const gigabytes_per_second_t>::value));
+  EXPECT_TRUE((traits::is_data_transfer_rate_unit<
+               const gigabytes_per_second_t&>::value));
+  EXPECT_TRUE(
+      (traits::is_data_transfer_rate_unit<gigabytes_per_second_t>::value));
+  EXPECT_FALSE((traits::is_data_transfer_rate_unit<year_t>::value));
+  EXPECT_TRUE(
+      (traits::is_data_transfer_rate_unit<gigabits_per_second_t,
+                                          gigabytes_per_second_t>::value));
+  EXPECT_FALSE(
+      (traits::is_data_transfer_rate_unit<year_t,
+                                          gigabytes_per_second_t>::value));
+}
+
+TEST_F(UnitManipulators, squared) {
+  double test;
+
+  test = convert<squared<meters>, square_feet>(0.092903);
+  EXPECT_NEAR(0.99999956944, test, 5.0e-12);
+
+  using scalar_2 = squared<scalar>;  // this is actually nonsensical, and should
+                                     // also result in a scalar.
+  bool isSame =
+      std::is_same<typename std::decay<scalar_t>::type,
+                   typename std::decay<unit_t<scalar_2>>::type>::value;
+  EXPECT_TRUE(isSame);
+}
+
+TEST_F(UnitManipulators, cubed) {
+  double test;
+
+  test = convert<cubed<meters>, cubic_feet>(0.0283168);
+  EXPECT_NEAR(0.999998354619, test, 5.0e-13);
+}
+
+TEST_F(UnitManipulators, square_root) {
+  double test;
+
+  test = convert<square_root<square_kilometer>, meter>(1.0);
+  EXPECT_TRUE((traits::is_convertible_unit<
+               typename std::decay<square_root<square_kilometer>>::type,
+               kilometer>::value));
+  EXPECT_NEAR(1000.0, test, 5.0e-13);
+}
+
+TEST_F(UnitManipulators, compound_unit) {
+  using acceleration1 = unit<std::ratio<1>, category::acceleration_unit>;
+  using acceleration2 =
+      compound_unit<meters, inverse<seconds>, inverse<seconds>>;
+  using acceleration3 =
+      unit<std::ratio<1>,
+           base_unit<std::ratio<1>, std::ratio<0>, std::ratio<-2>>>;
+  using acceleration4 = compound_unit<meters, inverse<squared<seconds>>>;
+  using acceleration5 = compound_unit<meters, squared<inverse<seconds>>>;
+
+  bool areSame12 = std::is_same<acceleration1, acceleration2>::value;
+  bool areSame23 = std::is_same<acceleration2, acceleration3>::value;
+  bool areSame34 = std::is_same<acceleration3, acceleration4>::value;
+  bool areSame45 = std::is_same<acceleration4, acceleration5>::value;
+
+  EXPECT_TRUE(areSame12);
+  EXPECT_TRUE(areSame23);
+  EXPECT_TRUE(areSame34);
+  EXPECT_TRUE(areSame45);
+
+  // test that thing with translations still compile
+  using arbitrary1 = compound_unit<meters, inverse<celsius>>;
+  using arbitrary2 = compound_unit<meters, celsius>;
+  using arbitrary3 = compound_unit<arbitrary1, arbitrary2>;
+  EXPECT_TRUE((std::is_same<square_meters, arbitrary3>::value));
+}
+
+TEST_F(UnitManipulators, dimensionalAnalysis) {
+  // these look like 'compound units', but the dimensional analysis can be
+  // REALLY handy if the unit types aren't know (i.e. they themselves are
+  // template parameters), as you can get the resulting unit of the operation.
+
+  using velocity = units::detail::unit_divide<meters, second>;
+  bool shouldBeTrue = std::is_same<meters_per_second, velocity>::value;
+  EXPECT_TRUE(shouldBeTrue);
+
+  using acceleration1 = unit<std::ratio<1>, category::acceleration_unit>;
+  using acceleration2 = units::detail::unit_divide<
+      meters, units::detail::unit_multiply<seconds, seconds>>;
+  shouldBeTrue = std::is_same<acceleration1, acceleration2>::value;
+  EXPECT_TRUE(shouldBeTrue);
+}
+
+#ifdef _MSC_VER
+#if (_MSC_VER >= 1900)
+TEST_F(UnitContainer, trivial) {
+  EXPECT_TRUE((std::is_trivial<meter_t>::value));
+  EXPECT_TRUE((std::is_trivially_assignable<meter_t, meter_t>::value));
+  EXPECT_TRUE((std::is_trivially_constructible<meter_t>::value));
+  EXPECT_TRUE((std::is_trivially_copy_assignable<meter_t>::value));
+  EXPECT_TRUE((std::is_trivially_copy_constructible<meter_t>::value));
+  EXPECT_TRUE((std::is_trivially_copyable<meter_t>::value));
+  EXPECT_TRUE((std::is_trivially_default_constructible<meter_t>::value));
+  EXPECT_TRUE((std::is_trivially_destructible<meter_t>::value));
+  EXPECT_TRUE((std::is_trivially_move_assignable<meter_t>::value));
+  EXPECT_TRUE((std::is_trivially_move_constructible<meter_t>::value));
+
+  EXPECT_TRUE((std::is_trivial<dB_t>::value));
+  EXPECT_TRUE((std::is_trivially_assignable<dB_t, dB_t>::value));
+  EXPECT_TRUE((std::is_trivially_constructible<dB_t>::value));
+  EXPECT_TRUE((std::is_trivially_copy_assignable<dB_t>::value));
+  EXPECT_TRUE((std::is_trivially_copy_constructible<dB_t>::value));
+  EXPECT_TRUE((std::is_trivially_copyable<dB_t>::value));
+  EXPECT_TRUE((std::is_trivially_default_constructible<dB_t>::value));
+  EXPECT_TRUE((std::is_trivially_destructible<dB_t>::value));
+  EXPECT_TRUE((std::is_trivially_move_assignable<dB_t>::value));
+  EXPECT_TRUE((std::is_trivially_move_constructible<dB_t>::value));
+}
+#endif
+#endif
+
+TEST_F(UnitContainer, has_value_member) {
+  EXPECT_TRUE((traits::has_value_member<linear_scale<double>, double>::value));
+  EXPECT_FALSE((traits::has_value_member<meter, double>::value));
+}
+
+TEST_F(UnitContainer, make_unit) {
+  auto dist = units::make_unit<meter_t>(5);
+  EXPECT_EQ(meter_t(5), dist);
+}
+
+TEST_F(UnitContainer, unitTypeAddition) {
+  // units
+  meter_t a_m(1.0), c_m;
+  foot_t b_ft(3.28084);
+
+  double d = convert<feet, meters>(b_ft());
+  EXPECT_NEAR(1.0, d, 5.0e-5);
+
+  c_m = a_m + b_ft;
+  EXPECT_NEAR(2.0, c_m(), 5.0e-5);
+
+  c_m = b_ft + meter_t(3);
+  EXPECT_NEAR(4.0, c_m(), 5.0e-5);
+
+  auto e_ft = b_ft + meter_t(3);
+  EXPECT_NEAR(13.12336, e_ft(), 5.0e-6);
+
+  // scalar
+  scalar_t sresult = scalar_t(1.0) + scalar_t(1.0);
+  EXPECT_NEAR(2.0, sresult, 5.0e-6);
+
+  sresult = scalar_t(1.0) + 1.0;
+  EXPECT_NEAR(2.0, sresult, 5.0e-6);
+
+  sresult = 1.0 + scalar_t(1.0);
+  EXPECT_NEAR(2.0, sresult, 5.0e-6);
+
+  d = scalar_t(1.0) + scalar_t(1.0);
+  EXPECT_NEAR(2.0, d, 5.0e-6);
+
+  d = scalar_t(1.0) + 1.0;
+  EXPECT_NEAR(2.0, d, 5.0e-6);
+
+  d = 1.0 + scalar_t(1.0);
+  EXPECT_NEAR(2.0, d, 5.0e-6);
+}
+
+TEST_F(UnitContainer, unitTypeUnaryAddition) {
+  meter_t a_m(1.0);
+
+  EXPECT_EQ(++a_m, meter_t(2));
+  EXPECT_EQ(a_m++, meter_t(2));
+  EXPECT_EQ(a_m, meter_t(3));
+  EXPECT_EQ(+a_m, meter_t(3));
+  EXPECT_EQ(a_m, meter_t(3));
+
+  dBW_t b_dBW(1.0);
+
+  EXPECT_EQ(++b_dBW, dBW_t(2));
+  EXPECT_EQ(b_dBW++, dBW_t(2));
+  EXPECT_EQ(b_dBW, dBW_t(3));
+  EXPECT_EQ(+b_dBW, dBW_t(3));
+  EXPECT_EQ(b_dBW, dBW_t(3));
+}
+
+TEST_F(UnitContainer, unitTypeSubtraction) {
+  meter_t a_m(1.0), c_m;
+  foot_t b_ft(3.28084);
+
+  c_m = a_m - b_ft;
+  EXPECT_NEAR(0.0, c_m(), 5.0e-5);
+
+  c_m = b_ft - meter_t(1);
+  EXPECT_NEAR(0.0, c_m(), 5.0e-5);
+
+  auto e_ft = b_ft - meter_t(1);
+  EXPECT_NEAR(0.0, e_ft(), 5.0e-6);
+
+  scalar_t sresult = scalar_t(1.0) - scalar_t(1.0);
+  EXPECT_NEAR(0.0, sresult, 5.0e-6);
+
+  sresult = scalar_t(1.0) - 1.0;
+  EXPECT_NEAR(0.0, sresult, 5.0e-6);
+
+  sresult = 1.0 - scalar_t(1.0);
+  EXPECT_NEAR(0.0, sresult, 5.0e-6);
+
+  double d = scalar_t(1.0) - scalar_t(1.0);
+  EXPECT_NEAR(0.0, d, 5.0e-6);
+
+  d = scalar_t(1.0) - 1.0;
+  EXPECT_NEAR(0.0, d, 5.0e-6);
+
+  d = 1.0 - scalar_t(1.0);
+  EXPECT_NEAR(0.0, d, 5.0e-6);
+}
+
+TEST_F(UnitContainer, unitTypeUnarySubtraction) {
+  meter_t a_m(4.0);
+
+  EXPECT_EQ(--a_m, meter_t(3));
+  EXPECT_EQ(a_m--, meter_t(3));
+  EXPECT_EQ(a_m, meter_t(2));
+  EXPECT_EQ(-a_m, meter_t(-2));
+  EXPECT_EQ(a_m, meter_t(2));
+
+  dBW_t b_dBW(4.0);
+
+  EXPECT_EQ(--b_dBW, dBW_t(3));
+  EXPECT_EQ(b_dBW--, dBW_t(3));
+  EXPECT_EQ(b_dBW, dBW_t(2));
+  EXPECT_EQ(-b_dBW, dBW_t(-2));
+  EXPECT_EQ(b_dBW, dBW_t(2));
+}
+
+TEST_F(UnitContainer, unitTypeMultiplication) {
+  meter_t a_m(1.0), b_m(2.0);
+  foot_t a_ft(3.28084);
+
+  auto c_m2 = a_m * b_m;
+  EXPECT_NEAR(2.0, c_m2(), 5.0e-5);
+
+  c_m2 = b_m * meter_t(2);
+  EXPECT_NEAR(4.0, c_m2(), 5.0e-5);
+
+  c_m2 = b_m * a_ft;
+  EXPECT_NEAR(2.0, c_m2(), 5.0e-5);
+
+  auto c_m = b_m * 2.0;
+  EXPECT_NEAR(4.0, c_m(), 5.0e-5);
+
+  c_m = 2.0 * b_m;
+  EXPECT_NEAR(4.0, c_m(), 5.0e-5);
+
+  double convert = scalar_t(3.14);
+  EXPECT_NEAR(3.14, convert, 5.0e-5);
+
+  scalar_t sresult = scalar_t(5.0) * scalar_t(4.0);
+  EXPECT_NEAR(20.0, sresult(), 5.0e-5);
+
+  sresult = scalar_t(5.0) * 4.0;
+  EXPECT_NEAR(20.0, sresult(), 5.0e-5);
+
+  sresult = 4.0 * scalar_t(5.0);
+  EXPECT_NEAR(20.0, sresult(), 5.0e-5);
+
+  double result = scalar_t(5.0) * scalar_t(4.0);
+  EXPECT_NEAR(20.0, result, 5.0e-5);
+
+  result = scalar_t(5.0) * 4.0;
+  EXPECT_NEAR(20.0, result, 5.0e-5);
+
+  result = 4.0 * scalar_t(5.0);
+  EXPECT_NEAR(20.0, result, 5.0e-5);
+}
+
+TEST_F(UnitContainer, unitTypeMixedUnitMultiplication) {
+  meter_t a_m(1.0);
+  foot_t b_ft(3.28084);
+  unit_t<inverse<meter>> i_m(2.0);
+
+  // resultant unit is square of leftmost unit
+  auto c_m2 = a_m * b_ft;
+  EXPECT_NEAR(1.0, c_m2(), 5.0e-5);
+
+  auto c_ft2 = b_ft * a_m;
+  EXPECT_NEAR(10.7639111056, c_ft2(), 5.0e-7);
+
+  // you can get whatever (compatible) type you want if you ask explicitly
+  square_meter_t d_m2 = b_ft * a_m;
+  EXPECT_NEAR(1.0, d_m2(), 5.0e-5);
+
+  // a unit times a sclar ends up with the same units.
+  meter_t e_m = a_m * scalar_t(3.0);
+  EXPECT_NEAR(3.0, e_m(), 5.0e-5);
+
+  e_m = scalar_t(4.0) * a_m;
+  EXPECT_NEAR(4.0, e_m(), 5.0e-5);
+
+  // unit times its inverse results in a scalar
+  scalar_t s = a_m * i_m;
+  EXPECT_NEAR(2.0, s, 5.0e-5);
+
+  c_m2 = b_ft * meter_t(2);
+  EXPECT_NEAR(2.0, c_m2(), 5.0e-5);
+
+  auto e_ft2 = b_ft * meter_t(3);
+  EXPECT_NEAR(32.2917333168, e_ft2(), 5.0e-6);
+
+  auto mps = meter_t(10.0) * unit_t<inverse<seconds>>(1.0);
+  EXPECT_EQ(mps, meters_per_second_t(10));
+}
+
+TEST_F(UnitContainer, unitTypeScalarMultiplication) {
+  meter_t a_m(1.0);
+
+  auto result_m = scalar_t(3.0) * a_m;
+  EXPECT_NEAR(3.0, result_m(), 5.0e-5);
+
+  result_m = a_m * scalar_t(4.0);
+  EXPECT_NEAR(4.0, result_m(), 5.0e-5);
+
+  result_m = 3.0 * a_m;
+  EXPECT_NEAR(3.0, result_m(), 5.0e-5);
+
+  result_m = a_m * 4.0;
+  EXPECT_NEAR(4.0, result_m(), 5.0e-5);
+
+  bool isSame = std::is_same<decltype(result_m), meter_t>::value;
+  EXPECT_TRUE(isSame);
+}
+
+TEST_F(UnitContainer, unitTypeDivision) {
+  meter_t a_m(1.0), b_m(2.0);
+  foot_t a_ft(3.28084);
+  second_t a_sec(10.0);
+  bool isSame;
+
+  auto c = a_m / a_ft;
+  EXPECT_NEAR(1.0, c, 5.0e-5);
+  isSame = std::is_same<decltype(c), scalar_t>::value;
+  EXPECT_TRUE(isSame);
+
+  c = a_m / b_m;
+  EXPECT_NEAR(0.5, c, 5.0e-5);
+  isSame = std::is_same<decltype(c), scalar_t>::value;
+  EXPECT_TRUE(isSame);
+
+  c = a_ft / a_m;
+  EXPECT_NEAR(1.0, c, 5.0e-5);
+  isSame = std::is_same<decltype(c), scalar_t>::value;
+  EXPECT_TRUE(isSame);
+
+  c = scalar_t(1.0) / 2.0;
+  EXPECT_NEAR(0.5, c, 5.0e-5);
+  isSame = std::is_same<decltype(c), scalar_t>::value;
+  EXPECT_TRUE(isSame);
+
+  c = 1.0 / scalar_t(2.0);
+  EXPECT_NEAR(0.5, c, 5.0e-5);
+  isSame = std::is_same<decltype(c), scalar_t>::value;
+  EXPECT_TRUE(isSame);
+
+  double d = scalar_t(1.0) / 2.0;
+  EXPECT_NEAR(0.5, d, 5.0e-5);
+
+  auto e = a_m / a_sec;
+  EXPECT_NEAR(0.1, e(), 5.0e-5);
+  isSame = std::is_same<decltype(e), meters_per_second_t>::value;
+  EXPECT_TRUE(isSame);
+
+  auto f = a_m / 8.0;
+  EXPECT_NEAR(0.125, f(), 5.0e-5);
+  isSame = std::is_same<decltype(f), meter_t>::value;
+  EXPECT_TRUE(isSame);
+
+  auto g = 4.0 / b_m;
+  EXPECT_NEAR(2.0, g(), 5.0e-5);
+  isSame = std::is_same<decltype(g), unit_t<inverse<meters>>>::value;
+  EXPECT_TRUE(isSame);
+
+  auto mph = mile_t(60.0) / hour_t(1.0);
+  meters_per_second_t mps = mph;
+  EXPECT_NEAR(26.8224, mps(), 5.0e-5);
+}
+
+TEST_F(UnitContainer, compoundAssignmentAddition) {
+  // units
+  meter_t a(0.0);
+  a += meter_t(1.0);
+
+  EXPECT_EQ(meter_t(1.0), a);
+
+  a += foot_t(meter_t(1));
+
+  EXPECT_EQ(meter_t(2.0), a);
+
+  // scalars
+  scalar_t b(0);
+  b += scalar_t(1.0);
+
+  EXPECT_EQ(scalar_t(1.0), b);
+
+  b += 1;
+
+  EXPECT_EQ(scalar_t(2.0), b);
+}
+
+TEST_F(UnitContainer, compoundAssignmentSubtraction) {
+  // units
+  meter_t a(2.0);
+  a -= meter_t(1.0);
+
+  EXPECT_EQ(meter_t(1.0), a);
+
+  a -= foot_t(meter_t(1));
+
+  EXPECT_EQ(meter_t(0.0), a);
+
+  // scalars
+  scalar_t b(2);
+  b -= scalar_t(1.0);
+
+  EXPECT_EQ(scalar_t(1.0), b);
+
+  b -= 1;
+
+  EXPECT_EQ(scalar_t(0), b);
+}
+
+TEST_F(UnitContainer, compoundAssignmentMultiplication) {
+  // units
+  meter_t a(2.0);
+  a *= scalar_t(2.0);
+
+  EXPECT_EQ(meter_t(4.0), a);
+
+  a *= 2.0;
+
+  EXPECT_EQ(meter_t(8.0), a);
+
+  // scalars
+  scalar_t b(2);
+  b *= scalar_t(2.0);
+
+  EXPECT_EQ(scalar_t(4.0), b);
+
+  b *= 2;
+
+  EXPECT_EQ(scalar_t(8.0), b);
+}
+
+TEST_F(UnitContainer, compoundAssignmentDivision) {
+  // units
+  meter_t a(8.0);
+  a /= scalar_t(2.0);
+
+  EXPECT_EQ(meter_t(4.0), a);
+
+  a /= 2.0;
+
+  EXPECT_EQ(meter_t(2.0), a);
+
+  // scalars
+  scalar_t b(8);
+  b /= scalar_t(2.0);
+
+  EXPECT_EQ(scalar_t(4.0), b);
+
+  b /= 2;
+
+  EXPECT_EQ(scalar_t(2.0), b);
+}
+
+TEST_F(UnitContainer, scalarTypeImplicitConversion) {
+  double test = scalar_t(3.0);
+  EXPECT_DOUBLE_EQ(3.0, test);
+
+  scalar_t testS = 3.0;
+  EXPECT_DOUBLE_EQ(3.0, testS);
+
+  scalar_t test3(ppm_t(10));
+  EXPECT_DOUBLE_EQ(0.00001, test3);
+
+  scalar_t test4;
+  test4 = ppm_t(1);
+  EXPECT_DOUBLE_EQ(0.000001, test4);
+}
+
+TEST_F(UnitContainer, valueMethod) {
+  double test = meter_t(3.0).to<double>();
+  EXPECT_DOUBLE_EQ(3.0, test);
+
+  auto test2 = meter_t(4.0).value();
+  EXPECT_DOUBLE_EQ(4.0, test2);
+  EXPECT_TRUE((std::is_same<decltype(test2), double>::value));
+}
+
+TEST_F(UnitContainer, convertMethod) {
+  double test = meter_t(3.0).convert<feet>().to<double>();
+  EXPECT_NEAR(9.84252, test, 5.0e-6);
+}
+
+#ifndef UNIT_LIB_DISABLE_IOSTREAM
+TEST_F(UnitContainer, cout) {
+  testing::internal::CaptureStdout();
+  std::cout << degree_t(349.87);
+  std::string output = testing::internal::GetCapturedStdout();
+  EXPECT_STREQ("349.87 deg", output.c_str());
+
+  testing::internal::CaptureStdout();
+  std::cout << meter_t(1.0);
+  output = testing::internal::GetCapturedStdout();
+  EXPECT_STREQ("1 m", output.c_str());
+
+  testing::internal::CaptureStdout();
+  std::cout << dB_t(31.0);
+  output = testing::internal::GetCapturedStdout();
+  EXPECT_STREQ("31 dB", output.c_str());
+
+  testing::internal::CaptureStdout();
+  std::cout << volt_t(21.79);
+  output = testing::internal::GetCapturedStdout();
+  EXPECT_STREQ("21.79 V", output.c_str());
+
+  testing::internal::CaptureStdout();
+  std::cout << dBW_t(12.0);
+  output = testing::internal::GetCapturedStdout();
+  EXPECT_STREQ("12 dBW", output.c_str());
+
+  testing::internal::CaptureStdout();
+  std::cout << dBm_t(120.0);
+  output = testing::internal::GetCapturedStdout();
+  EXPECT_STREQ("120 dBm", output.c_str());
+
+  testing::internal::CaptureStdout();
+  std::cout << miles_per_hour_t(72.1);
+  output = testing::internal::GetCapturedStdout();
+  EXPECT_STREQ("72.1 mph", output.c_str());
+
+  // undefined unit
+  testing::internal::CaptureStdout();
+  std::cout << units::math::cpow<4>(meter_t(2));
+  output = testing::internal::GetCapturedStdout();
+  EXPECT_STREQ("16 m^4", output.c_str());
+
+  testing::internal::CaptureStdout();
+  std::cout << units::math::cpow<3>(foot_t(2));
+  output = testing::internal::GetCapturedStdout();
+  EXPECT_STREQ("8 cu_ft", output.c_str());
+
+  testing::internal::CaptureStdout();
+  std::cout << std::setprecision(9) << units::math::cpow<4>(foot_t(2));
+  output = testing::internal::GetCapturedStdout();
+  EXPECT_STREQ("0.138095597 m^4", output.c_str());
+
+  // constants
+  testing::internal::CaptureStdout();
+  std::cout << std::setprecision(8) << constants::k_B;
+  output = testing::internal::GetCapturedStdout();
+#if defined(_MSC_VER) && (_MSC_VER <= 1800)
+  EXPECT_STREQ("1.3806485e-023 m^2 kg s^-2 K^-1", output.c_str());
+#else
+  EXPECT_STREQ("1.3806485e-23 m^2 kg s^-2 K^-1", output.c_str());
+#endif
+
+  testing::internal::CaptureStdout();
+  std::cout << std::setprecision(9) << constants::mu_B;
+  output = testing::internal::GetCapturedStdout();
+#if defined(_MSC_VER) && (_MSC_VER <= 1800)
+  EXPECT_STREQ("9.27400999e-024 m^2 A", output.c_str());
+#else
+  EXPECT_STREQ("9.27400999e-24 m^2 A", output.c_str());
+#endif
+
+  testing::internal::CaptureStdout();
+  std::cout << std::setprecision(7) << constants::sigma;
+  output = testing::internal::GetCapturedStdout();
+#if defined(_MSC_VER) && (_MSC_VER <= 1800)
+  EXPECT_STREQ("5.670367e-008 kg s^-3 K^-4", output.c_str());
+#else
+  EXPECT_STREQ("5.670367e-08 kg s^-3 K^-4", output.c_str());
+#endif
+}
+
+TEST_F(UnitContainer, to_string) {
+  foot_t a(3.5);
+  EXPECT_STREQ("3.5 ft", units::length::to_string(a).c_str());
+
+  meter_t b(8);
+  EXPECT_STREQ("8 m", units::length::to_string(b).c_str());
+}
+
+TEST_F(UnitContainer, DISABLED_to_string_locale) {
+  struct lconv* lc;
+
+  // German locale
+#if defined(_MSC_VER)
+  setlocale(LC_ALL, "de-DE");
+#else
+  EXPECT_STREQ("de_DE.utf8", setlocale(LC_ALL, "de_DE.utf8"));
+#endif
+
+  lc = localeconv();
+  char point_de = *lc->decimal_point;
+  EXPECT_EQ(point_de, ',');
+
+  kilometer_t de = 2_km;
+  EXPECT_STREQ("2 km", units::length::to_string(de).c_str());
+
+  de = 2.5_km;
+  EXPECT_STREQ("2,5 km", units::length::to_string(de).c_str());
+
+  // US locale
+#if defined(_MSC_VER)
+  setlocale(LC_ALL, "en-US");
+#else
+  EXPECT_STREQ("en_US.utf8", setlocale(LC_ALL, "en_US.utf8"));
+#endif
+
+  lc = localeconv();
+  char point_us = *lc->decimal_point;
+  EXPECT_EQ(point_us, '.');
+
+  mile_t us = 2_mi;
+  EXPECT_STREQ("2 mi", units::length::to_string(us).c_str());
+
+  us = 2.5_mi;
+  EXPECT_STREQ("2.5 mi", units::length::to_string(us).c_str());
+}
+
+TEST_F(UnitContainer, nameAndAbbreviation) {
+  foot_t a(3.5);
+  EXPECT_STREQ("ft", units::abbreviation(a));
+  EXPECT_STREQ("ft", a.abbreviation());
+  EXPECT_STREQ("foot", a.name());
+
+  meter_t b(8);
+  EXPECT_STREQ("m", units::abbreviation(b));
+  EXPECT_STREQ("m", b.abbreviation());
+  EXPECT_STREQ("meter", b.name());
+}
+#endif
+
+TEST_F(UnitContainer, negative) {
+  meter_t a(5.3);
+  meter_t b(-5.3);
+  EXPECT_NEAR(a.to<double>(), -b.to<double>(), 5.0e-320);
+  EXPECT_NEAR(b.to<double>(), -a.to<double>(), 5.0e-320);
+
+  dB_t c(2.87);
+  dB_t d(-2.87);
+  EXPECT_NEAR(c.to<double>(), -d.to<double>(), 5.0e-320);
+  EXPECT_NEAR(d.to<double>(), -c.to<double>(), 5.0e-320);
+
+  ppm_t e = -1 * ppm_t(10);
+  EXPECT_EQ(e, -ppm_t(10));
+  EXPECT_NEAR(-0.00001, e, 5.0e-10);
+}
+
+TEST_F(UnitContainer, concentration) {
+  ppb_t a(ppm_t(1));
+  EXPECT_EQ(ppb_t(1000), a);
+  EXPECT_EQ(0.000001, a);
+  EXPECT_EQ(0.000001, a.to<double>());
+
+  scalar_t b(ppm_t(1));
+  EXPECT_EQ(0.000001, b);
+
+  scalar_t c = ppb_t(1);
+  EXPECT_EQ(0.000000001, c);
+}
+
+TEST_F(UnitContainer, dBConversion) {
+  dBW_t a_dbw(23.1);
+  watt_t a_w = a_dbw;
+  dBm_t a_dbm = a_dbw;
+
+  EXPECT_NEAR(204.173794, a_w(), 5.0e-7);
+  EXPECT_NEAR(53.1, a_dbm(), 5.0e-7);
+
+  milliwatt_t b_mw(100000.0);
+  watt_t b_w = b_mw;
+  dBm_t b_dbm = b_mw;
+  dBW_t b_dbw = b_mw;
+
+  EXPECT_NEAR(100.0, b_w(), 5.0e-7);
+  EXPECT_NEAR(50.0, b_dbm(), 5.0e-7);
+  EXPECT_NEAR(20.0, b_dbw(), 5.0e-7);
+}
+
+TEST_F(UnitContainer, dBAddition) {
+  bool isSame;
+
+  auto result_dbw = dBW_t(10.0) + dB_t(30.0);
+  EXPECT_NEAR(40.0, result_dbw(), 5.0e-5);
+  result_dbw = dB_t(12.0) + dBW_t(30.0);
+  EXPECT_NEAR(42.0, result_dbw(), 5.0e-5);
+  isSame = std::is_same<decltype(result_dbw), dBW_t>::value;
+  EXPECT_TRUE(isSame);
+
+  auto result_dbm = dB_t(30.0) + dBm_t(20.0);
+  EXPECT_NEAR(50.0, result_dbm(), 5.0e-5);
+
+  // adding dBW to dBW is something you probably shouldn't do, but let's see if
+  // it works...
+  auto result_dBW2 = dBW_t(10.0) + dBm_t(40.0);
+  EXPECT_NEAR(20.0, result_dBW2(), 5.0e-5);
+  isSame = std::is_same<decltype(result_dBW2),
+                        unit_t<squared<watts>, double, decibel_scale>>::value;
+  EXPECT_TRUE(isSame);
+}
+
+TEST_F(UnitContainer, dBSubtraction) {
+  bool isSame;
+
+  auto result_dbw = dBW_t(10.0) - dB_t(30.0);
+  EXPECT_NEAR(-20.0, result_dbw(), 5.0e-5);
+  isSame = std::is_same<decltype(result_dbw), dBW_t>::value;
+  EXPECT_TRUE(isSame);
+
+  auto result_dbm = dBm_t(100.0) - dB_t(30.0);
+  EXPECT_NEAR(70.0, result_dbm(), 5.0e-5);
+  isSame = std::is_same<decltype(result_dbm), dBm_t>::value;
+  EXPECT_TRUE(isSame);
+
+  auto result_db = dBW_t(100.0) - dBW_t(80.0);
+  EXPECT_NEAR(20.0, result_db(), 5.0e-5);
+  isSame = std::is_same<decltype(result_db), dB_t>::value;
+  EXPECT_TRUE(isSame);
+
+  result_db = dB_t(100.0) - dB_t(80.0);
+  EXPECT_NEAR(20.0, result_db(), 5.0e-5);
+  isSame = std::is_same<decltype(result_db), dB_t>::value;
+  EXPECT_TRUE(isSame);
+}
+
+TEST_F(UnitContainer, unit_cast) {
+  meter_t test1(5.7);
+  hectare_t test2(16);
+
+  double dResult1 = 5.7;
+
+  double dResult2 = 16;
+  int iResult2 = 16;
+
+  EXPECT_EQ(dResult1, unit_cast<double>(test1));
+  EXPECT_EQ(dResult2, unit_cast<double>(test2));
+  EXPECT_EQ(iResult2, unit_cast<int>(test2));
+
+  EXPECT_TRUE(
+      (std::is_same<double, decltype(unit_cast<double>(test1))>::value));
+  EXPECT_TRUE((std::is_same<int, decltype(unit_cast<int>(test2))>::value));
+}
+
+// literal syntax is only supported in GCC 4.7+ and MSVC2015+
+#if !defined(_MSC_VER) || _MSC_VER > 1800
+TEST_F(UnitContainer, literals) {
+  // basic functionality testing
+  EXPECT_TRUE((std::is_same<decltype(16.2_m), meter_t>::value));
+  EXPECT_TRUE(meter_t(16.2) == 16.2_m);
+  EXPECT_TRUE(meter_t(16) == 16_m);
+
+  EXPECT_TRUE((std::is_same<decltype(11.2_ft), foot_t>::value));
+  EXPECT_TRUE(foot_t(11.2) == 11.2_ft);
+  EXPECT_TRUE(foot_t(11) == 11_ft);
+
+  // auto using literal syntax
+  auto x = 10.0_m;
+  EXPECT_TRUE((std::is_same<decltype(x), meter_t>::value));
+  EXPECT_TRUE(meter_t(10) == x);
+
+  // conversion using literal syntax
+  foot_t y = 0.3048_m;
+  EXPECT_TRUE(1_ft == y);
+
+  // Pythagorean theorem
+  meter_t a = 3_m;
+  meter_t b = 4_m;
+  meter_t c = sqrt(pow<2>(a) + pow<2>(b));
+  EXPECT_TRUE(c == 5_m);
+}
+#endif
+
+TEST_F(UnitConversion, length) {
+  double test;
+  test = convert<meters, nanometers>(0.000000001);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<meters, micrometers>(0.000001);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<meters, millimeters>(0.001);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<meters, centimeters>(0.01);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<meters, kilometers>(1000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<meters, meters>(1.0);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<meters, feet>(0.3048);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<meters, miles>(1609.344);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<meters, inches>(0.0254);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<meters, nauticalMiles>(1852.0);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<meters, astronicalUnits>(149597870700.0);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<meters, lightyears>(9460730472580800.0);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<meters, parsec>(3.08567758e16);
+  EXPECT_NEAR(1.0, test, 5.0e7);
+
+  test = convert<feet, feet>(6.3);
+  EXPECT_NEAR(6.3, test, 5.0e-5);
+  test = convert<feet, inches>(6.0);
+  EXPECT_NEAR(72.0, test, 5.0e-5);
+  test = convert<inches, feet>(6.0);
+  EXPECT_NEAR(0.5, test, 5.0e-5);
+  test = convert<meter, feet>(1.0);
+  EXPECT_NEAR(3.28084, test, 5.0e-5);
+  test = convert<miles, nauticalMiles>(6.3);
+  EXPECT_NEAR(5.47455, test, 5.0e-6);
+  test = convert<miles, meters>(11.0);
+  EXPECT_NEAR(17702.8, test, 5.0e-2);
+  test = convert<meters, chains>(1.0);
+  EXPECT_NEAR(0.0497097, test, 5.0e-7);
+}
+
+TEST_F(UnitConversion, mass) {
+  double test;
+
+  test = convert<kilograms, grams>(1.0e-3);
+  EXPECT_NEAR(1.0, test, 5.0e-6);
+  test = convert<kilograms, micrograms>(1.0e-9);
+  EXPECT_NEAR(1.0, test, 5.0e-6);
+  test = convert<kilograms, milligrams>(1.0e-6);
+  EXPECT_NEAR(1.0, test, 5.0e-6);
+  test = convert<kilograms, kilograms>(1.0);
+  EXPECT_NEAR(1.0, test, 5.0e-6);
+  test = convert<kilograms, metric_tons>(1000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-6);
+  test = convert<kilograms, pounds>(0.453592);
+  EXPECT_NEAR(1.0, test, 5.0e-6);
+  test = convert<kilograms, long_tons>(1016.05);
+  EXPECT_NEAR(1.0, test, 5.0e-6);
+  test = convert<kilograms, short_tons>(907.185);
+  EXPECT_NEAR(1.0, test, 5.0e-6);
+  test = convert<kilograms, mass::ounces>(0.0283495);
+  EXPECT_NEAR(1.0, test, 5.0e-6);
+  test = convert<kilograms, carats>(0.0002);
+  EXPECT_NEAR(1.0, test, 5.0e-6);
+  test = convert<slugs, kilograms>(1.0);
+  EXPECT_NEAR(14.593903, test, 5.0e-7);
+
+  test = convert<pounds, carats>(6.3);
+  EXPECT_NEAR(14288.2, test, 5.0e-2);
+}
+
+TEST_F(UnitConversion, time) {
+  double result = 0;
+  double daysPerYear = 365;
+  double hoursPerDay = 24;
+  double minsPerHour = 60;
+  double secsPerMin = 60;
+  double daysPerWeek = 7;
+
+  result = 2 * daysPerYear * hoursPerDay * minsPerHour * secsPerMin *
+           (1 / minsPerHour) * (1 / secsPerMin) * (1 / hoursPerDay) *
+           (1 / daysPerWeek);
+  EXPECT_NEAR(104.286, result, 5.0e-4);
+
+  year_t twoYears(2.0);
+  week_t twoYearsInWeeks = twoYears;
+  EXPECT_NEAR(week_t(104.286).to<double>(), twoYearsInWeeks.to<double>(),
+              5.0e-4);
+
+  double test;
+
+  test = convert<seconds, seconds>(1.0);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<seconds, nanoseconds>(1.0e-9);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<seconds, microseconds>(1.0e-6);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<seconds, milliseconds>(1.0e-3);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<seconds, minutes>(60.0);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<seconds, hours>(3600.0);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<seconds, days>(86400.0);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<seconds, weeks>(604800.0);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<seconds, years>(3.154e7);
+  EXPECT_NEAR(1.0, test, 5.0e3);
+
+  test = convert<years, weeks>(2.0);
+  EXPECT_NEAR(104.2857142857143, test, 5.0e-14);
+  test = convert<hours, minutes>(4.0);
+  EXPECT_NEAR(240.0, test, 5.0e-14);
+  test = convert<julian_years, days>(1.0);
+  EXPECT_NEAR(365.25, test, 5.0e-14);
+  test = convert<gregorian_years, days>(1.0);
+  EXPECT_NEAR(365.2425, test, 5.0e-14);
+}
+
+TEST_F(UnitConversion, angle) {
+  angle::degree_t quarterCircleDeg(90.0);
+  angle::radian_t quarterCircleRad = quarterCircleDeg;
+  EXPECT_NEAR(angle::radian_t(constants::detail::PI_VAL / 2.0).to<double>(),
+              quarterCircleRad.to<double>(), 5.0e-12);
+
+  double test;
+
+  test = convert<angle::radians, angle::radians>(1.0);
+  EXPECT_NEAR(1.0, test, 5.0e-20);
+  test = convert<angle::radians, angle::milliradians>(0.001);
+  EXPECT_NEAR(1.0, test, 5.0e-4);
+  test = convert<angle::radians, angle::degrees>(0.0174533);
+  EXPECT_NEAR(1.0, test, 5.0e-7);
+  test = convert<angle::radians, angle::arcminutes>(0.000290888);
+  EXPECT_NEAR(0.99999928265913, test, 5.0e-8);
+  test = convert<angle::radians, angle::arcseconds>(4.8481e-6);
+  EXPECT_NEAR(0.999992407, test, 5.0e-10);
+  test = convert<angle::radians, angle::turns>(6.28319);
+  EXPECT_NEAR(1.0, test, 5.0e-6);
+  test = convert<angle::radians, angle::gradians>(0.015708);
+  EXPECT_NEAR(1.0, test, 5.0e-6);
+
+  test = convert<angle::radians, angle::radians>(2.1);
+  EXPECT_NEAR(2.1, test, 5.0e-6);
+  test = convert<angle::arcseconds, angle::gradians>(2.1);
+  EXPECT_NEAR(0.000648148, test, 5.0e-6);
+  test = convert<angle::radians, angle::degrees>(constants::detail::PI_VAL);
+  EXPECT_NEAR(180.0, test, 5.0e-6);
+  test = convert<angle::degrees, angle::radians>(90.0);
+  EXPECT_NEAR(constants::detail::PI_VAL / 2, test, 5.0e-6);
+}
+
+TEST_F(UnitConversion, current) {
+  double test;
+
+  test = convert<current::A, current::mA>(2.1);
+  EXPECT_NEAR(2100.0, test, 5.0e-6);
+}
+
+TEST_F(UnitConversion, temperature) {
+  // temp conversion are weird/hard since they involve translations AND scaling.
+  double test;
+
+  test = convert<kelvin, kelvin>(72.0);
+  EXPECT_NEAR(72.0, test, 5.0e-5);
+  test = convert<fahrenheit, fahrenheit>(72.0);
+  EXPECT_NEAR(72.0, test, 5.0e-5);
+  test = convert<kelvin, fahrenheit>(300.0);
+  EXPECT_NEAR(80.33, test, 5.0e-5);
+  test = convert<fahrenheit, kelvin>(451.0);
+  EXPECT_NEAR(505.928, test, 5.0e-4);
+  test = convert<kelvin, celsius>(300.0);
+  EXPECT_NEAR(26.85, test, 5.0e-3);
+  test = convert<celsius, kelvin>(451.0);
+  EXPECT_NEAR(724.15, test, 5.0e-3);
+  test = convert<fahrenheit, celsius>(72.0);
+  EXPECT_NEAR(22.2222, test, 5.0e-5);
+  test = convert<celsius, fahrenheit>(100.0);
+  EXPECT_NEAR(212.0, test, 5.0e-5);
+  test = convert<fahrenheit, celsius>(32.0);
+  EXPECT_NEAR(0.0, test, 5.0e-5);
+  test = convert<celsius, fahrenheit>(0.0);
+  EXPECT_NEAR(32.0, test, 5.0e-5);
+  test = convert<rankine, kelvin>(100.0);
+  EXPECT_NEAR(55.5556, test, 5.0e-5);
+  test = convert<kelvin, rankine>(100.0);
+  EXPECT_NEAR(180.0, test, 5.0e-5);
+  test = convert<fahrenheit, rankine>(100.0);
+  EXPECT_NEAR(559.67, test, 5.0e-5);
+  test = convert<rankine, fahrenheit>(72.0);
+  EXPECT_NEAR(-387.67, test, 5.0e-5);
+  test = convert<reaumur, kelvin>(100.0);
+  EXPECT_NEAR(398.0, test, 5.0e-1);
+  test = convert<reaumur, celsius>(80.0);
+  EXPECT_NEAR(100.0, test, 5.0e-5);
+  test = convert<celsius, reaumur>(212.0);
+  EXPECT_NEAR(169.6, test, 5.0e-2);
+  test = convert<reaumur, fahrenheit>(80.0);
+  EXPECT_NEAR(212.0, test, 5.0e-5);
+  test = convert<fahrenheit, reaumur>(37.0);
+  EXPECT_NEAR(2.222, test, 5.0e-3);
+}
+
+TEST_F(UnitConversion, luminous_intensity) {
+  double test;
+
+  test = convert<candela, millicandela>(72.0);
+  EXPECT_NEAR(72000.0, test, 5.0e-5);
+  test = convert<millicandela, candela>(376.0);
+  EXPECT_NEAR(0.376, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, solid_angle) {
+  double test;
+  bool same;
+
+  same = std::is_same<traits::base_unit_of<steradians>,
+                      traits::base_unit_of<degrees_squared>>::value;
+  EXPECT_TRUE(same);
+
+  test = convert<steradians, steradians>(72.0);
+  EXPECT_NEAR(72.0, test, 5.0e-5);
+  test = convert<steradians, degrees_squared>(1.0);
+  EXPECT_NEAR(3282.8, test, 5.0e-2);
+  test = convert<steradians, spats>(8.0);
+  EXPECT_NEAR(0.636619772367582, test, 5.0e-14);
+  test = convert<degrees_squared, steradians>(3282.8);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<degrees_squared, degrees_squared>(72.0);
+  EXPECT_NEAR(72.0, test, 5.0e-5);
+  test = convert<degrees_squared, spats>(3282.8);
+  EXPECT_NEAR(1.0 / (4 * constants::detail::PI_VAL), test, 5.0e-5);
+  test = convert<spats, steradians>(1.0 / (4 * constants::detail::PI_VAL));
+  EXPECT_NEAR(1.0, test, 5.0e-14);
+  test = convert<spats, degrees_squared>(1.0 / (4 * constants::detail::PI_VAL));
+  EXPECT_NEAR(3282.8, test, 5.0e-2);
+  test = convert<spats, spats>(72.0);
+  EXPECT_NEAR(72.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, frequency) {
+  double test;
+
+  test = convert<hertz, kilohertz>(63000.0);
+  EXPECT_NEAR(63.0, test, 5.0e-5);
+  test = convert<hertz, hertz>(6.3);
+  EXPECT_NEAR(6.3, test, 5.0e-5);
+  test = convert<kilohertz, hertz>(5.0);
+  EXPECT_NEAR(5000.0, test, 5.0e-5);
+  test = convert<megahertz, hertz>(1.0);
+  EXPECT_NEAR(1.0e6, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, velocity) {
+  double test;
+  bool same;
+
+  same = std::is_same<meters_per_second,
+                      unit<std::ratio<1>, category::velocity_unit>>::value;
+  EXPECT_TRUE(same);
+  same = traits::is_convertible_unit<miles_per_hour, meters_per_second>::value;
+  EXPECT_TRUE(same);
+
+  test = convert<meters_per_second, miles_per_hour>(1250.0);
+  EXPECT_NEAR(2796.17, test, 5.0e-3);
+  test = convert<feet_per_second, kilometers_per_hour>(2796.17);
+  EXPECT_NEAR(3068.181418, test, 5.0e-7);
+  test = convert<knots, miles_per_hour>(600.0);
+  EXPECT_NEAR(690.468, test, 5.0e-4);
+  test = convert<miles_per_hour, feet_per_second>(120.0);
+  EXPECT_NEAR(176.0, test, 5.0e-5);
+  test = convert<feet_per_second, meters_per_second>(10.0);
+  EXPECT_NEAR(3.048, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, angular_velocity) {
+  double test;
+  bool same;
+
+  same =
+      std::is_same<radians_per_second,
+                   unit<std::ratio<1>, category::angular_velocity_unit>>::value;
+  EXPECT_TRUE(same);
+  same = traits::is_convertible_unit<rpm, radians_per_second>::value;
+  EXPECT_TRUE(same);
+
+  test = convert<radians_per_second, milliarcseconds_per_year>(1.0);
+  EXPECT_NEAR(6.504e15, test, 1.0e12);
+  test = convert<degrees_per_second, radians_per_second>(1.0);
+  EXPECT_NEAR(0.0174533, test, 5.0e-8);
+  test = convert<rpm, radians_per_second>(1.0);
+  EXPECT_NEAR(0.10471975512, test, 5.0e-13);
+  test = convert<milliarcseconds_per_year, radians_per_second>(1.0);
+  EXPECT_NEAR(1.537e-16, test, 5.0e-20);
+}
+
+TEST_F(UnitConversion, acceleration) {
+  double test;
+
+  test = convert<standard_gravity, meters_per_second_squared>(1.0);
+  EXPECT_NEAR(9.80665, test, 5.0e-10);
+}
+TEST_F(UnitConversion, force) {
+  double test;
+
+  test = convert<units::force::newton, units::force::newton>(1.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<units::force::newton, units::force::pounds>(6.3);
+  EXPECT_NEAR(1.4163, test, 5.0e-5);
+  test = convert<units::force::newton, units::force::dynes>(5.0);
+  EXPECT_NEAR(500000.0, test, 5.0e-5);
+  test = convert<units::force::newtons, units::force::poundals>(2.1);
+  EXPECT_NEAR(15.1893, test, 5.0e-5);
+  test = convert<units::force::newtons, units::force::kiloponds>(173.0);
+  EXPECT_NEAR(17.6411, test, 5.0e-5);
+  test = convert<units::force::poundals, units::force::kiloponds>(21.879);
+  EXPECT_NEAR(0.308451933, test, 5.0e-10);
+}
+
+TEST_F(UnitConversion, area) {
+  double test;
+
+  test = convert<hectares, acres>(6.3);
+  EXPECT_NEAR(15.5676, test, 5.0e-5);
+  test = convert<square_miles, square_kilometers>(10.0);
+  EXPECT_NEAR(25.8999, test, 5.0e-5);
+  test = convert<square_inch, square_meter>(4.0);
+  EXPECT_NEAR(0.00258064, test, 5.0e-9);
+  test = convert<acre, square_foot>(5.0);
+  EXPECT_NEAR(217800.0, test, 5.0e-5);
+  test = convert<square_meter, square_foot>(1.0);
+  EXPECT_NEAR(10.7639, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, pressure) {
+  double test;
+
+  test = convert<pascals, torr>(1.0);
+  EXPECT_NEAR(0.00750062, test, 5.0e-5);
+  test = convert<bar, psi>(2.2);
+  EXPECT_NEAR(31.9083, test, 5.0e-5);
+  test = convert<atmospheres, bar>(4.0);
+  EXPECT_NEAR(4.053, test, 5.0e-5);
+  test = convert<torr, pascals>(800.0);
+  EXPECT_NEAR(106657.89474, test, 5.0e-5);
+  test = convert<psi, atmospheres>(38.0);
+  EXPECT_NEAR(2.58575, test, 5.0e-5);
+  test = convert<psi, pascals>(1.0);
+  EXPECT_NEAR(6894.76, test, 5.0e-3);
+  test = convert<pascals, bar>(0.25);
+  EXPECT_NEAR(2.5e-6, test, 5.0e-5);
+  test = convert<torr, atmospheres>(9.0);
+  EXPECT_NEAR(0.0118421, test, 5.0e-8);
+  test = convert<bar, torr>(12.0);
+  EXPECT_NEAR(9000.74, test, 5.0e-3);
+  test = convert<atmospheres, psi>(1.0);
+  EXPECT_NEAR(14.6959, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, charge) {
+  double test;
+
+  test = convert<coulombs, ampere_hours>(4.0);
+  EXPECT_NEAR(0.00111111, test, 5.0e-9);
+  test = convert<ampere_hours, coulombs>(1.0);
+  EXPECT_NEAR(3600.0, test, 5.0e-6);
+}
+
+TEST_F(UnitConversion, energy) {
+  double test;
+
+  test = convert<joules, calories>(8000.000464);
+  EXPECT_NEAR(1912.046, test, 5.0e-4);
+  test = convert<therms, joules>(12.0);
+  EXPECT_NEAR(1.266e+9, test, 5.0e5);
+  test = convert<megajoules, watt_hours>(100.0);
+  EXPECT_NEAR(27777.778, test, 5.0e-4);
+  test = convert<kilocalories, megajoules>(56.0);
+  EXPECT_NEAR(0.234304, test, 5.0e-7);
+  test = convert<kilojoules, therms>(56.0);
+  EXPECT_NEAR(0.000530904, test, 5.0e-5);
+  test = convert<british_thermal_units, kilojoules>(18.56399995447);
+  EXPECT_NEAR(19.5860568, test, 5.0e-5);
+  test = convert<calories, energy::foot_pounds>(18.56399995447);
+  EXPECT_NEAR(57.28776190423856, test, 5.0e-5);
+  test = convert<megajoules, calories>(1.0);
+  EXPECT_NEAR(239006.0, test, 5.0e-1);
+  test = convert<kilocalories, kilowatt_hours>(2.0);
+  EXPECT_NEAR(0.00232444, test, 5.0e-9);
+  test = convert<therms, kilocalories>(0.1);
+  EXPECT_NEAR(2521.04, test, 5.0e-3);
+  test = convert<watt_hours, megajoules>(67.0);
+  EXPECT_NEAR(0.2412, test, 5.0e-5);
+  test = convert<british_thermal_units, watt_hours>(100.0);
+  EXPECT_NEAR(29.3071, test, 5.0e-5);
+  test = convert<calories, BTU>(100.0);
+  EXPECT_NEAR(0.396567, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, power) {
+  double test;
+
+  test = convert<compound_unit<energy::foot_pounds, inverse<seconds>>, watts>(
+      550.0);
+  EXPECT_NEAR(745.7, test, 5.0e-2);
+  test = convert<watts, gigawatts>(1000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-4);
+  test = convert<microwatts, watts>(200000.0);
+  EXPECT_NEAR(0.2, test, 5.0e-4);
+  test = convert<horsepower, watts>(100.0);
+  EXPECT_NEAR(74570.0, test, 5.0e-1);
+  test = convert<horsepower, megawatts>(5.0);
+  EXPECT_NEAR(0.0037284994, test, 5.0e-7);
+  test = convert<kilowatts, horsepower>(232.0);
+  EXPECT_NEAR(311.117, test, 5.0e-4);
+  test = convert<milliwatts, horsepower>(1001.0);
+  EXPECT_NEAR(0.001342363, test, 5.0e-9);
+}
+
+TEST_F(UnitConversion, voltage) {
+  double test;
+
+  test = convert<volts, millivolts>(10.0);
+  EXPECT_NEAR(10000.0, test, 5.0e-5);
+  test = convert<picovolts, volts>(1000000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<nanovolts, volts>(1000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<microvolts, volts>(1000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<millivolts, volts>(1000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<kilovolts, volts>(0.001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<megavolts, volts>(0.000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<gigavolts, volts>(0.000000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<statvolts, volts>(299.792458);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<millivolts, statvolts>(1000.0);
+  EXPECT_NEAR(299.792458, test, 5.0e-5);
+  test = convert<abvolts, nanovolts>(0.1);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<microvolts, abvolts>(0.01);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, capacitance) {
+  double test;
+
+  test = convert<farads, millifarads>(10.0);
+  EXPECT_NEAR(10000.0, test, 5.0e-5);
+  test = convert<picofarads, farads>(1000000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<nanofarads, farads>(1000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<microfarads, farads>(1000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<millifarads, farads>(1000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<kilofarads, farads>(0.001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<megafarads, farads>(0.000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<gigafarads, farads>(0.000000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, impedance) {
+  double test;
+
+  test = convert<ohms, milliohms>(10.0);
+  EXPECT_NEAR(10000.0, test, 5.0e-5);
+  test = convert<picoohms, ohms>(1000000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<nanoohms, ohms>(1000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<microohms, ohms>(1000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<milliohms, ohms>(1000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<kiloohms, ohms>(0.001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<megaohms, ohms>(0.000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<gigaohms, ohms>(0.000000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, conductance) {
+  double test;
+
+  test = convert<siemens, millisiemens>(10.0);
+  EXPECT_NEAR(10000.0, test, 5.0e-5);
+  test = convert<picosiemens, siemens>(1000000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<nanosiemens, siemens>(1000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<microsiemens, siemens>(1000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<millisiemens, siemens>(1000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<kilosiemens, siemens>(0.001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<megasiemens, siemens>(0.000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<gigasiemens, siemens>(0.000000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, magnetic_flux) {
+  double test;
+
+  test = convert<webers, milliwebers>(10.0);
+  EXPECT_NEAR(10000.0, test, 5.0e-5);
+  test = convert<picowebers, webers>(1000000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<nanowebers, webers>(1000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<microwebers, webers>(1000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<milliwebers, webers>(1000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<kilowebers, webers>(0.001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<megawebers, webers>(0.000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<gigawebers, webers>(0.000000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<maxwells, webers>(100000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<nanowebers, maxwells>(10.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, magnetic_field_strength) {
+  double test;
+
+  test = convert<teslas, milliteslas>(10.0);
+  EXPECT_NEAR(10000.0, test, 5.0e-5);
+  test = convert<picoteslas, teslas>(1000000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<nanoteslas, teslas>(1000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<microteslas, teslas>(1000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<milliteslas, teslas>(1000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<kiloteslas, teslas>(0.001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<megateslas, teslas>(0.000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<gigateslas, teslas>(0.000000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<gauss, teslas>(10000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<nanoteslas, gauss>(100000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, inductance) {
+  double test;
+
+  test = convert<henries, millihenries>(10.0);
+  EXPECT_NEAR(10000.0, test, 5.0e-5);
+  test = convert<picohenries, henries>(1000000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<nanohenries, henries>(1000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<microhenries, henries>(1000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<millihenries, henries>(1000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<kilohenries, henries>(0.001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<megahenries, henries>(0.000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<gigahenries, henries>(0.000000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, luminous_flux) {
+  double test;
+
+  test = convert<lumens, millilumens>(10.0);
+  EXPECT_NEAR(10000.0, test, 5.0e-5);
+  test = convert<picolumens, lumens>(1000000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<nanolumens, lumens>(1000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<microlumens, lumens>(1000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<millilumens, lumens>(1000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<kilolumens, lumens>(0.001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<megalumens, lumens>(0.000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<gigalumens, lumens>(0.000000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, illuminance) {
+  double test;
+
+  test = convert<luxes, milliluxes>(10.0);
+  EXPECT_NEAR(10000.0, test, 5.0e-5);
+  test = convert<picoluxes, luxes>(1000000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<nanoluxes, luxes>(1000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<microluxes, luxes>(1000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<milliluxes, luxes>(1000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<kiloluxes, luxes>(0.001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<megaluxes, luxes>(0.000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<gigaluxes, luxes>(0.000000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+
+  test = convert<footcandles, luxes>(0.092903);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<lux, lumens_per_square_inch>(1550.0031000062);
+  EXPECT_NEAR(1.0, test, 5.0e-13);
+  test = convert<phots, luxes>(0.0001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, radiation) {
+  double test;
+
+  test = convert<becquerels, millibecquerels>(10.0);
+  EXPECT_NEAR(10000.0, test, 5.0e-5);
+  test = convert<picobecquerels, becquerels>(1000000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<nanobecquerels, becquerels>(1000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<microbecquerels, becquerels>(1000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<millibecquerels, becquerels>(1000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<kilobecquerels, becquerels>(0.001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<megabecquerels, becquerels>(0.000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<gigabecquerels, becquerels>(0.000000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+
+  test = convert<grays, milligrays>(10.0);
+  EXPECT_NEAR(10000.0, test, 5.0e-5);
+  test = convert<picograys, grays>(1000000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<nanograys, grays>(1000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<micrograys, grays>(1000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<milligrays, grays>(1000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<kilograys, grays>(0.001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<megagrays, grays>(0.000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<gigagrays, grays>(0.000000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+
+  test = convert<sieverts, millisieverts>(10.0);
+  EXPECT_NEAR(10000.0, test, 5.0e-5);
+  test = convert<picosieverts, sieverts>(1000000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<nanosieverts, sieverts>(1000000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<microsieverts, sieverts>(1000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<millisieverts, sieverts>(1000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<kilosieverts, sieverts>(0.001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<megasieverts, sieverts>(0.000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<gigasieverts, sieverts>(0.000000001);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+
+  test = convert<becquerels, curies>(37.0e9);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<becquerels, rutherfords>(1000000.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<rads, grays>(100.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, torque) {
+  double test;
+
+  test = convert<torque::foot_pounds, newton_meter>(1.0);
+  EXPECT_NEAR(1.355817948, test, 5.0e-5);
+  test = convert<inch_pounds, newton_meter>(1.0);
+  EXPECT_NEAR(0.112984829, test, 5.0e-5);
+  test = convert<foot_poundals, newton_meter>(1.0);
+  EXPECT_NEAR(4.214011009e-2, test, 5.0e-5);
+  test = convert<meter_kilograms, newton_meter>(1.0);
+  EXPECT_NEAR(9.80665, test, 5.0e-5);
+  test = convert<inch_pound, meter_kilogram>(86.79616930855788);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<foot_poundals, inch_pound>(2.681170713);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, volume) {
+  double test;
+
+  test = convert<cubic_meters, cubic_meter>(1.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<cubic_millimeters, cubic_meter>(1.0);
+  EXPECT_NEAR(1.0e-9, test, 5.0e-5);
+  test = convert<cubic_kilometers, cubic_meter>(1.0);
+  EXPECT_NEAR(1.0e9, test, 5.0e-5);
+  test = convert<liters, cubic_meter>(1.0);
+  EXPECT_NEAR(0.001, test, 5.0e-5);
+  test = convert<milliliters, cubic_meter>(1.0);
+  EXPECT_NEAR(1.0e-6, test, 5.0e-5);
+  test = convert<cubic_inches, cubic_meter>(1.0);
+  EXPECT_NEAR(1.6387e-5, test, 5.0e-10);
+  test = convert<cubic_feet, cubic_meter>(1.0);
+  EXPECT_NEAR(0.0283168, test, 5.0e-8);
+  test = convert<cubic_yards, cubic_meter>(1.0);
+  EXPECT_NEAR(0.764555, test, 5.0e-7);
+  test = convert<cubic_miles, cubic_meter>(1.0);
+  EXPECT_NEAR(4.168e+9, test, 5.0e5);
+  test = convert<gallons, cubic_meter>(1.0);
+  EXPECT_NEAR(0.00378541, test, 5.0e-8);
+  test = convert<quarts, cubic_meter>(1.0);
+  EXPECT_NEAR(0.000946353, test, 5.0e-10);
+  test = convert<pints, cubic_meter>(1.0);
+  EXPECT_NEAR(0.000473176, test, 5.0e-10);
+  test = convert<cups, cubic_meter>(1.0);
+  EXPECT_NEAR(0.00024, test, 5.0e-6);
+  test = convert<volume::fluid_ounces, cubic_meter>(1.0);
+  EXPECT_NEAR(2.9574e-5, test, 5.0e-5);
+  test = convert<barrels, cubic_meter>(1.0);
+  EXPECT_NEAR(0.158987294928, test, 5.0e-13);
+  test = convert<bushels, cubic_meter>(1.0);
+  EXPECT_NEAR(0.0352391, test, 5.0e-8);
+  test = convert<cords, cubic_meter>(1.0);
+  EXPECT_NEAR(3.62456, test, 5.0e-6);
+  test = convert<cubic_fathoms, cubic_meter>(1.0);
+  EXPECT_NEAR(6.11644, test, 5.0e-6);
+  test = convert<tablespoons, cubic_meter>(1.0);
+  EXPECT_NEAR(1.4787e-5, test, 5.0e-10);
+  test = convert<teaspoons, cubic_meter>(1.0);
+  EXPECT_NEAR(4.9289e-6, test, 5.0e-11);
+  test = convert<pinches, cubic_meter>(1.0);
+  EXPECT_NEAR(616.11519921875e-9, test, 5.0e-20);
+  test = convert<dashes, cubic_meter>(1.0);
+  EXPECT_NEAR(308.057599609375e-9, test, 5.0e-20);
+  test = convert<drops, cubic_meter>(1.0);
+  EXPECT_NEAR(82.14869322916e-9, test, 5.0e-9);
+  test = convert<fifths, cubic_meter>(1.0);
+  EXPECT_NEAR(0.00075708236, test, 5.0e-12);
+  test = convert<drams, cubic_meter>(1.0);
+  EXPECT_NEAR(3.69669e-6, test, 5.0e-12);
+  test = convert<gills, cubic_meter>(1.0);
+  EXPECT_NEAR(0.000118294, test, 5.0e-10);
+  test = convert<pecks, cubic_meter>(1.0);
+  EXPECT_NEAR(0.00880977, test, 5.0e-9);
+  test = convert<sacks, cubic_meter>(9.4591978);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<shots, cubic_meter>(1.0);
+  EXPECT_NEAR(4.43603e-5, test, 5.0e-11);
+  test = convert<strikes, cubic_meter>(1.0);
+  EXPECT_NEAR(0.07047814033376, test, 5.0e-5);
+  test = convert<volume::fluid_ounces, milliliters>(1.0);
+  EXPECT_NEAR(29.5735, test, 5.0e-5);
+}
+
+TEST_F(UnitConversion, density) {
+  double test;
+
+  test = convert<kilograms_per_cubic_meter, kilograms_per_cubic_meter>(1.0);
+  EXPECT_NEAR(1.0, test, 5.0e-5);
+  test = convert<grams_per_milliliter, kilograms_per_cubic_meter>(1.0);
+  EXPECT_NEAR(1000.0, test, 5.0e-5);
+  test = convert<kilograms_per_liter, kilograms_per_cubic_meter>(1.0);
+  EXPECT_NEAR(1000.0, test, 5.0e-5);
+  test = convert<ounces_per_cubic_foot, kilograms_per_cubic_meter>(1.0);
+  EXPECT_NEAR(1.001153961, test, 5.0e-10);
+  test = convert<ounces_per_cubic_inch, kilograms_per_cubic_meter>(1.0);
+  EXPECT_NEAR(1.729994044e3, test, 5.0e-7);
+  test = convert<ounces_per_gallon, kilograms_per_cubic_meter>(1.0);
+  EXPECT_NEAR(7.489151707, test, 5.0e-10);
+  test = convert<pounds_per_cubic_foot, kilograms_per_cubic_meter>(1.0);
+  EXPECT_NEAR(16.01846337, test, 5.0e-9);
+  test = convert<pounds_per_cubic_inch, kilograms_per_cubic_meter>(1.0);
+  EXPECT_NEAR(2.767990471e4, test, 5.0e-6);
+  test = convert<pounds_per_gallon, kilograms_per_cubic_meter>(1.0);
+  EXPECT_NEAR(119.8264273, test, 5.0e-8);
+  test = convert<slugs_per_cubic_foot, kilograms_per_cubic_meter>(1.0);
+  EXPECT_NEAR(515.3788184, test, 5.0e-6);
+}
+
+TEST_F(UnitConversion, concentration) {
+  double test;
+
+  test = ppm_t(1.0);
+  EXPECT_NEAR(1.0e-6, test, 5.0e-12);
+  test = ppb_t(1.0);
+  EXPECT_NEAR(1.0e-9, test, 5.0e-12);
+  test = ppt_t(1.0);
+  EXPECT_NEAR(1.0e-12, test, 5.0e-12);
+  test = percent_t(18.0);
+  EXPECT_NEAR(0.18, test, 5.0e-12);
+}
+
+TEST_F(UnitConversion, data) {
+  EXPECT_EQ(8, (convert<byte, bit>(1)));
+
+  EXPECT_EQ(1000, (convert<kilobytes, bytes>(1)));
+  EXPECT_EQ(1000, (convert<megabytes, kilobytes>(1)));
+  EXPECT_EQ(1000, (convert<gigabytes, megabytes>(1)));
+  EXPECT_EQ(1000, (convert<terabytes, gigabytes>(1)));
+  EXPECT_EQ(1000, (convert<petabytes, terabytes>(1)));
+  EXPECT_EQ(1000, (convert<exabytes, petabytes>(1)));
+
+  EXPECT_EQ(1024, (convert<kibibytes, bytes>(1)));
+  EXPECT_EQ(1024, (convert<mebibytes, kibibytes>(1)));
+  EXPECT_EQ(1024, (convert<gibibytes, mebibytes>(1)));
+  EXPECT_EQ(1024, (convert<tebibytes, gibibytes>(1)));
+  EXPECT_EQ(1024, (convert<pebibytes, tebibytes>(1)));
+  EXPECT_EQ(1024, (convert<exbibytes, pebibytes>(1)));
+
+  EXPECT_EQ(93750000, (convert<gigabytes, kibibits>(12)));
+
+  EXPECT_EQ(1000, (convert<kilobits, bits>(1)));
+  EXPECT_EQ(1000, (convert<megabits, kilobits>(1)));
+  EXPECT_EQ(1000, (convert<gigabits, megabits>(1)));
+  EXPECT_EQ(1000, (convert<terabits, gigabits>(1)));
+  EXPECT_EQ(1000, (convert<petabits, terabits>(1)));
+  EXPECT_EQ(1000, (convert<exabits, petabits>(1)));
+
+  EXPECT_EQ(1024, (convert<kibibits, bits>(1)));
+  EXPECT_EQ(1024, (convert<mebibits, kibibits>(1)));
+  EXPECT_EQ(1024, (convert<gibibits, mebibits>(1)));
+  EXPECT_EQ(1024, (convert<tebibits, gibibits>(1)));
+  EXPECT_EQ(1024, (convert<pebibits, tebibits>(1)));
+  EXPECT_EQ(1024, (convert<exbibits, pebibits>(1)));
+
+  // Source: https://en.wikipedia.org/wiki/Binary_prefix
+  EXPECT_NEAR(percent_t(2.4), kibibyte_t(1) / kilobyte_t(1) - 1, 0.005);
+  EXPECT_NEAR(percent_t(4.9), mebibyte_t(1) / megabyte_t(1) - 1, 0.005);
+  EXPECT_NEAR(percent_t(7.4), gibibyte_t(1) / gigabyte_t(1) - 1, 0.005);
+  EXPECT_NEAR(percent_t(10.0), tebibyte_t(1) / terabyte_t(1) - 1, 0.005);
+  EXPECT_NEAR(percent_t(12.6), pebibyte_t(1) / petabyte_t(1) - 1, 0.005);
+  EXPECT_NEAR(percent_t(15.3), exbibyte_t(1) / exabyte_t(1) - 1, 0.005);
+}
+
+TEST_F(UnitConversion, data_transfer_rate) {
+  EXPECT_EQ(8, (convert<bytes_per_second, bits_per_second>(1)));
+
+  EXPECT_EQ(1000, (convert<kilobytes_per_second, bytes_per_second>(1)));
+  EXPECT_EQ(1000, (convert<megabytes_per_second, kilobytes_per_second>(1)));
+  EXPECT_EQ(1000, (convert<gigabytes_per_second, megabytes_per_second>(1)));
+  EXPECT_EQ(1000, (convert<terabytes_per_second, gigabytes_per_second>(1)));
+  EXPECT_EQ(1000, (convert<petabytes_per_second, terabytes_per_second>(1)));
+  EXPECT_EQ(1000, (convert<exabytes_per_second, petabytes_per_second>(1)));
+
+  EXPECT_EQ(1024, (convert<kibibytes_per_second, bytes_per_second>(1)));
+  EXPECT_EQ(1024, (convert<mebibytes_per_second, kibibytes_per_second>(1)));
+  EXPECT_EQ(1024, (convert<gibibytes_per_second, mebibytes_per_second>(1)));
+  EXPECT_EQ(1024, (convert<tebibytes_per_second, gibibytes_per_second>(1)));
+  EXPECT_EQ(1024, (convert<pebibytes_per_second, tebibytes_per_second>(1)));
+  EXPECT_EQ(1024, (convert<exbibytes_per_second, pebibytes_per_second>(1)));
+
+  EXPECT_EQ(93750000, (convert<gigabytes_per_second, kibibits_per_second>(12)));
+
+  EXPECT_EQ(1000, (convert<kilobits_per_second, bits_per_second>(1)));
+  EXPECT_EQ(1000, (convert<megabits_per_second, kilobits_per_second>(1)));
+  EXPECT_EQ(1000, (convert<gigabits_per_second, megabits_per_second>(1)));
+  EXPECT_EQ(1000, (convert<terabits_per_second, gigabits_per_second>(1)));
+  EXPECT_EQ(1000, (convert<petabits_per_second, terabits_per_second>(1)));
+  EXPECT_EQ(1000, (convert<exabits_per_second, petabits_per_second>(1)));
+
+  EXPECT_EQ(1024, (convert<kibibits_per_second, bits_per_second>(1)));
+  EXPECT_EQ(1024, (convert<mebibits_per_second, kibibits_per_second>(1)));
+  EXPECT_EQ(1024, (convert<gibibits_per_second, mebibits_per_second>(1)));
+  EXPECT_EQ(1024, (convert<tebibits_per_second, gibibits_per_second>(1)));
+  EXPECT_EQ(1024, (convert<pebibits_per_second, tebibits_per_second>(1)));
+  EXPECT_EQ(1024, (convert<exbibits_per_second, pebibits_per_second>(1)));
+
+  // Source: https://en.wikipedia.org/wiki/Binary_prefix
+  EXPECT_NEAR(percent_t(2.4),
+              kibibytes_per_second_t(1) / kilobytes_per_second_t(1) - 1, 0.005);
+  EXPECT_NEAR(percent_t(4.9),
+              mebibytes_per_second_t(1) / megabytes_per_second_t(1) - 1, 0.005);
+  EXPECT_NEAR(percent_t(7.4),
+              gibibytes_per_second_t(1) / gigabytes_per_second_t(1) - 1, 0.005);
+  EXPECT_NEAR(percent_t(10.0),
+              tebibytes_per_second_t(1) / terabytes_per_second_t(1) - 1, 0.005);
+  EXPECT_NEAR(percent_t(12.6),
+              pebibytes_per_second_t(1) / petabytes_per_second_t(1) - 1, 0.005);
+  EXPECT_NEAR(percent_t(15.3),
+              exbibytes_per_second_t(1) / exabytes_per_second_t(1) - 1, 0.005);
+}
+
+TEST_F(UnitConversion, pi) {
+  EXPECT_TRUE(
+      units::traits::is_dimensionless_unit<decltype(constants::pi)>::value);
+  EXPECT_TRUE(units::traits::is_dimensionless_unit<constants::PI>::value);
+
+  // implicit conversion/arithmetic
+  EXPECT_NEAR(3.14159, constants::pi, 5.0e-6);
+  EXPECT_NEAR(6.28318531, (2 * constants::pi), 5.0e-9);
+  EXPECT_NEAR(6.28318531, (constants::pi + constants::pi), 5.0e-9);
+  EXPECT_NEAR(0.0, (constants::pi - constants::pi), 5.0e-9);
+  EXPECT_NEAR(31.00627668, units::math::cpow<3>(constants::pi), 5.0e-10);
+  EXPECT_NEAR(0.0322515344, (1.0 / units::math::cpow<3>(constants::pi)),
+              5.0e-11);
+  EXPECT_TRUE(constants::detail::PI_VAL == constants::pi);
+  EXPECT_TRUE(1.0 != constants::pi);
+  EXPECT_TRUE(4.0 > constants::pi);
+  EXPECT_TRUE(3.0 < constants::pi);
+  EXPECT_TRUE(constants::pi > 3.0);
+  EXPECT_TRUE(constants::pi < 4.0);
+
+  // explicit conversion
+  EXPECT_NEAR(3.14159, constants::pi.to<double>(), 5.0e-6);
+
+  // auto multiplication
+  EXPECT_TRUE(
+      (std::is_same<meter_t, decltype(constants::pi * meter_t(1))>::value));
+  EXPECT_TRUE(
+      (std::is_same<meter_t, decltype(meter_t(1) * constants::pi)>::value));
+
+  EXPECT_NEAR(constants::detail::PI_VAL,
+              (constants::pi * meter_t(1)).to<double>(), 5.0e-10);
+  EXPECT_NEAR(constants::detail::PI_VAL,
+              (meter_t(1) * constants::pi).to<double>(), 5.0e-10);
+
+  // explicit multiplication
+  meter_t a = constants::pi * meter_t(1);
+  meter_t b = meter_t(1) * constants::pi;
+
+  EXPECT_NEAR(constants::detail::PI_VAL, a.to<double>(), 5.0e-10);
+  EXPECT_NEAR(constants::detail::PI_VAL, b.to<double>(), 5.0e-10);
+
+  // auto division
+  EXPECT_TRUE(
+      (std::is_same<hertz_t, decltype(constants::pi / second_t(1))>::value));
+  EXPECT_TRUE(
+      (std::is_same<second_t, decltype(second_t(1) / constants::pi)>::value));
+
+  EXPECT_NEAR(constants::detail::PI_VAL,
+              (constants::pi / second_t(1)).to<double>(), 5.0e-10);
+  EXPECT_NEAR(1.0 / constants::detail::PI_VAL,
+              (second_t(1) / constants::pi).to<double>(), 5.0e-10);
+
+  // explicit
+  hertz_t c = constants::pi / second_t(1);
+  second_t d = second_t(1) / constants::pi;
+
+  EXPECT_NEAR(constants::detail::PI_VAL, c.to<double>(), 5.0e-10);
+  EXPECT_NEAR(1.0 / constants::detail::PI_VAL, d.to<double>(), 5.0e-10);
+}
+
+TEST_F(UnitConversion, constants) {
+  // Source: NIST "2014 CODATA recommended values"
+  EXPECT_NEAR(299792458, constants::c(), 5.0e-9);
+  EXPECT_NEAR(6.67408e-11, constants::G(), 5.0e-17);
+  EXPECT_NEAR(6.626070040e-34, constants::h(), 5.0e-44);
+  EXPECT_NEAR(1.2566370614e-6, constants::mu0(), 5.0e-17);
+  EXPECT_NEAR(8.854187817e-12, constants::epsilon0(), 5.0e-21);
+  EXPECT_NEAR(376.73031346177, constants::Z0(), 5.0e-12);
+  EXPECT_NEAR(8987551787.3681764, constants::k_e(), 5.0e-6);
+  EXPECT_NEAR(1.6021766208e-19, constants::e(), 5.0e-29);
+  EXPECT_NEAR(9.10938356e-31, constants::m_e(), 5.0e-40);
+  EXPECT_NEAR(1.672621898e-27, constants::m_p(), 5.0e-37);
+  EXPECT_NEAR(9.274009994e-24, constants::mu_B(), 5.0e-32);
+  EXPECT_NEAR(6.022140857e23, constants::N_A(), 5.0e14);
+  EXPECT_NEAR(8.3144598, constants::R(), 5.0e-8);
+  EXPECT_NEAR(1.38064852e-23, constants::k_B(), 5.0e-31);
+  EXPECT_NEAR(96485.33289, constants::F(), 5.0e-5);
+  EXPECT_NEAR(5.670367e-8, constants::sigma(), 5.0e-14);
+}
+
+TEST_F(UnitConversion, std_chrono) {
+  nanosecond_t a = std::chrono::nanoseconds(10);
+  EXPECT_EQ(nanosecond_t(10), a);
+  microsecond_t b = std::chrono::microseconds(10);
+  EXPECT_EQ(microsecond_t(10), b);
+  millisecond_t c = std::chrono::milliseconds(10);
+  EXPECT_EQ(millisecond_t(10), c);
+  second_t d = std::chrono::seconds(1);
+  EXPECT_EQ(second_t(1), d);
+  minute_t e = std::chrono::minutes(120);
+  EXPECT_EQ(minute_t(120), e);
+  hour_t f = std::chrono::hours(2);
+  EXPECT_EQ(hour_t(2), f);
+
+  std::chrono::nanoseconds g = nanosecond_t(100);
+  EXPECT_EQ(std::chrono::duration_cast<std::chrono::nanoseconds>(g).count(),
+            100);
+  std::chrono::nanoseconds h = microsecond_t(2);
+  EXPECT_EQ(std::chrono::duration_cast<std::chrono::nanoseconds>(h).count(),
+            2000);
+  std::chrono::nanoseconds i = millisecond_t(1);
+  EXPECT_EQ(std::chrono::duration_cast<std::chrono::nanoseconds>(i).count(),
+            1000000);
+  std::chrono::nanoseconds j = second_t(1);
+  EXPECT_EQ(std::chrono::duration_cast<std::chrono::nanoseconds>(j).count(),
+            1000000000);
+  std::chrono::nanoseconds k = minute_t(1);
+  EXPECT_EQ(std::chrono::duration_cast<std::chrono::nanoseconds>(k).count(),
+            60000000000);
+  std::chrono::nanoseconds l = hour_t(1);
+  EXPECT_EQ(std::chrono::duration_cast<std::chrono::nanoseconds>(l).count(),
+            3600000000000);
+}
+
+TEST_F(UnitConversion, squaredTemperature) {
+  using squared_celsius = units::compound_unit<squared<celsius>>;
+  using squared_celsius_t = units::unit_t<squared_celsius>;
+  const squared_celsius_t right(100);
+  const celsius_t rootRight = units::math::sqrt(right);
+  EXPECT_EQ(celsius_t(10), rootRight);
+}
+
+TEST_F(UnitMath, min) {
+  meter_t a(1);
+  foot_t c(1);
+  EXPECT_EQ(c, math::min(a, c));
+}
+
+TEST_F(UnitMath, max) {
+  meter_t a(1);
+  foot_t c(1);
+  EXPECT_EQ(a, math::max(a, c));
+}
+
+TEST_F(UnitMath, cos) {
+  EXPECT_TRUE((std::is_same<typename std::decay<scalar_t>::type,
+                            typename std::decay<decltype(
+                                cos(angle::radian_t(0)))>::type>::value));
+  EXPECT_NEAR(scalar_t(-0.41614683654), cos(angle::radian_t(2)), 5.0e-11);
+  EXPECT_NEAR(scalar_t(-0.70710678118), cos(angle::degree_t(135)),
+              5.0e-11);
+}
+
+TEST_F(UnitMath, sin) {
+  EXPECT_TRUE((std::is_same<typename std::decay<scalar_t>::type,
+                            typename std::decay<decltype(
+                                sin(angle::radian_t(0)))>::type>::value));
+  EXPECT_NEAR(scalar_t(0.90929742682), sin(angle::radian_t(2)), 5.0e-11);
+  EXPECT_NEAR(scalar_t(0.70710678118), sin(angle::degree_t(135)), 5.0e-11);
+}
+
+TEST_F(UnitMath, tan) {
+  EXPECT_TRUE((std::is_same<typename std::decay<scalar_t>::type,
+                            typename std::decay<decltype(
+                                tan(angle::radian_t(0)))>::type>::value));
+  EXPECT_NEAR(scalar_t(-2.18503986326), tan(angle::radian_t(2)), 5.0e-11);
+  EXPECT_NEAR(scalar_t(-1.0), tan(angle::degree_t(135)), 5.0e-11);
+}
+
+TEST_F(UnitMath, acos) {
+  EXPECT_TRUE(
+      (std::is_same<
+          typename std::decay<angle::radian_t>::type,
+          typename std::decay<decltype(acos(scalar_t(0)))>::type>::value));
+  EXPECT_NEAR(angle::radian_t(2).to<double>(),
+              acos(scalar_t(-0.41614683654)).to<double>(), 5.0e-11);
+  EXPECT_NEAR(
+      angle::degree_t(135).to<double>(),
+      angle::degree_t(acos(scalar_t(-0.70710678118654752440084436210485)))
+          .to<double>(),
+      5.0e-12);
+}
+
+TEST_F(UnitMath, asin) {
+  EXPECT_TRUE(
+      (std::is_same<
+          typename std::decay<angle::radian_t>::type,
+          typename std::decay<decltype(asin(scalar_t(0)))>::type>::value));
+  EXPECT_NEAR(angle::radian_t(1.14159265).to<double>(),
+              asin(scalar_t(0.90929742682)).to<double>(), 5.0e-9);
+  EXPECT_NEAR(
+      angle::degree_t(45).to<double>(),
+      angle::degree_t(asin(scalar_t(0.70710678118654752440084436210485)))
+          .to<double>(),
+      5.0e-12);
+}
+
+TEST_F(UnitMath, atan) {
+  EXPECT_TRUE(
+      (std::is_same<
+          typename std::decay<angle::radian_t>::type,
+          typename std::decay<decltype(atan(scalar_t(0)))>::type>::value));
+  EXPECT_NEAR(angle::radian_t(-1.14159265).to<double>(),
+              atan(scalar_t(-2.18503986326)).to<double>(), 5.0e-9);
+  EXPECT_NEAR(angle::degree_t(-45).to<double>(),
+              angle::degree_t(atan(scalar_t(-1.0))).to<double>(), 5.0e-12);
+}
+
+TEST_F(UnitMath, atan2) {
+  EXPECT_TRUE((std::is_same<typename std::decay<angle::radian_t>::type,
+                            typename std::decay<decltype(atan2(
+                                scalar_t(1), scalar_t(1)))>::type>::value));
+  EXPECT_NEAR(angle::radian_t(constants::detail::PI_VAL / 4).to<double>(),
+              atan2(scalar_t(2), scalar_t(2)).to<double>(), 5.0e-12);
+  EXPECT_NEAR(
+      angle::degree_t(45).to<double>(),
+      angle::degree_t(atan2(scalar_t(2), scalar_t(2))).to<double>(),
+      5.0e-12);
+
+  EXPECT_TRUE((std::is_same<typename std::decay<angle::radian_t>::type,
+                            typename std::decay<decltype(atan2(
+                                scalar_t(1), scalar_t(1)))>::type>::value));
+  EXPECT_NEAR(angle::radian_t(constants::detail::PI_VAL / 6).to<double>(),
+              atan2(scalar_t(1), scalar_t(std::sqrt(3))).to<double>(),
+              5.0e-12);
+  EXPECT_NEAR(angle::degree_t(30).to<double>(),
+              angle::degree_t(atan2(scalar_t(1), scalar_t(std::sqrt(3))))
+                  .to<double>(),
+              5.0e-12);
+}
+
+TEST_F(UnitMath, cosh) {
+  EXPECT_TRUE((std::is_same<typename std::decay<scalar_t>::type,
+                            typename std::decay<decltype(
+                                cosh(angle::radian_t(0)))>::type>::value));
+  EXPECT_NEAR(scalar_t(3.76219569108), cosh(angle::radian_t(2)), 5.0e-11);
+  EXPECT_NEAR(scalar_t(5.32275215), cosh(angle::degree_t(135)), 5.0e-9);
+}
+
+TEST_F(UnitMath, sinh) {
+  EXPECT_TRUE((std::is_same<typename std::decay<scalar_t>::type,
+                            typename std::decay<decltype(
+                                sinh(angle::radian_t(0)))>::type>::value));
+  EXPECT_NEAR(scalar_t(3.62686040785), sinh(angle::radian_t(2)), 5.0e-11);
+  EXPECT_NEAR(scalar_t(5.22797192), sinh(angle::degree_t(135)), 5.0e-9);
+}
+
+TEST_F(UnitMath, tanh) {
+  EXPECT_TRUE((std::is_same<typename std::decay<scalar_t>::type,
+                            typename std::decay<decltype(
+                                tanh(angle::radian_t(0)))>::type>::value));
+  EXPECT_NEAR(scalar_t(0.96402758007), tanh(angle::radian_t(2)), 5.0e-11);
+  EXPECT_NEAR(scalar_t(0.98219338), tanh(angle::degree_t(135)), 5.0e-11);
+}
+
+TEST_F(UnitMath, acosh) {
+  EXPECT_TRUE((std::is_same<typename std::decay<angle::radian_t>::type,
+                            typename std::decay<decltype(
+                                acosh(scalar_t(0)))>::type>::value));
+  EXPECT_NEAR(angle::radian_t(1.316957896924817).to<double>(),
+              acosh(scalar_t(2.0)).to<double>(), 5.0e-11);
+  EXPECT_NEAR(angle::degree_t(75.456129290216893).to<double>(),
+              angle::degree_t(acosh(scalar_t(2.0))).to<double>(), 5.0e-12);
+}
+
+TEST_F(UnitMath, asinh) {
+  EXPECT_TRUE((std::is_same<typename std::decay<angle::radian_t>::type,
+                            typename std::decay<decltype(
+                                asinh(scalar_t(0)))>::type>::value));
+  EXPECT_NEAR(angle::radian_t(1.443635475178810).to<double>(),
+              asinh(scalar_t(2)).to<double>(), 5.0e-9);
+  EXPECT_NEAR(angle::degree_t(82.714219883108939).to<double>(),
+              angle::degree_t(asinh(scalar_t(2))).to<double>(), 5.0e-12);
+}
+
+TEST_F(UnitMath, atanh) {
+  EXPECT_TRUE((std::is_same<typename std::decay<angle::radian_t>::type,
+                            typename std::decay<decltype(
+                                atanh(scalar_t(0)))>::type>::value));
+  EXPECT_NEAR(angle::radian_t(0.549306144334055).to<double>(),
+              atanh(scalar_t(0.5)).to<double>(), 5.0e-9);
+  EXPECT_NEAR(angle::degree_t(31.472923730945389).to<double>(),
+              angle::degree_t(atanh(scalar_t(0.5))).to<double>(), 5.0e-12);
+}
+
+TEST_F(UnitMath, exp) {
+  double val = 10.0;
+  EXPECT_EQ(std::exp(val), exp(scalar_t(val)));
+}
+
+TEST_F(UnitMath, log) {
+  double val = 100.0;
+  EXPECT_EQ(std::log(val), log(scalar_t(val)));
+}
+
+TEST_F(UnitMath, log10) {
+  double val = 100.0;
+  EXPECT_EQ(std::log10(val), log10(scalar_t(val)));
+}
+
+TEST_F(UnitMath, modf) {
+  double val = 100.0;
+  double modfr1;
+  scalar_t modfr2;
+  EXPECT_EQ(std::modf(val, &modfr1), modf(scalar_t(val), &modfr2));
+  EXPECT_EQ(modfr1, modfr2);
+}
+
+TEST_F(UnitMath, exp2) {
+  double val = 10.0;
+  EXPECT_EQ(std::exp2(val), exp2(scalar_t(val)));
+}
+
+TEST_F(UnitMath, expm1) {
+  double val = 10.0;
+  EXPECT_EQ(std::expm1(val), expm1(scalar_t(val)));
+}
+
+TEST_F(UnitMath, log1p) {
+  double val = 10.0;
+  EXPECT_EQ(std::log1p(val), log1p(scalar_t(val)));
+}
+
+TEST_F(UnitMath, log2) {
+  double val = 10.0;
+  EXPECT_EQ(std::log2(val), log2(scalar_t(val)));
+}
+
+TEST_F(UnitMath, pow) {
+  bool isSame;
+  meter_t value(10.0);
+
+  auto sq = pow<2>(value);
+  EXPECT_NEAR(100.0, sq(), 5.0e-2);
+  isSame = std::is_same<decltype(sq), square_meter_t>::value;
+  EXPECT_TRUE(isSame);
+
+  auto cube = pow<3>(value);
+  EXPECT_NEAR(1000.0, cube(), 5.0e-2);
+  isSame = std::is_same<decltype(cube), unit_t<cubed<meter>>>::value;
+  EXPECT_TRUE(isSame);
+
+  auto fourth = pow<4>(value);
+  EXPECT_NEAR(10000.0, fourth(), 5.0e-2);
+  isSame = std::is_same<
+      decltype(fourth),
+      unit_t<compound_unit<squared<meter>, squared<meter>>>>::value;
+  EXPECT_TRUE(isSame);
+}
+
+TEST_F(UnitMath, sqrt) {
+  EXPECT_TRUE((std::is_same<typename std::decay<meter_t>::type,
+                            typename std::decay<decltype(sqrt(
+                                square_meter_t(4.0)))>::type>::value));
+  EXPECT_NEAR(meter_t(2.0).to<double>(),
+              sqrt(square_meter_t(4.0)).to<double>(), 5.0e-9);
+
+  EXPECT_TRUE((std::is_same<typename std::decay<angle::radian_t>::type,
+                            typename std::decay<decltype(
+                                sqrt(steradian_t(16.0)))>::type>::value));
+  EXPECT_NEAR(angle::radian_t(4.0).to<double>(),
+              sqrt(steradian_t(16.0)).to<double>(), 5.0e-9);
+
+  EXPECT_TRUE((std::is_convertible<typename std::decay<foot_t>::type,
+                                   typename std::decay<decltype(sqrt(
+                                       square_foot_t(10.0)))>::type>::value));
+
+  // for rational conversion (i.e. no integral root) let's check a bunch of
+  // different ways this could go wrong
+  foot_t resultFt = sqrt(square_foot_t(10.0));
+  EXPECT_NEAR(foot_t(3.16227766017).to<double>(),
+              sqrt(square_foot_t(10.0)).to<double>(), 5.0e-9);
+  EXPECT_NEAR(foot_t(3.16227766017).to<double>(), resultFt.to<double>(),
+              5.0e-9);
+  EXPECT_EQ(resultFt, sqrt(square_foot_t(10.0)));
+}
+
+TEST_F(UnitMath, hypot) {
+  EXPECT_TRUE((std::is_same<typename std::decay<meter_t>::type,
+                            typename std::decay<decltype(hypot(
+                                meter_t(3.0), meter_t(4.0)))>::type>::value));
+  EXPECT_NEAR(meter_t(5.0).to<double>(),
+              (hypot(meter_t(3.0), meter_t(4.0))).to<double>(), 5.0e-9);
+
+  EXPECT_TRUE((std::is_same<typename std::decay<foot_t>::type,
+                            typename std::decay<decltype(hypot(
+                                foot_t(3.0), meter_t(1.2192)))>::type>::value));
+  EXPECT_NEAR(foot_t(5.0).to<double>(),
+              (hypot(foot_t(3.0), meter_t(1.2192))).to<double>(), 5.0e-9);
+}
+
+TEST_F(UnitMath, ceil) {
+  double val = 101.1;
+  EXPECT_EQ(std::ceil(val), ceil(meter_t(val)).to<double>());
+  EXPECT_TRUE((std::is_same<typename std::decay<meter_t>::type,
+                            typename std::decay<decltype(
+                                ceil(meter_t(val)))>::type>::value));
+}
+
+TEST_F(UnitMath, floor) {
+  double val = 101.1;
+  EXPECT_EQ(std::floor(val), floor(scalar_t(val)));
+}
+
+TEST_F(UnitMath, fmod) {
+  EXPECT_EQ(std::fmod(100.0, 101.2),
+            fmod(meter_t(100.0), meter_t(101.2)).to<double>());
+}
+
+TEST_F(UnitMath, trunc) {
+  double val = 101.1;
+  EXPECT_EQ(std::trunc(val), trunc(scalar_t(val)));
+}
+
+TEST_F(UnitMath, round) {
+  double val = 101.1;
+  EXPECT_EQ(std::round(val), round(scalar_t(val)));
+}
+
+TEST_F(UnitMath, copysign) {
+  double sign = -1;
+  meter_t val(5.0);
+  EXPECT_EQ(meter_t(-5.0), copysign(val, sign));
+  EXPECT_EQ(meter_t(-5.0), copysign(val, angle::radian_t(sign)));
+}
+
+TEST_F(UnitMath, fdim) {
+  EXPECT_EQ(meter_t(0.0), fdim(meter_t(8.0), meter_t(10.0)));
+  EXPECT_EQ(meter_t(2.0), fdim(meter_t(10.0), meter_t(8.0)));
+  EXPECT_NEAR(meter_t(9.3904).to<double>(),
+              fdim(meter_t(10.0), foot_t(2.0)).to<double>(),
+              5.0e-320);  // not sure why they aren't comparing exactly equal,
+                          // but clearly they are.
+}
+
+TEST_F(UnitMath, fmin) {
+  EXPECT_EQ(meter_t(8.0), fmin(meter_t(8.0), meter_t(10.0)));
+  EXPECT_EQ(meter_t(8.0), fmin(meter_t(10.0), meter_t(8.0)));
+  EXPECT_EQ(foot_t(2.0), fmin(meter_t(10.0), foot_t(2.0)));
+}
+
+TEST_F(UnitMath, fmax) {
+  EXPECT_EQ(meter_t(10.0), fmax(meter_t(8.0), meter_t(10.0)));
+  EXPECT_EQ(meter_t(10.0), fmax(meter_t(10.0), meter_t(8.0)));
+  EXPECT_EQ(meter_t(10.0), fmax(meter_t(10.0), foot_t(2.0)));
+}
+
+TEST_F(UnitMath, fabs) {
+  EXPECT_EQ(meter_t(10.0), fabs(meter_t(-10.0)));
+  EXPECT_EQ(meter_t(10.0), fabs(meter_t(10.0)));
+}
+
+TEST_F(UnitMath, abs) {
+  EXPECT_EQ(meter_t(10.0), abs(meter_t(-10.0)));
+  EXPECT_EQ(meter_t(10.0), abs(meter_t(10.0)));
+}
+
+TEST_F(UnitMath, fma) {
+  meter_t x(2.0);
+  meter_t y(3.0);
+  square_meter_t z(1.0);
+  EXPECT_EQ(square_meter_t(7.0), fma(x, y, z));
+}
+
+// Constexpr
+#if !defined(_MSC_VER) || _MSC_VER > 1800
+TEST_F(Constexpr, construction) {
+  constexpr meter_t result0(0);
+  constexpr auto result1 = make_unit<meter_t>(1);
+  constexpr auto result2 = meter_t(2);
+
+  EXPECT_EQ(meter_t(0), result0);
+  EXPECT_EQ(meter_t(1), result1);
+  EXPECT_EQ(meter_t(2), result2);
+
+  EXPECT_TRUE(noexcept(result0));
+  EXPECT_TRUE(noexcept(result1));
+  EXPECT_TRUE(noexcept(result2));
+}
+
+TEST_F(Constexpr, constants) {
+  EXPECT_TRUE(noexcept(constants::c()));
+  EXPECT_TRUE(noexcept(constants::G()));
+  EXPECT_TRUE(noexcept(constants::h()));
+  EXPECT_TRUE(noexcept(constants::mu0()));
+  EXPECT_TRUE(noexcept(constants::epsilon0()));
+  EXPECT_TRUE(noexcept(constants::Z0()));
+  EXPECT_TRUE(noexcept(constants::k_e()));
+  EXPECT_TRUE(noexcept(constants::e()));
+  EXPECT_TRUE(noexcept(constants::m_e()));
+  EXPECT_TRUE(noexcept(constants::m_p()));
+  EXPECT_TRUE(noexcept(constants::mu_B()));
+  EXPECT_TRUE(noexcept(constants::N_A()));
+  EXPECT_TRUE(noexcept(constants::R()));
+  EXPECT_TRUE(noexcept(constants::k_B()));
+  EXPECT_TRUE(noexcept(constants::F()));
+  EXPECT_TRUE(noexcept(constants::sigma()));
+}
+
+TEST_F(Constexpr, arithmetic) {
+  constexpr auto result0(1_m + 1_m);
+  constexpr auto result1(1_m - 1_m);
+  constexpr auto result2(1_m * 1_m);
+  constexpr auto result3(1_m / 1_m);
+  constexpr auto result4(meter_t(1) + meter_t(1));
+  constexpr auto result5(meter_t(1) - meter_t(1));
+  constexpr auto result6(meter_t(1) * meter_t(1));
+  constexpr auto result7(meter_t(1) / meter_t(1));
+  constexpr auto result8(units::math::cpow<2>(meter_t(2)));
+  constexpr auto result9 = units::math::cpow<3>(2_m);
+  constexpr auto result10 = 2_m * 2_m;
+
+  EXPECT_TRUE(noexcept(result0));
+  EXPECT_TRUE(noexcept(result1));
+  EXPECT_TRUE(noexcept(result2));
+  EXPECT_TRUE(noexcept(result3));
+  EXPECT_TRUE(noexcept(result4));
+  EXPECT_TRUE(noexcept(result5));
+  EXPECT_TRUE(noexcept(result6));
+  EXPECT_TRUE(noexcept(result7));
+  EXPECT_TRUE(noexcept(result8));
+  EXPECT_TRUE(noexcept(result9));
+  EXPECT_TRUE(noexcept(result10));
+
+  EXPECT_EQ(8_cu_m, result9);
+  EXPECT_EQ(4_sq_m, result10);
+}
+
+TEST_F(Constexpr, realtional) {
+  constexpr bool equalityTrue = (1_m == 1_m);
+  constexpr bool equalityFalse = (1_m == 2_m);
+  constexpr bool lessThanTrue = (1_m < 2_m);
+  constexpr bool lessThanFalse = (1_m < 1_m);
+  constexpr bool lessThanEqualTrue1 = (1_m <= 1_m);
+  constexpr bool lessThanEqualTrue2 = (1_m <= 2_m);
+  constexpr bool lessThanEqualFalse = (1_m < 0_m);
+  constexpr bool greaterThanTrue = (2_m > 1_m);
+  constexpr bool greaterThanFalse = (2_m > 2_m);
+  constexpr bool greaterThanEqualTrue1 = (2_m >= 1_m);
+  constexpr bool greaterThanEqualTrue2 = (2_m >= 2_m);
+  constexpr bool greaterThanEqualFalse = (2_m > 3_m);
+
+  EXPECT_TRUE(equalityTrue);
+  EXPECT_TRUE(lessThanTrue);
+  EXPECT_TRUE(lessThanEqualTrue1);
+  EXPECT_TRUE(lessThanEqualTrue2);
+  EXPECT_TRUE(greaterThanTrue);
+  EXPECT_TRUE(greaterThanEqualTrue1);
+  EXPECT_TRUE(greaterThanEqualTrue2);
+  EXPECT_FALSE(equalityFalse);
+  EXPECT_FALSE(lessThanFalse);
+  EXPECT_FALSE(lessThanEqualFalse);
+  EXPECT_FALSE(greaterThanFalse);
+  EXPECT_FALSE(greaterThanEqualFalse);
+}
+
+TEST_F(Constexpr, stdArray) {
+  constexpr std::array<meter_t, 5> arr = {0_m, 1_m, 2_m, 3_m, 4_m};
+  constexpr bool equal = (arr[3] == 3_m);
+  EXPECT_TRUE(equal);
+}
+
+#endif
+
+TEST_F(CompileTimeArithmetic, unit_value_t) {
+  typedef unit_value_t<meters, 3, 2> mRatio;
+  EXPECT_EQ(meter_t(1.5), mRatio::value());
+}
+
+TEST_F(CompileTimeArithmetic, is_unit_value_t) {
+  typedef unit_value_t<meters, 3, 2> mRatio;
+
+  EXPECT_TRUE((traits::is_unit_value_t<mRatio>::value));
+  EXPECT_FALSE((traits::is_unit_value_t<meter_t>::value));
+  EXPECT_FALSE((traits::is_unit_value_t<double>::value));
+
+  EXPECT_TRUE((traits::is_unit_value_t<mRatio, meters>::value));
+  EXPECT_FALSE((traits::is_unit_value_t<meter_t, meters>::value));
+  EXPECT_FALSE((traits::is_unit_value_t<double, meters>::value));
+}
+
+TEST_F(CompileTimeArithmetic, is_unit_value_t_category) {
+  typedef unit_value_t<feet, 3, 2> mRatio;
+  EXPECT_TRUE(
+      (traits::is_unit_value_t_category<category::length_unit, mRatio>::value));
+  EXPECT_FALSE(
+      (traits::is_unit_value_t_category<category::angle_unit, mRatio>::value));
+  EXPECT_FALSE((
+      traits::is_unit_value_t_category<category::length_unit, meter_t>::value));
+  EXPECT_FALSE(
+      (traits::is_unit_value_t_category<category::length_unit, double>::value));
+}
+
+TEST_F(CompileTimeArithmetic, unit_value_add) {
+  typedef unit_value_t<meters, 3, 2> mRatio;
+
+  using sum = unit_value_add<mRatio, mRatio>;
+  EXPECT_EQ(meter_t(3.0), sum::value());
+  EXPECT_TRUE(
+      (traits::is_unit_value_t_category<category::length_unit, sum>::value));
+
+  typedef unit_value_t<feet, 1> ftRatio;
+
+  using sumf = unit_value_add<ftRatio, mRatio>;
+  EXPECT_TRUE((
+      std::is_same<typename std::decay<foot_t>::type,
+                   typename std::decay<decltype(sumf::value())>::type>::value));
+  EXPECT_NEAR(5.92125984, sumf::value().to<double>(), 5.0e-8);
+  EXPECT_TRUE(
+      (traits::is_unit_value_t_category<category::length_unit, sumf>::value));
+
+  typedef unit_value_t<celsius, 1> cRatio;
+  typedef unit_value_t<fahrenheit, 2> fRatio;
+
+  using sumc = unit_value_add<cRatio, fRatio>;
+  EXPECT_TRUE((
+      std::is_same<typename std::decay<celsius_t>::type,
+                   typename std::decay<decltype(sumc::value())>::type>::value));
+  EXPECT_NEAR(2.11111111111, sumc::value().to<double>(), 5.0e-8);
+  EXPECT_TRUE((traits::is_unit_value_t_category<category::temperature_unit,
+                                                sumc>::value));
+
+  typedef unit_value_t<angle::radian, 1> rRatio;
+  typedef unit_value_t<angle::degree, 3> dRatio;
+
+  using sumr = unit_value_add<rRatio, dRatio>;
+  EXPECT_TRUE((
+      std::is_same<typename std::decay<angle::radian_t>::type,
+                   typename std::decay<decltype(sumr::value())>::type>::value));
+  EXPECT_NEAR(1.05235988, sumr::value().to<double>(), 5.0e-8);
+  EXPECT_TRUE(
+      (traits::is_unit_value_t_category<category::angle_unit, sumr>::value));
+}
+
+TEST_F(CompileTimeArithmetic, unit_value_subtract) {
+  typedef unit_value_t<meters, 3, 2> mRatio;
+
+  using diff = unit_value_subtract<mRatio, mRatio>;
+  EXPECT_EQ(meter_t(0), diff::value());
+  EXPECT_TRUE(
+      (traits::is_unit_value_t_category<category::length_unit, diff>::value));
+
+  typedef unit_value_t<feet, 1> ftRatio;
+
+  using difff = unit_value_subtract<ftRatio, mRatio>;
+  EXPECT_TRUE((std::is_same<
+               typename std::decay<foot_t>::type,
+               typename std::decay<decltype(difff::value())>::type>::value));
+  EXPECT_NEAR(-3.92125984, difff::value().to<double>(), 5.0e-8);
+  EXPECT_TRUE(
+      (traits::is_unit_value_t_category<category::length_unit, difff>::value));
+
+  typedef unit_value_t<celsius, 1> cRatio;
+  typedef unit_value_t<fahrenheit, 2> fRatio;
+
+  using diffc = unit_value_subtract<cRatio, fRatio>;
+  EXPECT_TRUE((std::is_same<
+               typename std::decay<celsius_t>::type,
+               typename std::decay<decltype(diffc::value())>::type>::value));
+  EXPECT_NEAR(-0.11111111111, diffc::value().to<double>(), 5.0e-8);
+  EXPECT_TRUE((traits::is_unit_value_t_category<category::temperature_unit,
+                                                diffc>::value));
+
+  typedef unit_value_t<angle::radian, 1> rRatio;
+  typedef unit_value_t<angle::degree, 3> dRatio;
+
+  using diffr = unit_value_subtract<rRatio, dRatio>;
+  EXPECT_TRUE((std::is_same<
+               typename std::decay<angle::radian_t>::type,
+               typename std::decay<decltype(diffr::value())>::type>::value));
+  EXPECT_NEAR(0.947640122, diffr::value().to<double>(), 5.0e-8);
+  EXPECT_TRUE(
+      (traits::is_unit_value_t_category<category::angle_unit, diffr>::value));
+}
+
+TEST_F(CompileTimeArithmetic, unit_value_multiply) {
+  typedef unit_value_t<meters, 2> mRatio;
+  typedef unit_value_t<feet, 656168, 100000> ftRatio;  // 2 meter
+
+  using product = unit_value_multiply<mRatio, mRatio>;
+  EXPECT_EQ(square_meter_t(4), product::value());
+  EXPECT_TRUE(
+      (traits::is_unit_value_t_category<category::area_unit, product>::value));
+
+  using productM = unit_value_multiply<mRatio, ftRatio>;
+
+  EXPECT_TRUE((std::is_same<
+               typename std::decay<square_meter_t>::type,
+               typename std::decay<decltype(productM::value())>::type>::value));
+  EXPECT_NEAR(4.0, productM::value().to<double>(), 5.0e-7);
+  EXPECT_TRUE(
+      (traits::is_unit_value_t_category<category::area_unit, productM>::value));
+
+  using productF = unit_value_multiply<ftRatio, mRatio>;
+  EXPECT_TRUE((std::is_same<
+               typename std::decay<square_foot_t>::type,
+               typename std::decay<decltype(productF::value())>::type>::value));
+  EXPECT_NEAR(43.0556444224, productF::value().to<double>(), 5.0e-6);
+  EXPECT_TRUE(
+      (traits::is_unit_value_t_category<category::area_unit, productF>::value));
+
+  using productF2 = unit_value_multiply<ftRatio, ftRatio>;
+  EXPECT_TRUE(
+      (std::is_same<
+          typename std::decay<square_foot_t>::type,
+          typename std::decay<decltype(productF2::value())>::type>::value));
+  EXPECT_NEAR(43.0556444224, productF2::value().to<double>(), 5.0e-8);
+  EXPECT_TRUE((
+      traits::is_unit_value_t_category<category::area_unit, productF2>::value));
+
+  typedef unit_value_t<units::force::newton, 5> nRatio;
+
+  using productN = unit_value_multiply<nRatio, ftRatio>;
+  EXPECT_FALSE(
+      (std::is_same<
+          typename std::decay<torque::newton_meter_t>::type,
+          typename std::decay<decltype(productN::value())>::type>::value));
+  EXPECT_TRUE((std::is_convertible<
+               typename std::decay<torque::newton_meter_t>::type,
+               typename std::decay<decltype(productN::value())>::type>::value));
+  EXPECT_NEAR(32.8084, productN::value().to<double>(), 5.0e-8);
+  EXPECT_NEAR(10.0, (productN::value().convert<newton_meter>().to<double>()),
+              5.0e-7);
+  EXPECT_TRUE((traits::is_unit_value_t_category<category::torque_unit,
+                                                productN>::value));
+
+  typedef unit_value_t<angle::radian, 11, 10> r1Ratio;
+  typedef unit_value_t<angle::radian, 22, 10> r2Ratio;
+
+  using productR = unit_value_multiply<r1Ratio, r2Ratio>;
+  EXPECT_TRUE((std::is_same<
+               typename std::decay<steradian_t>::type,
+               typename std::decay<decltype(productR::value())>::type>::value));
+  EXPECT_NEAR(2.42, productR::value().to<double>(), 5.0e-8);
+  EXPECT_NEAR(7944.39137,
+              (productR::value().convert<degrees_squared>().to<double>()),
+              5.0e-6);
+  EXPECT_TRUE((traits::is_unit_value_t_category<category::solid_angle_unit,
+                                                productR>::value));
+}
+
+TEST_F(CompileTimeArithmetic, unit_value_divide) {
+  typedef unit_value_t<meters, 2> mRatio;
+  typedef unit_value_t<feet, 656168, 100000> ftRatio;  // 2 meter
+
+  using product = unit_value_divide<mRatio, mRatio>;
+  EXPECT_EQ(scalar_t(1), product::value());
+  EXPECT_TRUE((
+      traits::is_unit_value_t_category<category::scalar_unit, product>::value));
+
+  using productM = unit_value_divide<mRatio, ftRatio>;
+
+  EXPECT_TRUE((std::is_same<
+               typename std::decay<scalar_t>::type,
+               typename std::decay<decltype(productM::value())>::type>::value));
+  EXPECT_NEAR(1, productM::value().to<double>(), 5.0e-7);
+  EXPECT_TRUE((traits::is_unit_value_t_category<category::scalar_unit,
+                                                productM>::value));
+
+  using productF = unit_value_divide<ftRatio, mRatio>;
+  EXPECT_TRUE((std::is_same<
+               typename std::decay<scalar_t>::type,
+               typename std::decay<decltype(productF::value())>::type>::value));
+  EXPECT_NEAR(1.0, productF::value().to<double>(), 5.0e-6);
+  EXPECT_TRUE((traits::is_unit_value_t_category<category::scalar_unit,
+                                                productF>::value));
+
+  using productF2 = unit_value_divide<ftRatio, ftRatio>;
+  EXPECT_TRUE(
+      (std::is_same<
+          typename std::decay<scalar_t>::type,
+          typename std::decay<decltype(productF2::value())>::type>::value));
+  EXPECT_NEAR(1.0, productF2::value().to<double>(), 5.0e-8);
+  EXPECT_TRUE((traits::is_unit_value_t_category<category::scalar_unit,
+                                                productF2>::value));
+
+  typedef unit_value_t<seconds, 10> sRatio;
+
+  using productMS = unit_value_divide<mRatio, sRatio>;
+  EXPECT_TRUE(
+      (std::is_same<
+          typename std::decay<meters_per_second_t>::type,
+          typename std::decay<decltype(productMS::value())>::type>::value));
+  EXPECT_NEAR(0.2, productMS::value().to<double>(), 5.0e-8);
+  EXPECT_TRUE((traits::is_unit_value_t_category<category::velocity_unit,
+                                                productMS>::value));
+
+  typedef unit_value_t<angle::radian, 20> rRatio;
+
+  using productRS = unit_value_divide<rRatio, sRatio>;
+  EXPECT_TRUE(
+      (std::is_same<
+          typename std::decay<radians_per_second_t>::type,
+          typename std::decay<decltype(productRS::value())>::type>::value));
+  EXPECT_NEAR(2, productRS::value().to<double>(), 5.0e-8);
+  EXPECT_NEAR(114.592,
+              (productRS::value().convert<degrees_per_second>().to<double>()),
+              5.0e-4);
+  EXPECT_TRUE((traits::is_unit_value_t_category<category::angular_velocity_unit,
+                                                productRS>::value));
+}
+
+TEST_F(CompileTimeArithmetic, unit_value_power) {
+  typedef unit_value_t<meters, 2> mRatio;
+
+  using sq = unit_value_power<mRatio, 2>;
+  EXPECT_TRUE((std::is_convertible<
+               typename std::decay<square_meter_t>::type,
+               typename std::decay<decltype(sq::value())>::type>::value));
+  EXPECT_NEAR(4, sq::value().to<double>(), 5.0e-8);
+  EXPECT_TRUE(
+      (traits::is_unit_value_t_category<category::area_unit, sq>::value));
+
+  typedef unit_value_t<angle::radian, 18, 10> rRatio;
+
+  using sqr = unit_value_power<rRatio, 2>;
+  EXPECT_TRUE((std::is_convertible<
+               typename std::decay<steradian_t>::type,
+               typename std::decay<decltype(sqr::value())>::type>::value));
+  EXPECT_NEAR(3.24, sqr::value().to<double>(), 5.0e-8);
+  EXPECT_NEAR(10636.292574038049895092690529904,
+              (sqr::value().convert<degrees_squared>().to<double>()), 5.0e-10);
+  EXPECT_TRUE((traits::is_unit_value_t_category<category::solid_angle_unit,
+                                                sqr>::value));
+}
+
+TEST_F(CompileTimeArithmetic, unit_value_sqrt) {
+  typedef unit_value_t<square_meters, 10> mRatio;
+
+  using root = unit_value_sqrt<mRatio>;
+  EXPECT_TRUE((std::is_convertible<
+               typename std::decay<meter_t>::type,
+               typename std::decay<decltype(root::value())>::type>::value));
+  EXPECT_NEAR(3.16227766017, root::value().to<double>(), 5.0e-9);
+  EXPECT_TRUE(
+      (traits::is_unit_value_t_category<category::length_unit, root>::value));
+
+  typedef unit_value_t<hectare, 51, 7> hRatio;
+
+  using rooth = unit_value_sqrt<hRatio, 100000000>;
+  EXPECT_TRUE((std::is_convertible<
+               typename std::decay<mile_t>::type,
+               typename std::decay<decltype(rooth::value())>::type>::value));
+  EXPECT_NEAR(2.69920623253, rooth::value().to<double>(), 5.0e-8);
+  EXPECT_NEAR(269.920623, rooth::value().convert<meters>().to<double>(),
+              5.0e-6);
+  EXPECT_TRUE(
+      (traits::is_unit_value_t_category<category::length_unit, rooth>::value));
+
+  typedef unit_value_t<steradian, 18, 10> rRatio;
+
+  using rootr = unit_value_sqrt<rRatio>;
+  EXPECT_TRUE((traits::is_angle_unit<decltype(rootr::value())>::value));
+  EXPECT_NEAR(1.3416407865, rootr::value().to<double>(), 5.0e-8);
+  EXPECT_NEAR(76.870352574,
+              rootr::value().convert<angle::degrees>().to<double>(), 5.0e-6);
+  EXPECT_TRUE(
+      (traits::is_unit_value_t_category<category::angle_unit, rootr>::value));
+}
+
+TEST_F(CaseStudies, radarRangeEquation) {
+  watt_t P_t;            // transmit power
+  scalar_t G;            // gain
+  meter_t lambda;        // wavelength
+  square_meter_t sigma;  // radar cross section
+  meter_t R;             // range
+  kelvin_t T_s;          // system noise temp
+  hertz_t B_n;           // bandwidth
+  scalar_t L;            // loss
+
+  P_t = megawatt_t(1.4);
+  G = dB_t(33.0);
+  lambda = constants::c / megahertz_t(2800);
+  sigma = square_meter_t(1.0);
+  R = meter_t(111000.0);
+  T_s = kelvin_t(950.0);
+  B_n = megahertz_t(1.67);
+  L = dB_t(8.0);
+
+  scalar_t SNR = (P_t * math::pow<2>(G) * math::pow<2>(lambda) * sigma) /
+                 (math::pow<3>(4 * constants::pi) * math::pow<4>(R) *
+                  constants::k_B * T_s * B_n * L);
+
+  EXPECT_NEAR(1.535, SNR(), 5.0e-4);
+}
+
+TEST_F(CaseStudies, pythagoreanTheorum) {
+  EXPECT_EQ(meter_t(3), RightTriangle::a::value());
+  EXPECT_EQ(meter_t(4), RightTriangle::b::value());
+  EXPECT_EQ(meter_t(5), RightTriangle::c::value());
+  EXPECT_TRUE(pow<2>(RightTriangle::a::value()) +
+                  pow<2>(RightTriangle::b::value()) ==
+              pow<2>(RightTriangle::c::value()));
+}
diff --git a/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp b/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp
index 692e2a7..2db9b54 100644
--- a/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp
+++ b/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp
@@ -1,5 +1,5 @@
 /*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
 /* Open Source Software - may be modified and shared by FRC teams. The code   */
 /* must be accompanied by the FIRST BSD license file in the root directory of */
 /* the project.                                                               */
@@ -135,9 +135,8 @@
   mockProtocol = "myProtocol";
 
   clientPipe->Connect(pipeName, [&] {
-    auto ws = WebSocket::CreateClient(
-        *clientPipe, "/test", pipeName,
-        ArrayRef<StringRef>{"myProtocol", "myProtocol2"});
+    auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName,
+                                      {"myProtocol", "myProtocol2"});
     ws->closed.connect([&](uint16_t code, StringRef msg) {
       Finish();
       if (code != 1005 && code != 1006)
@@ -222,8 +221,8 @@
   std::shared_ptr<WebSocket> ws;
 };
 
-INSTANTIATE_TEST_CASE_P(WebSocketClientDataTests, WebSocketClientDataTest,
-                        ::testing::Values(0, 1, 125, 126, 65535, 65536), );
+INSTANTIATE_TEST_SUITE_P(WebSocketClientDataTests, WebSocketClientDataTest,
+                         ::testing::Values(0, 1, 125, 126, 65535, 65536));
 
 TEST_P(WebSocketClientDataTest, SendBinary) {
   int gotCallback = 0;
diff --git a/wpiutil/src/test/native/cpp/WebSocketIntegrationTest.cpp b/wpiutil/src/test/native/cpp/WebSocketIntegrationTest.cpp
index f51e8fc..9a66a2e 100644
--- a/wpiutil/src/test/native/cpp/WebSocketIntegrationTest.cpp
+++ b/wpiutil/src/test/native/cpp/WebSocketIntegrationTest.cpp
@@ -1,5 +1,5 @@
 /*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
 /* Open Source Software - may be modified and shared by FRC teams. The code   */
 /* must be accompanied by the FIRST BSD license file in the root directory of */
 /* the project.                                                               */
@@ -35,7 +35,7 @@
       if (code != 1005 && code != 1006)
         FAIL() << "Code: " << code << " Reason: " << reason;
     });
-    ws->open.connect([&, s = ws.get() ](StringRef) {
+    ws->open.connect([&, s = ws.get()](StringRef) {
       ++gotClientOpen;
       s->Close();
     });
@@ -68,7 +68,7 @@
       if (code != 1005 && code != 1006)
         FAIL() << "Code: " << code << " Reason: " << reason;
     });
-    ws->open.connect([&, s = ws.get() ](StringRef protocol) {
+    ws->open.connect([&, s = ws.get()](StringRef protocol) {
       ++gotClientOpen;
       s->Close();
       ASSERT_EQ(protocol, "proto1");
@@ -134,7 +134,7 @@
       if (code != 1005 && code != 1006)
         FAIL() << "Code: " << code << " Reason: " << reason;
     });
-    ws->open.connect([&, s = ws.get() ](StringRef) {
+    ws->open.connect([&, s = ws.get()](StringRef) {
       s->SendText(uv::Buffer{"hello"}, [&](auto, uv::Error) {});
       s->Close();
     });
diff --git a/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp b/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp
index 7d723e3..d11fdda 100644
--- a/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp
+++ b/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp
@@ -1,5 +1,5 @@
 /*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
 /* Open Source Software - may be modified and shared by FRC teams. The code   */
 /* must be accompanied by the FIRST BSD license file in the root directory of */
 /* the project.                                                               */
@@ -141,14 +141,15 @@
     });
   };
   // need to respond with close for server to finish shutdown
-  auto message = BuildMessage(0x08, true, true, {0x03u, 0xe8u});
+  const uint8_t contents[] = {0x03u, 0xe8u};
+  auto message = BuildMessage(0x08, true, true, contents);
   handleData = [&](StringRef) {
     clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
   };
 
   loop->Run();
 
-  auto expectData = BuildMessage(0x08, true, false, {0x03u, 0xe8u});
+  auto expectData = BuildMessage(0x08, true, false, contents);
   ASSERT_EQ(wireData, expectData);
   ASSERT_EQ(gotClosed, 1);
 }
@@ -164,16 +165,15 @@
     });
   };
   // need to respond with close for server to finish shutdown
-  auto message = BuildMessage(0x08, true, true,
-                              {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'});
+  const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'};
+  auto message = BuildMessage(0x08, true, true, contents);
   handleData = [&](StringRef) {
     clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
   };
 
   loop->Run();
 
-  auto expectData = BuildMessage(0x08, true, false,
-                                 {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'});
+  auto expectData = BuildMessage(0x08, true, false, contents);
   ASSERT_EQ(wireData, expectData);
   ASSERT_EQ(gotClosed, 1);
 }
@@ -211,7 +211,8 @@
       ASSERT_EQ(code, 1000) << "reason: " << reason;
     });
   };
-  auto message = BuildMessage(0x08, true, true, {0x03u, 0xe8u});
+  const uint8_t contents[] = {0x03u, 0xe8u};
+  auto message = BuildMessage(0x08, true, true, contents);
   resp.headersComplete.connect([&](bool) {
     clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
   });
@@ -219,7 +220,7 @@
   loop->Run();
 
   // the endpoint should echo the message
-  auto expectData = BuildMessage(0x08, true, false, {0x03u, 0xe8u});
+  auto expectData = BuildMessage(0x08, true, false, contents);
   ASSERT_EQ(wireData, expectData);
   ASSERT_EQ(gotClosed, 1);
 }
@@ -233,8 +234,8 @@
       ASSERT_EQ(reason, "hangup");
     });
   };
-  auto message = BuildMessage(0x08, true, true,
-                              {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'});
+  const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'};
+  auto message = BuildMessage(0x08, true, true, contents);
   resp.headersComplete.connect([&](bool) {
     clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
   });
@@ -242,8 +243,7 @@
   loop->Run();
 
   // the endpoint should echo the message
-  auto expectData = BuildMessage(0x08, true, false,
-                                 {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'});
+  auto expectData = BuildMessage(0x08, true, false, contents);
   ASSERT_EQ(wireData, expectData);
   ASSERT_EQ(gotClosed, 1);
 }
@@ -257,10 +257,10 @@
     : public WebSocketServerTest,
       public ::testing::WithParamInterface<uint8_t> {};
 
-INSTANTIATE_TEST_CASE_P(WebSocketServerBadOpcodeTests,
-                        WebSocketServerBadOpcodeTest,
-                        ::testing::Values(3, 4, 5, 6, 7, 0xb, 0xc, 0xd, 0xe,
-                                          0xf), );
+INSTANTIATE_TEST_SUITE_P(WebSocketServerBadOpcodeTests,
+                         WebSocketServerBadOpcodeTest,
+                         ::testing::Values(3, 4, 5, 6, 7, 0xb, 0xc, 0xd, 0xe,
+                                           0xf));
 
 TEST_P(WebSocketServerBadOpcodeTest, Receive) {
   int gotCallback = 0;
@@ -289,9 +289,9 @@
     : public WebSocketServerTest,
       public ::testing::WithParamInterface<uint8_t> {};
 
-INSTANTIATE_TEST_CASE_P(WebSocketServerControlFrameTests,
-                        WebSocketServerControlFrameTest,
-                        ::testing::Values(0x8, 0x9, 0xa), );
+INSTANTIATE_TEST_SUITE_P(WebSocketServerControlFrameTests,
+                         WebSocketServerControlFrameTest,
+                         ::testing::Values(0x8, 0x9, 0xa));
 
 TEST_P(WebSocketServerControlFrameTest, ReceiveFragment) {
   int gotCallback = 0;
@@ -532,8 +532,8 @@
 class WebSocketServerDataTest : public WebSocketServerTest,
                                 public ::testing::WithParamInterface<size_t> {};
 
-INSTANTIATE_TEST_CASE_P(WebSocketServerDataTests, WebSocketServerDataTest,
-                        ::testing::Values(0, 1, 125, 126, 65535, 65536), );
+INSTANTIATE_TEST_SUITE_P(WebSocketServerDataTests, WebSocketServerDataTest,
+                         ::testing::Values(0, 1, 125, 126, 65535, 65536));
 
 TEST_P(WebSocketServerDataTest, SendText) {
   int gotCallback = 0;
diff --git a/wpiutil/src/test/native/cpp/json/unit-cbor.cpp b/wpiutil/src/test/native/cpp/json/unit-cbor.cpp
index 84e98eb..2e37a17 100644
--- a/wpiutil/src/test/native/cpp/json/unit-cbor.cpp
+++ b/wpiutil/src/test/native/cpp/json/unit-cbor.cpp
@@ -139,8 +139,8 @@
     -4294967297,
 };
 
-INSTANTIATE_TEST_CASE_P(CborSignedNeg8Tests, CborSignedNeg8Test,
-                        ::testing::ValuesIn(neg8_numbers), );
+INSTANTIATE_TEST_SUITE_P(CborSignedNeg8Tests, CborSignedNeg8Test,
+                        ::testing::ValuesIn(neg8_numbers));
 
 // -4294967296..-65537
 class CborSignedNeg4Test : public ::testing::TestWithParam<int64_t> {};
@@ -189,8 +189,8 @@
     -4294967296,
 };
 
-INSTANTIATE_TEST_CASE_P(CborSignedNeg4Tests, CborSignedNeg4Test,
-                        ::testing::ValuesIn(neg4_numbers), );
+INSTANTIATE_TEST_SUITE_P(CborSignedNeg4Tests, CborSignedNeg4Test,
+                        ::testing::ValuesIn(neg4_numbers));
 
 // -65536..-257
 TEST(CborSignedTest, Neg2)
@@ -447,8 +447,8 @@
     1048576u,
 };
 
-INSTANTIATE_TEST_CASE_P(CborSignedPos4Tests, CborSignedPos4Test,
-                        ::testing::ValuesIn(pos4_numbers), );
+INSTANTIATE_TEST_SUITE_P(CborSignedPos4Tests, CborSignedPos4Test,
+                        ::testing::ValuesIn(pos4_numbers));
 
 // 4294967296..4611686018427387903
 class CborSignedPos8Test : public ::testing::TestWithParam<uint64_t> {};
@@ -500,8 +500,8 @@
     4611686018427387903ul
 };
 
-INSTANTIATE_TEST_CASE_P(CborSignedPos8Tests, CborSignedPos8Test,
-                        ::testing::ValuesIn(pos8_numbers), );
+INSTANTIATE_TEST_SUITE_P(CborSignedPos8Tests, CborSignedPos8Test,
+                        ::testing::ValuesIn(pos8_numbers));
 
 /*
 // -32768..-129 (int 16)
@@ -670,8 +670,8 @@
     EXPECT_EQ(json::from_cbor(result), j);
 }
 
-INSTANTIATE_TEST_CASE_P(CborUnsignedPos4Tests, CborUnsignedPos4Test,
-                        ::testing::ValuesIn(pos4_numbers), );
+INSTANTIATE_TEST_SUITE_P(CborUnsignedPos4Tests, CborUnsignedPos4Test,
+                        ::testing::ValuesIn(pos4_numbers));
 
 // 4294967296..4611686018427387903 (eight-byte uint64_t)
 class CborUnsignedPos8Test : public ::testing::TestWithParam<uint64_t> {};
@@ -716,8 +716,8 @@
     EXPECT_EQ(json::from_cbor(result), j);
 }
 
-INSTANTIATE_TEST_CASE_P(CborUnsignedPos8Tests, CborUnsignedPos8Test,
-                        ::testing::ValuesIn(pos8_numbers), );
+INSTANTIATE_TEST_SUITE_P(CborUnsignedPos8Tests, CborUnsignedPos8Test,
+                        ::testing::ValuesIn(pos8_numbers));
 
 // 3.1415925
 TEST(CborFloatTest, Number)
@@ -853,8 +853,8 @@
     65535u
 };
 
-INSTANTIATE_TEST_CASE_P(CborString3Tests, CborString3Test,
-                        ::testing::ValuesIn(string3_lens), );
+INSTANTIATE_TEST_SUITE_P(CborString3Tests, CborString3Test,
+                        ::testing::ValuesIn(string3_lens));
 
 // N = 65536..4294967295
 class CborString5Test : public ::testing::TestWithParam<size_t> {};
@@ -893,8 +893,8 @@
     1048576u
 };
 
-INSTANTIATE_TEST_CASE_P(CborString5Tests, CborString5Test,
-                        ::testing::ValuesIn(string5_lens), );
+INSTANTIATE_TEST_SUITE_P(CborString5Tests, CborString5Test,
+                        ::testing::ValuesIn(string5_lens));
 
 TEST(CborArrayTest, Empty)
 {
@@ -948,7 +948,7 @@
 {
     json j(257, nullptr);
     std::vector<uint8_t> expected(j.size() + 3, 0xf6); // all null
-    expected[0] = static_cast<char>(0x99); // array 16 bit
+    expected[0] = static_cast<uint8_t>(0x99); // array 16 bit
     expected[1] = 0x01; // size (0x0101), byte 0
     expected[2] = 0x01; // size (0x0101), byte 1
     const auto result = json::to_cbor(j);
@@ -963,7 +963,7 @@
 {
     json j(65793, nullptr);
     std::vector<uint8_t> expected(j.size() + 5, 0xf6); // all null
-    expected[0] = static_cast<char>(0x9a); // array 32 bit
+    expected[0] = static_cast<uint8_t>(0x9a); // array 32 bit
     expected[1] = 0x00; // size (0x00010101), byte 0
     expected[2] = 0x01; // size (0x00010101), byte 1
     expected[3] = 0x01; // size (0x00010101), byte 2
@@ -1236,8 +1236,8 @@
     0xf8,
 };
 
-INSTANTIATE_TEST_CASE_P(CborUnsupportedBytesTests, CborUnsupportedBytesTest,
-                        ::testing::ValuesIn(unsupported_bytes_cases), );
+INSTANTIATE_TEST_SUITE_P(CborUnsupportedBytesTests, CborUnsupportedBytesTest,
+                        ::testing::ValuesIn(unsupported_bytes_cases));
 #if 0
 // use this testcase outside [hide] to run it with Valgrind
 TEST(CborRoundtripTest, Sample)
@@ -1327,7 +1327,7 @@
     "test/data/cbor_regression/test21",
 };
 
-INSTANTIATE_TEST_CASE_P(CborRegressionFuzzTests, CborRegressionFuzzTest,
+INSTANTIATE_TEST_SUITE_P(CborRegressionFuzzTests, CborRegressionFuzzTest,
                         ::testing::ValuesIn(fuzz_test_cases));
 
 class CborRegressionFlynnTest : public ::testing::TestWithParam<const char*> {};
@@ -1497,7 +1497,7 @@
     "test/data/nst_json_testsuite/test_parsing/y_structure_whitespace_array.json",
 };
 
-INSTANTIATE_TEST_CASE_P(CborRegressionFlynnTests, CborRegressionFlynnTest,
+INSTANTIATE_TEST_SUITE_P(CborRegressionFlynnTests, CborRegressionFlynnTest,
                         ::testing::ValuesIn(flynn_test_cases));
 
 #endif
@@ -1644,16 +1644,16 @@
     {"-4.1", {0xfb,0xc0,0x10,0x66,0x66,0x66,0x66,0x66,0x66}, true},
 };
 
-INSTANTIATE_TEST_CASE_P(CborRfc7049AppendixANumberTests, CborRoundtripTest,
-                        ::testing::ValuesIn(rfc7049_appendix_a_numbers), );
+INSTANTIATE_TEST_SUITE_P(CborRfc7049AppendixANumberTests, CborRoundtripTest,
+                        ::testing::ValuesIn(rfc7049_appendix_a_numbers));
 
 static const internal::CborRoundtripTestParam rfc7049_appendix_a_simple_values[] = {
     {"false", {0xf4}, true},
     {"true", {0xf5}, true},
 };
 
-INSTANTIATE_TEST_CASE_P(CborRfc7049AppendixASimpleValueTests, CborRoundtripTest,
-                        ::testing::ValuesIn(rfc7049_appendix_a_simple_values), );
+INSTANTIATE_TEST_SUITE_P(CborRfc7049AppendixASimpleValueTests, CborRoundtripTest,
+                        ::testing::ValuesIn(rfc7049_appendix_a_simple_values));
 
 static const internal::CborRoundtripTestParam rfc7049_appendix_a_strings[] = {
     {"\"\"", {0x60}, true},
@@ -1666,8 +1666,8 @@
     {"\"streaming\"", {0x7f,0x65,0x73,0x74,0x72,0x65,0x61,0x64,0x6d,0x69,0x6e,0x67,0xff}, false},
 };
 
-INSTANTIATE_TEST_CASE_P(CborRfc7049AppendixAStringTests, CborRoundtripTest,
-                        ::testing::ValuesIn(rfc7049_appendix_a_strings), );
+INSTANTIATE_TEST_SUITE_P(CborRfc7049AppendixAStringTests, CborRoundtripTest,
+                        ::testing::ValuesIn(rfc7049_appendix_a_strings));
 
 static const internal::CborRoundtripTestParam rfc7049_appendix_a_arrays[] = {
     {"[]", {0x80}, true},
@@ -1683,8 +1683,8 @@
     {"[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]", {0x9f,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x18,0x18,0x19,0xff}, false},
 };
 
-INSTANTIATE_TEST_CASE_P(CborRfc7049AppendixAArrayTests, CborRoundtripTest,
-                        ::testing::ValuesIn(rfc7049_appendix_a_arrays), );
+INSTANTIATE_TEST_SUITE_P(CborRfc7049AppendixAArrayTests, CborRoundtripTest,
+                        ::testing::ValuesIn(rfc7049_appendix_a_arrays));
 
 static const internal::CborRoundtripTestParam rfc7049_appendix_a_objects[] = {
     {"{}", {0xa0}, true},
@@ -1697,5 +1697,5 @@
     {"{\"Fun\": true, \"Amt\": -2}", {0xbf,0x63,0x46,0x75,0x6e,0xf5,0x63,0x41,0x6d,0x74,0x21,0xff}, false},
 };
 
-INSTANTIATE_TEST_CASE_P(CborRfc7049AppendixAObjectTests, CborRoundtripTest,
-                        ::testing::ValuesIn(rfc7049_appendix_a_objects), );
+INSTANTIATE_TEST_SUITE_P(CborRfc7049AppendixAObjectTests, CborRoundtripTest,
+                        ::testing::ValuesIn(rfc7049_appendix_a_objects));
diff --git a/wpiutil/src/test/native/cpp/json/unit-constructor1.cpp b/wpiutil/src/test/native/cpp/json/unit-constructor1.cpp
index 71c4678..85b9b91 100644
--- a/wpiutil/src/test/native/cpp/json/unit-constructor1.cpp
+++ b/wpiutil/src/test/native/cpp/json/unit-constructor1.cpp
@@ -67,8 +67,8 @@
     json::value_t::number_float,
 };
 
-INSTANTIATE_TEST_CASE_P(JsonConstructTypeTests, JsonConstructTypeTest,
-                        ::testing::ValuesIn(construct_type_cases), );
+INSTANTIATE_TEST_SUITE_P(JsonConstructTypeTests, JsonConstructTypeTest,
+                        ::testing::ValuesIn(construct_type_cases));
 
 
 TEST(JsonConstructNullTest, NoParameter)
@@ -214,7 +214,7 @@
                          std::array<json, 6>, std::vector<json>,
                          std::deque<json>>
     JsonConstructArrayTestTypes;
-TYPED_TEST_CASE(JsonConstructArrayTest, JsonConstructArrayTestTypes);
+TYPED_TEST_SUITE(JsonConstructArrayTest, JsonConstructArrayTestTypes, );
 
 // clang warns on missing braces on the TypeParam initializer line below.
 // Suppress this warning.
@@ -405,7 +405,7 @@
 #endif
     > JsonConstructIntegerTestTypes;
 
-TYPED_TEST_CASE(JsonConstructIntegerTest, JsonConstructIntegerTestTypes);
+TYPED_TEST_SUITE(JsonConstructIntegerTest, JsonConstructIntegerTestTypes, );
 
 TYPED_TEST(JsonConstructIntegerTest, Implicit)
 {
@@ -523,7 +523,7 @@
                          >
     JsonConstructFloatTestTypes;
 
-TYPED_TEST_CASE(JsonConstructFloatTest, JsonConstructFloatTestTypes);
+TYPED_TEST_SUITE(JsonConstructFloatTest, JsonConstructFloatTestTypes, );
 
 TYPED_TEST(JsonConstructFloatTest, Implicit)
 {
diff --git a/wpiutil/src/test/native/cpp/json/unit-conversions.cpp b/wpiutil/src/test/native/cpp/json/unit-conversions.cpp
index e4981f2..b60e5ac 100644
--- a/wpiutil/src/test/native/cpp/json/unit-conversions.cpp
+++ b/wpiutil/src/test/native/cpp/json/unit-conversions.cpp
@@ -62,7 +62,7 @@
     , std::unordered_map<std::string, json>
     , std::unordered_multimap<std::string, json>
     > JsonGetObjectTestTypes;
-TYPED_TEST_CASE(JsonGetObjectTest, JsonGetObjectTestTypes);
+TYPED_TEST_SUITE(JsonGetObjectTest, JsonGetObjectTestTypes, );
 
 TYPED_TEST(JsonGetObjectTest, Explicit)
 {
@@ -109,7 +109,7 @@
                          /*std::forward_list<json>,*/ std::vector<json>,
                          std::deque<json>>
     JsonGetArrayTestTypes;
-TYPED_TEST_CASE(JsonGetArrayTest, JsonGetArrayTestTypes);
+TYPED_TEST_SUITE(JsonGetArrayTest, JsonGetArrayTestTypes, );
 
 TYPED_TEST(JsonGetArrayTest, Explicit)
 {
@@ -200,7 +200,7 @@
 };
 
 typedef ::testing::Types<std::string, std::string> JsonGetStringTestTypes;
-TYPED_TEST_CASE(JsonGetStringTest, JsonGetStringTestTypes);
+TYPED_TEST_SUITE(JsonGetStringTest, JsonGetStringTestTypes, );
 
 TYPED_TEST(JsonGetStringTest, Explicit)
 {
@@ -244,7 +244,7 @@
 };
 
 typedef ::testing::Types<bool, bool> JsonGetBooleanTestTypes;
-TYPED_TEST_CASE(JsonGetBooleanTest, JsonGetBooleanTestTypes);
+TYPED_TEST_SUITE(JsonGetBooleanTest, JsonGetBooleanTestTypes, );
 
 TYPED_TEST(JsonGetBooleanTest, Explicit)
 {
@@ -328,7 +328,7 @@
 #endif
     > JsonGetIntegerTestTypes;
 
-TYPED_TEST_CASE(JsonGetIntegerTest, JsonGetIntegerTestTypes);
+TYPED_TEST_SUITE(JsonGetIntegerTest, JsonGetIntegerTestTypes, );
 
 TYPED_TEST(JsonGetIntegerTest, Explicit)
 {
@@ -381,7 +381,7 @@
 typedef ::testing::Types<double, float, double>
     JsonGetFloatTestTypes;
 
-TYPED_TEST_CASE(JsonGetFloatTest, JsonGetFloatTestTypes);
+TYPED_TEST_SUITE(JsonGetFloatTest, JsonGetFloatTestTypes, );
 
 TYPED_TEST(JsonGetFloatTest, Explicit)
 {
diff --git a/wpiutil/src/test/native/cpp/json/unit-deserialization.cpp b/wpiutil/src/test/native/cpp/json/unit-deserialization.cpp
index 12ac280..2505ef5 100644
--- a/wpiutil/src/test/native/cpp/json/unit-deserialization.cpp
+++ b/wpiutil/src/test/native/cpp/json/unit-deserialization.cpp
@@ -133,6 +133,6 @@
     "\"\x7F\xF4\x7F",
 };
 
-INSTANTIATE_TEST_CASE_P(JsonDeserializationErrorTests,
+INSTANTIATE_TEST_SUITE_P(JsonDeserializationErrorTests,
                         JsonDeserializationErrorTest,
-                        ::testing::ValuesIn(error_cases), );
+                        ::testing::ValuesIn(error_cases));
diff --git a/wpiutil/src/test/native/cpp/json/unit-msgpack.cpp b/wpiutil/src/test/native/cpp/json/unit-msgpack.cpp
index 0c03ee0..c0a237d 100644
--- a/wpiutil/src/test/native/cpp/json/unit-msgpack.cpp
+++ b/wpiutil/src/test/native/cpp/json/unit-msgpack.cpp
@@ -253,8 +253,8 @@
     4294967295u,
 };
 
-INSTANTIATE_TEST_CASE_P(MessagePackSignedPos4Tests, MessagePackSignedPos4Test,
-                        ::testing::ValuesIn(pos4_numbers), );
+INSTANTIATE_TEST_SUITE_P(MessagePackSignedPos4Tests, MessagePackSignedPos4Test,
+                        ::testing::ValuesIn(pos4_numbers));
 
 // 4294967296..9223372036854775807 (int 64)
 class MessagePackSignedPos8Test : public ::testing::TestWithParam<uint64_t> {};
@@ -306,8 +306,8 @@
     9223372036854775807lu,
 };
 
-INSTANTIATE_TEST_CASE_P(MessagePackSignedPos8Tests, MessagePackSignedPos8Test,
-                        ::testing::ValuesIn(pos8_numbers), );
+INSTANTIATE_TEST_SUITE_P(MessagePackSignedPos8Tests, MessagePackSignedPos8Test,
+                        ::testing::ValuesIn(pos8_numbers));
 
 // -128..-33 (int 8)
 TEST(MessagePackSignedTest, Neg1)
@@ -419,8 +419,8 @@
     -2147483648ll,
 };
 
-INSTANTIATE_TEST_CASE_P(MessagePackSignedNeg4Tests, MessagePackSignedNeg4Test,
-                        ::testing::ValuesIn(neg4_numbers), );
+INSTANTIATE_TEST_SUITE_P(MessagePackSignedNeg4Tests, MessagePackSignedNeg4Test,
+                        ::testing::ValuesIn(neg4_numbers));
 
 // -9223372036854775808..-2147483649 (int 64)
 class MessagePackSignedNeg8Test : public ::testing::TestWithParam<int64_t> {};
@@ -470,8 +470,8 @@
     -2147483649ll,
 };
 
-INSTANTIATE_TEST_CASE_P(MessagePackSignedNeg8Tests, MessagePackSignedNeg8Test,
-                        ::testing::ValuesIn(neg8_numbers), );
+INSTANTIATE_TEST_SUITE_P(MessagePackSignedNeg8Tests, MessagePackSignedNeg8Test,
+                        ::testing::ValuesIn(neg8_numbers));
 
 // 0..127 (positive fixnum)
 TEST(MessagePackUnsignedTest, Pos0)
@@ -605,9 +605,9 @@
     EXPECT_EQ(json::from_msgpack(result), j);
 }
 
-INSTANTIATE_TEST_CASE_P(MessagePackUnsignedPos4Tests,
+INSTANTIATE_TEST_SUITE_P(MessagePackUnsignedPos4Tests,
                         MessagePackUnsignedPos4Test,
-                        ::testing::ValuesIn(pos4_numbers), );
+                        ::testing::ValuesIn(pos4_numbers));
 
 // 4294967296..18446744073709551615 (uint 64)
 class MessagePackUnsignedPos8Test : public ::testing::TestWithParam<uint64_t> {};
@@ -652,9 +652,9 @@
     EXPECT_EQ(json::from_msgpack(result), j);
 }
 
-INSTANTIATE_TEST_CASE_P(MessagePackUnsignedPos8Tests,
+INSTANTIATE_TEST_SUITE_P(MessagePackUnsignedPos8Tests,
                         MessagePackUnsignedPos8Test,
-                        ::testing::ValuesIn(pos8_numbers), );
+                        ::testing::ValuesIn(pos8_numbers));
 
 // 3.1415925
 TEST(MessagePackFloatTest, Number)
@@ -786,8 +786,8 @@
     65535u
 };
 
-INSTANTIATE_TEST_CASE_P(MessagePackString3Tests, MessagePackString3Test,
-                        ::testing::ValuesIn(string3_lens), );
+INSTANTIATE_TEST_SUITE_P(MessagePackString3Tests, MessagePackString3Test,
+                        ::testing::ValuesIn(string3_lens));
 
 
 // N = 65536..4294967295
@@ -827,8 +827,8 @@
     1048576u
 };
 
-INSTANTIATE_TEST_CASE_P(MessagePackString5Tests, MessagePackString5Test,
-                        ::testing::ValuesIn(string5_lens), );
+INSTANTIATE_TEST_SUITE_P(MessagePackString5Tests, MessagePackString5Test,
+                        ::testing::ValuesIn(string5_lens));
 
 TEST(MessagePackArrayTest, Empty)
 {
@@ -1264,6 +1264,6 @@
     "test/data/nst_json_testsuite/test_parsing/y_structure_whitespace_array.json",
 };
 
-INSTANTIATE_TEST_CASE_P(MessagePackRegressionTests, MessagePackRegressionTest,
+INSTANTIATE_TEST_SUITE_P(MessagePackRegressionTests, MessagePackRegressionTest,
                         ::testing::ValuesIn(regression_test_cases));
 #endif
diff --git a/wpiutil/src/test/native/cpp/llvm/FunctionExtrasTest.cpp b/wpiutil/src/test/native/cpp/llvm/FunctionExtrasTest.cpp
new file mode 100644
index 0000000..5dcd11f
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/llvm/FunctionExtrasTest.cpp
@@ -0,0 +1,228 @@
+//===- FunctionExtrasTest.cpp - Unit tests for function type erasure ------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/FunctionExtras.h"
+#include "gtest/gtest.h"
+
+#include <memory>
+
+using namespace wpi;
+
+namespace {
+
+TEST(UniqueFunctionTest, Basic) {
+  unique_function<int(int, int)> Sum = [](int A, int B) { return A + B; };
+  EXPECT_EQ(Sum(1, 2), 3);
+
+  unique_function<int(int, int)> Sum2 = std::move(Sum);
+  EXPECT_EQ(Sum2(1, 2), 3);
+
+  unique_function<int(int, int)> Sum3 = [](int A, int B) { return A + B; };
+  Sum2 = std::move(Sum3);
+  EXPECT_EQ(Sum2(1, 2), 3);
+
+  Sum2 = unique_function<int(int, int)>([](int A, int B) { return A + B; });
+  EXPECT_EQ(Sum2(1, 2), 3);
+
+  // Explicit self-move test.
+  *&Sum2 = std::move(Sum2);
+  EXPECT_EQ(Sum2(1, 2), 3);
+
+  Sum2 = unique_function<int(int, int)>();
+  EXPECT_FALSE(Sum2);
+
+  // Make sure we can forward through l-value reference parameters.
+  unique_function<void(int &)> Inc = [](int &X) { ++X; };
+  int X = 42;
+  Inc(X);
+  EXPECT_EQ(X, 43);
+
+  // Make sure we can forward through r-value reference parameters with
+  // move-only types.
+  unique_function<int(std::unique_ptr<int> &&)> ReadAndDeallocByRef =
+      [](std::unique_ptr<int> &&Ptr) {
+        int V = *Ptr;
+        Ptr.reset();
+        return V;
+      };
+  std::unique_ptr<int> Ptr{new int(13)};
+  EXPECT_EQ(ReadAndDeallocByRef(std::move(Ptr)), 13);
+  EXPECT_FALSE((bool)Ptr);
+
+  // Make sure we can pass a move-only temporary as opposed to a local variable.
+  EXPECT_EQ(ReadAndDeallocByRef(std::unique_ptr<int>(new int(42))), 42);
+
+  // Make sure we can pass a move-only type by-value.
+  unique_function<int(std::unique_ptr<int>)> ReadAndDeallocByVal =
+      [](std::unique_ptr<int> Ptr) {
+        int V = *Ptr;
+        Ptr.reset();
+        return V;
+      };
+  Ptr.reset(new int(13));
+  EXPECT_EQ(ReadAndDeallocByVal(std::move(Ptr)), 13);
+  EXPECT_FALSE((bool)Ptr);
+
+  EXPECT_EQ(ReadAndDeallocByVal(std::unique_ptr<int>(new int(42))), 42);
+}
+
+TEST(UniqueFunctionTest, Captures) {
+  long A = 1, B = 2, C = 3, D = 4, E = 5;
+
+  unique_function<long()> Tmp;
+
+  unique_function<long()> C1 = [A]() { return A; };
+  EXPECT_EQ(C1(), 1);
+  Tmp = std::move(C1);
+  EXPECT_EQ(Tmp(), 1);
+
+  unique_function<long()> C2 = [A, B]() { return A + B; };
+  EXPECT_EQ(C2(), 3);
+  Tmp = std::move(C2);
+  EXPECT_EQ(Tmp(), 3);
+
+  unique_function<long()> C3 = [A, B, C]() { return A + B + C; };
+  EXPECT_EQ(C3(), 6);
+  Tmp = std::move(C3);
+  EXPECT_EQ(Tmp(), 6);
+
+  unique_function<long()> C4 = [A, B, C, D]() { return A + B + C + D; };
+  EXPECT_EQ(C4(), 10);
+  Tmp = std::move(C4);
+  EXPECT_EQ(Tmp(), 10);
+
+  unique_function<long()> C5 = [A, B, C, D, E]() { return A + B + C + D + E; };
+  EXPECT_EQ(C5(), 15);
+  Tmp = std::move(C5);
+  EXPECT_EQ(Tmp(), 15);
+}
+
+TEST(UniqueFunctionTest, MoveOnly) {
+  struct SmallCallable {
+    std::unique_ptr<int> A{new int(1)};
+
+    int operator()(int B) { return *A + B; }
+  };
+  unique_function<int(int)> Small = SmallCallable();
+  EXPECT_EQ(Small(2), 3);
+  unique_function<int(int)> Small2 = std::move(Small);
+  EXPECT_EQ(Small2(2), 3);
+
+  struct LargeCallable {
+    std::unique_ptr<int> A{new int(1)};
+    std::unique_ptr<int> B{new int(2)};
+    std::unique_ptr<int> C{new int(3)};
+    std::unique_ptr<int> D{new int(4)};
+    std::unique_ptr<int> E{new int(5)};
+
+    int operator()() { return *A + *B + *C + *D + *E; }
+  };
+  unique_function<int()> Large = LargeCallable();
+  EXPECT_EQ(Large(), 15);
+  unique_function<int()> Large2 = std::move(Large);
+  EXPECT_EQ(Large2(), 15);
+}
+
+TEST(UniqueFunctionTest, CountForwardingCopies) {
+  struct CopyCounter {
+    int &CopyCount;
+
+    CopyCounter(int &CopyCount) : CopyCount(CopyCount) {}
+    CopyCounter(const CopyCounter &Arg) : CopyCount(Arg.CopyCount) {
+      ++CopyCount;
+    }
+  };
+
+  unique_function<void(CopyCounter)> ByValF = [](CopyCounter) {};
+  int CopyCount = 0;
+  ByValF(CopyCounter(CopyCount));
+  EXPECT_EQ(1, CopyCount);
+
+  CopyCount = 0;
+  {
+    CopyCounter Counter{CopyCount};
+    ByValF(Counter);
+  }
+  EXPECT_EQ(2, CopyCount);
+
+  // Check that we don't generate a copy at all when we can bind a reference all
+  // the way down, even if that reference could *in theory* allow copies.
+  unique_function<void(const CopyCounter &)> ByRefF = [](const CopyCounter &) {
+  };
+  CopyCount = 0;
+  ByRefF(CopyCounter(CopyCount));
+  EXPECT_EQ(0, CopyCount);
+
+  CopyCount = 0;
+  {
+    CopyCounter Counter{CopyCount};
+    ByRefF(Counter);
+  }
+  EXPECT_EQ(0, CopyCount);
+
+  // If we use a reference, we can make a stronger guarantee that *no* copy
+  // occurs.
+  struct Uncopyable {
+    Uncopyable() = default;
+    Uncopyable(const Uncopyable &) = delete;
+  };
+  unique_function<void(const Uncopyable &)> UncopyableF =
+      [](const Uncopyable &) {};
+  UncopyableF(Uncopyable());
+  Uncopyable X;
+  UncopyableF(X);
+}
+
+TEST(UniqueFunctionTest, CountForwardingMoves) {
+  struct MoveCounter {
+    int &MoveCount;
+
+    MoveCounter(int &MoveCount) : MoveCount(MoveCount) {}
+    MoveCounter(MoveCounter &&Arg) : MoveCount(Arg.MoveCount) { ++MoveCount; }
+  };
+
+  unique_function<void(MoveCounter)> ByValF = [](MoveCounter) {};
+  int MoveCount = 0;
+  ByValF(MoveCounter(MoveCount));
+  EXPECT_EQ(1, MoveCount);
+
+  MoveCount = 0;
+  {
+    MoveCounter Counter{MoveCount};
+    ByValF(std::move(Counter));
+  }
+  EXPECT_EQ(2, MoveCount);
+
+  // Check that when we use an r-value reference we get no spurious copies.
+  unique_function<void(MoveCounter &&)> ByRefF = [](MoveCounter &&) {};
+  MoveCount = 0;
+  ByRefF(MoveCounter(MoveCount));
+  EXPECT_EQ(0, MoveCount);
+
+  MoveCount = 0;
+  {
+    MoveCounter Counter{MoveCount};
+    ByRefF(std::move(Counter));
+  }
+  EXPECT_EQ(0, MoveCount);
+
+  // If we use an r-value reference we can in fact make a stronger guarantee
+  // with an unmovable type.
+  struct Unmovable {
+    Unmovable() = default;
+    Unmovable(Unmovable &&) = delete;
+  };
+  unique_function<void(const Unmovable &)> UnmovableF = [](const Unmovable &) {
+  };
+  UnmovableF(Unmovable());
+  Unmovable X;
+  UnmovableF(X);
+}
+
+} // anonymous namespace
diff --git a/wpiutil/src/test/native/cpp/priority_mutex_test.cpp b/wpiutil/src/test/native/cpp/priority_mutex_test.cpp
index c5c86b6..448a757 100644
--- a/wpiutil/src/test/native/cpp/priority_mutex_test.cpp
+++ b/wpiutil/src/test/native/cpp/priority_mutex_test.cpp
@@ -1,5 +1,5 @@
 /*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
+/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
 /* Open Source Software - may be modified and shared by FRC teams. The code   */
 /* must be accompanied by the FIRST BSD license file in the root directory of */
 /* the project.                                                               */
@@ -25,14 +25,14 @@
  public:
   // Efficiently waits until the Notification has been notified once.
   void Wait() {
-    std::unique_lock<priority_mutex> lock(m_mutex);
+    std::unique_lock lock(m_mutex);
     while (!m_set) {
       m_condition.wait(lock);
     }
   }
   // Sets the condition to true, and wakes all waiting threads.
   void Notify() {
-    std::lock_guard<priority_mutex> lock(m_mutex);
+    std::scoped_lock lock(m_mutex);
     m_set = true;
     m_condition.notify_all();
   }
diff --git a/wpiutil/src/test/native/cpp/spinlock_bench.cpp b/wpiutil/src/test/native/cpp/spinlock_bench.cpp
index 2280a44..b36e558 100644
--- a/wpiutil/src/test/native/cpp/spinlock_bench.cpp
+++ b/wpiutil/src/test/native/cpp/spinlock_bench.cpp
@@ -1,5 +1,5 @@
 /*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
 /* Open Source Software - may be modified and shared by FRC teams. The code   */
 /* must be accompanied by the FIRST BSD license file in the root directory of */
 /* the project.                                                               */
@@ -35,7 +35,7 @@
 
     auto start = high_resolution_clock::now();
     for (int i = 0; i < 10000000; i++) {
-      std::lock_guard<std::mutex> lock(std_mutex);
+      std::scoped_lock lock(std_mutex);
       ++value;
     }
     auto stop = high_resolution_clock::now();
@@ -49,7 +49,7 @@
 
     auto start = high_resolution_clock::now();
     for (int i = 0; i < 1000000; i++) {
-      std::lock_guard<std::mutex> lock(std_mutex);
+      std::scoped_lock lock(std_mutex);
       ++value;
     }
     auto stop = high_resolution_clock::now();
@@ -64,7 +64,7 @@
 
     auto start = high_resolution_clock::now();
     for (int i = 0; i < 1000000; i++) {
-      std::lock_guard<std::recursive_mutex> lock(std_recursive_mutex);
+      std::scoped_lock lock(std_recursive_mutex);
       ++value;
     }
     auto stop = high_resolution_clock::now();
@@ -79,7 +79,7 @@
 
     auto start = high_resolution_clock::now();
     for (int i = 0; i < 1000000; i++) {
-      std::lock_guard<wpi::mutex> lock(wpi_mutex);
+      std::scoped_lock lock(wpi_mutex);
       ++value;
     }
     auto stop = high_resolution_clock::now();
@@ -94,7 +94,7 @@
 
     auto start = high_resolution_clock::now();
     for (int i = 0; i < 1000000; i++) {
-      std::lock_guard<wpi::recursive_mutex> lock(wpi_recursive_mutex);
+      std::scoped_lock lock(wpi_recursive_mutex);
       ++value;
     }
     auto stop = high_resolution_clock::now();
@@ -109,7 +109,7 @@
 
     auto start = high_resolution_clock::now();
     for (int i = 0; i < 1000000; i++) {
-      std::lock_guard<wpi::spinlock> lock(spinlock);
+      std::scoped_lock lock(spinlock);
       ++value;
     }
     auto stop = high_resolution_clock::now();
@@ -124,7 +124,7 @@
 
     auto start = high_resolution_clock::now();
     for (int i = 0; i < 1000000; i++) {
-      std::lock_guard<wpi::recursive_spinlock1> lock(recursive_spinlock1);
+      std::scoped_lock lock(recursive_spinlock1);
       ++value;
     }
     auto stop = high_resolution_clock::now();
@@ -139,7 +139,7 @@
 
     auto start = high_resolution_clock::now();
     for (int i = 0; i < 1000000; i++) {
-      std::lock_guard<wpi::recursive_spinlock2> lock(recursive_spinlock2);
+      std::scoped_lock lock(recursive_spinlock2);
       ++value;
     }
     auto stop = high_resolution_clock::now();
@@ -154,7 +154,7 @@
 
     auto start = high_resolution_clock::now();
     for (int i = 0; i < 1000000; i++) {
-      std::lock_guard<wpi::recursive_spinlock> lock(recursive_spinlock);
+      std::scoped_lock lock(recursive_spinlock);
       ++value;
     }
     auto stop = high_resolution_clock::now();
diff --git a/wpiutil/src/test/native/cpp/test_optional.cpp b/wpiutil/src/test/native/cpp/test_optional.cpp
deleted file mode 100644
index e64f0c2..0000000
--- a/wpiutil/src/test/native/cpp/test_optional.cpp
+++ /dev/null
@@ -1,1523 +0,0 @@
-// Copyright (C) 2011 - 2017 Andrzej Krzemienski.
-//
-// Use, modification, and distribution is subject to the Boost Software
-// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-//
-// The idea and interface is based on Boost.Optional library
-// authored by Fernando Luis Cacciola Carballal
-
-# include "wpi/optional.h"
-
-#include "gtest/gtest.h"
-
-# include <vector>
-# include <iostream>
-# include <functional>
-# include <complex>
-
-
-
-enum  State 
-{
-    sDefaultConstructed,
-    sValueCopyConstructed,
-    sValueMoveConstructed,
-    sCopyConstructed,
-    sMoveConstructed,
-    sMoveAssigned,
-    sCopyAssigned,
-    sValueCopyAssigned,
-    sValueMoveAssigned,
-    sMovedFrom,
-    sValueConstructed
-};
-
-struct OracleVal
-{
-    State s;
-    int i;
-    OracleVal(int i = 0) : s(sValueConstructed), i(i) {}
-};
-
-struct Oracle
-{
-    State s;
-    OracleVal val;
-
-    Oracle() : s(sDefaultConstructed) {}
-    Oracle(const OracleVal& v) : s(sValueCopyConstructed), val(v) {}
-    Oracle(OracleVal&& v) : s(sValueMoveConstructed), val(std::move(v)) {v.s = sMovedFrom;}
-    Oracle(const Oracle& o) : s(sCopyConstructed), val(o.val) {}
-    Oracle(Oracle&& o) : s(sMoveConstructed), val(std::move(o.val)) {o.s = sMovedFrom;}
-
-    Oracle& operator=(const OracleVal& v) { s = sValueCopyConstructed; val = v; return *this; }
-    Oracle& operator=(OracleVal&& v) { s = sValueMoveConstructed; val = std::move(v); v.s = sMovedFrom; return *this; }
-    Oracle& operator=(const Oracle& o) { s = sCopyConstructed; val = o.val; return *this; }
-    Oracle& operator=(Oracle&& o) { s = sMoveConstructed; val = std::move(o.val); o.s = sMovedFrom; return *this; }
-};
-
-struct Guard
-{
-    std::string val;
-    Guard() : val{} {}
-    explicit Guard(std::string s, int = 0) : val(s) {}
-    Guard(const Guard&) = delete;
-    Guard(Guard&&) = delete;
-    void operator=(const Guard&) = delete;
-    void operator=(Guard&&) = delete;
-};
-
-struct ExplicitStr
-{
-    std::string s;
-    explicit ExplicitStr(const char* chp) : s(chp) {};
-};
-
-struct Date
-{
-    int i;
-    Date() = delete;
-    Date(int i) : i{i} {};
-    Date(Date&& d) : i(d.i) { d.i = 0; }
-    Date(const Date&) = delete;
-    Date& operator=(const Date&) = delete;
-    Date& operator=(Date&& d) { i = d.i; d.i = 0; return *this;};
-};
-
-bool operator==( Oracle const& a, Oracle const& b ) { return a.val.i == b.val.i; }
-bool operator!=( Oracle const& a, Oracle const& b ) { return a.val.i != b.val.i; }
-
-
-namespace tr2 = wpi;
-
-
-TEST(Optional, disengaged_ctor)
-{
-    tr2::optional<int> o1;
-    assert (!o1);
-
-    tr2::optional<int> o2 = tr2::nullopt;
-    assert (!o2);
-
-    tr2::optional<int> o3 = o2;
-    assert (!o3);
-
-    assert (o1 == tr2::nullopt);
-    assert (o1 == tr2::optional<int>{});
-    assert (!o1);
-    assert (bool(o1) == false);
-
-    assert (o2 == tr2::nullopt);
-    assert (o2 == tr2::optional<int>{});
-    assert (!o2);
-    assert (bool(o2) == false);
-
-    assert (o3 == tr2::nullopt);
-    assert (o3 == tr2::optional<int>{});
-    assert (!o3);
-    assert (bool(o3) == false);
-
-    assert (o1 == o2);
-    assert (o2 == o1);
-    assert (o1 == o3);
-    assert (o3 == o1);
-    assert (o2 == o3);
-    assert (o3 == o2);
-}
-
-
-TEST(Optional, value_ctor)
-{
-  OracleVal v;
-  tr2::optional<Oracle> oo1(v);
-  assert (oo1 != tr2::nullopt);
-  assert (oo1 != tr2::optional<Oracle>{});
-  assert (oo1 == tr2::optional<Oracle>{v});
-  assert (!!oo1);
-  assert (bool(oo1));
-  // NA: assert (oo1->s == sValueCopyConstructed);
-  assert (oo1->s == sMoveConstructed);
-  assert (v.s == sValueConstructed);
-  
-  tr2::optional<Oracle> oo2(std::move(v));
-  assert (oo2 != tr2::nullopt);
-  assert (oo2 != tr2::optional<Oracle>{});
-  assert (oo2 == oo1);
-  assert (!!oo2);
-  assert (bool(oo2));
-  // NA: assert (oo2->s == sValueMoveConstructed);
-  assert (oo2->s == sMoveConstructed);
-  assert (v.s == sMovedFrom);
-
-  {
-      OracleVal v;
-      tr2::optional<Oracle> oo1{tr2::in_place, v};
-      assert (oo1 != tr2::nullopt);
-      assert (oo1 != tr2::optional<Oracle>{});
-      assert (oo1 == tr2::optional<Oracle>{v});
-      assert (!!oo1);
-      assert (bool(oo1));
-      assert (oo1->s == sValueCopyConstructed);
-      assert (v.s == sValueConstructed);
-
-      tr2::optional<Oracle> oo2{tr2::in_place, std::move(v)};
-      assert (oo2 != tr2::nullopt);
-      assert (oo2 != tr2::optional<Oracle>{});
-      assert (oo2 == oo1);
-      assert (!!oo2);
-      assert (bool(oo2));
-      assert (oo2->s == sValueMoveConstructed);
-      assert (v.s == sMovedFrom);
-  }
-}
-
-
-TEST(Optional, assignment)
-{
-    tr2::optional<int> oi;
-    oi = tr2::optional<int>{1};
-    assert (*oi == 1);
-
-    oi = tr2::nullopt;
-    assert (!oi);
-
-    oi = 2;
-    assert (*oi == 2);
-
-    oi = {};
-    assert (!oi);
-}
-
-
-template <class T>
-struct MoveAware
-{
-  T val;
-  bool moved;
-  MoveAware(T val) : val(val), moved(false) {}
-  MoveAware(MoveAware const&) = delete;
-  MoveAware(MoveAware&& rhs) : val(rhs.val), moved(rhs.moved) {
-    rhs.moved = true;
-  }
-  MoveAware& operator=(MoveAware const&) = delete;
-  MoveAware& operator=(MoveAware&& rhs) {
-    val = (rhs.val);
-    moved = (rhs.moved);
-    rhs.moved = true;
-    return *this;
-  }
-};
-
-TEST(Optional, moved_from_state)
-{
-  // first, test mock:
-  MoveAware<int> i{1}, j{2};
-  assert (i.val == 1);
-  assert (!i.moved);
-  assert (j.val == 2);
-  assert (!j.moved);
-  
-  MoveAware<int> k = std::move(i);
-  assert (k.val == 1);
-  assert (!k.moved);
-  assert (i.val == 1);
-  assert (i.moved);
-  
-  k = std::move(j);
-  assert (k.val == 2);
-  assert (!k.moved);
-  assert (j.val == 2);
-  assert (j.moved);
-  
-  // now, test optional
-  tr2::optional<MoveAware<int>> oi{1}, oj{2};
-  assert (oi);
-  assert (!oi->moved);
-  assert (oj);
-  assert (!oj->moved);
-  
-  tr2::optional<MoveAware<int>> ok = std::move(oi);
-  assert (ok);
-  assert (!ok->moved);
-  assert (oi);
-  assert (oi->moved);
-  
-  ok = std::move(oj);
-  assert (ok);
-  assert (!ok->moved);
-  assert (oj);
-  assert (oj->moved);
-}
-
-
-TEST(Optional, copy_move_ctor_optional_int)
-{
-  tr2::optional<int> oi;
-  tr2::optional<int> oj = oi;
-  
-  assert (!oj);
-  assert (oj == oi);
-  assert (oj == tr2::nullopt);
-  assert (!bool(oj));
-  
-  oi = 1;
-  tr2::optional<int> ok = oi;
-  assert (!!ok);
-  assert (bool(ok));
-  assert (ok == oi);
-  assert (ok != oj);
-  assert (*ok == 1);
-  
-  tr2::optional<int> ol = std::move(oi);
-  assert (!!ol);
-  assert (bool(ol));
-  assert (ol == oi);
-  assert (ol != oj);
-  assert (*ol == 1);
-}
-
-
-TEST(Optional, optional_optional)
-{
-  tr2::optional<tr2::optional<int>> oi1 = tr2::nullopt;
-  assert (oi1 == tr2::nullopt);
-  assert (!oi1);
-  
-  {
-  tr2::optional<tr2::optional<int>> oi2 {tr2::in_place};
-  assert (oi2 != tr2::nullopt);
-  assert (bool(oi2));
-  assert (*oi2 == tr2::nullopt);
-  //assert (!(*oi2));
-  //std::cout << typeid(**oi2).name() << std::endl;
-  }
-  
-  {
-  tr2::optional<tr2::optional<int>> oi2 {tr2::in_place, tr2::nullopt};
-  assert (oi2 != tr2::nullopt);
-  assert (bool(oi2));
-  assert (*oi2 == tr2::nullopt);
-  assert (!*oi2);
-  }
-  
-  {
-  tr2::optional<tr2::optional<int>> oi2 {tr2::optional<int>{}};
-  assert (oi2 != tr2::nullopt);
-  assert (bool(oi2));
-  assert (*oi2 == tr2::nullopt);
-  assert (!*oi2);
-  }
-  
-  tr2::optional<int> oi;
-  auto ooi = tr2::make_optional(oi);
-  static_assert( std::is_same<tr2::optional<tr2::optional<int>>, decltype(ooi)>::value, "");
-
-}
-
-TEST(Optional, example_guard)
-{
-  using namespace tr2;
-  //FAILS: optional<Guard> ogx(Guard("res1")); 
-  //FAILS: optional<Guard> ogx = "res1"; 
-  //FAILS: optional<Guard> ogx("res1"); 
-  optional<Guard> oga;                     // Guard is non-copyable (and non-moveable)     
-  optional<Guard> ogb(in_place, "res1");   // initialzes the contained value with "res1"  
-  assert (bool(ogb));
-  assert (ogb->val == "res1");
-            
-  optional<Guard> ogc(in_place);           // default-constructs the contained value
-  assert (bool(ogc));
-  assert (ogc->val == "");
-
-  oga.emplace("res1");                     // initialzes the contained value with "res1"  
-  assert (bool(oga));
-  assert (oga->val == "res1");
-  
-  oga.emplace();                           // destroys the contained value and 
-                                           // default-constructs the new one
-  assert (bool(oga));
-  assert (oga->val == "");
-  
-  oga = nullopt;                        // OK: make disengaged the optional Guard
-  assert (!(oga));
-  //FAILS: ogb = {};                          // ERROR: Guard is not Moveable
-}
-
-
-void process(){}
-void process(int ){}
-void processNil(){}
-
-
-TEST(Optional, example1)
-{
-  using namespace tr2;
-  optional<int> oi;                 // create disengaged object
-  optional<int> oj = nullopt;          // alternative syntax
-  oi = oj;                          // assign disengaged object
-  optional<int> ok = oj;            // ok is disengaged
-
-  if (oi)  assert(false);           // 'if oi is engaged...'
-  if (!oi) assert(true);            // 'if oi is disengaged...'
-
-  if (oi != nullopt) assert(false);    // 'if oi is engaged...'
-  if (oi == nullopt) assert(true);     // 'if oi is disengaged...'
-
-  assert(oi == ok);                 // two disengaged optionals compare equal
-  
-  ///////////////////////////////////////////////////////////////////////////
-  optional<int> ol{1};              // ol is engaged; its contained value is 1
-  ok = 2;                           // ok becomes engaged; its contained value is 2
-  oj = ol;                          // oj becomes engaged; its contained value is 1
-
-  assert(oi != ol);                 // disengaged != engaged
-  assert(ok != ol);                 // different contained values
-  assert(oj == ol);                 // same contained value
-  assert(oi < ol);                  // disengaged < engaged
-  assert(ol < ok);                  // less by contained value
-  
-  /////////////////////////////////////////////////////////////////////////////
-  optional<int> om{1};              // om is engaged; its contained value is 1
-  optional<int> on = om;            // on is engaged; its contained value is 1
-  om = 2;                           // om is engaged; its contained value is 2
-  assert (on != om);                // on still contains 3. They are not pointers
-
-  /////////////////////////////////////////////////////////////////////////////
-  int i = *ol;                      // i obtains the value contained in ol
-  assert (i == 1);
-  *ol = 9;                          // the object contained in ol becomes 9
-  assert(*ol == 9);
-  assert(ol == make_optional(9));  
-  
-  ///////////////////////////////////
-  int p = 1;
-  optional<int> op = p;
-  assert(*op == 1);
-  p = 2;                         
-  assert(*op == 1);                 // value contained in op is separated from p
-
-  ////////////////////////////////
-  if (ol)                      
-    process(*ol);                   // use contained value if present
-  else
-    process();                      // proceed without contained value
-    
-  if (!om)   
-    processNil();
-  else  
-    process(*om);   
-  
-  /////////////////////////////////////////
-  process(ol.value_or(0));     // use 0 if ol is disengaged
-  
-  ////////////////////////////////////////////
-  ok = nullopt;                         // if ok was engaged calls T's dtor
-  oj = {};                           // assigns a temporary disengaged optional
-}
-
-
-TEST(Optional, example_guard2)
-{
-  using wpi::optional;
-  const optional<int> c = 4; 
-  int i = *c;                        // i becomes 4
-  assert (i == 4);
-  // FAILS: *c = i;                            // ERROR: cannot assign to const int&
-}
-
-
-TEST(Optional, example_ref)
-{
-  using namespace wpi;
-  int i = 1;
-  int j = 2;
-  optional<int&> ora;                 // disengaged optional reference to int
-  optional<int&> orb = i;             // contained reference refers to object i
-
-  *orb = 3;                          // i becomes 3
-  // FAILS: ora = j;                           // ERROR: optional refs do not have assignment from T
-  // FAILS: ora = {j};                         // ERROR: optional refs do not have copy/move assignment
-  // FAILS: ora = orb;                         // ERROR: no copy/move assignment
-  ora.emplace(j);                    // OK: contained reference refers to object j
-  ora.emplace(i);                    // OK: contained reference now refers to object i
-
-  ora = nullopt;                        // OK: ora becomes disengaged
-}
-
-
-template <typename T>
-T getValue( tr2::optional<T> newVal = tr2::nullopt, tr2::optional<T&> storeHere = tr2::nullopt )
-{
-  T cached{};
-  
-  if (newVal) {
-    cached = *newVal;
-    
-    if (storeHere) {
-      *storeHere = *newVal; // LEGAL: assigning T to T
-    }      
-  }
-  return cached;      
-}
-
-TEST(Optional, example_optional_arg)
-{
-  int iii = 0;
-  iii = getValue<int>(iii, iii);
-  iii = getValue<int>(iii);
-  iii = getValue<int>();
-  
-  {
-    using namespace wpi;
-    optional<Guard> grd1{in_place, "res1", 1};   // guard 1 initialized
-    optional<Guard> grd2;
-
-    grd2.emplace("res2", 2);                     // guard 2 initialized
-    grd1 = nullopt;                                 // guard 1 released
-
-  }                                              // guard 2 released (in dtor)
-}
-
-
-std::tuple<Date, Date, Date> getStartMidEnd() { return std::tuple<Date, Date, Date>{Date{1}, Date{2}, Date{3}}; }
-void run(Date const&, Date const&, Date const&) {}
-
-TEST(Optional, example_date)
-{
-  using namespace wpi;
-  optional<Date> start, mid, end;           // Date doesn't have default ctor (no good default date)
-
-  std::tie(start, mid, end) = getStartMidEnd();
-  run(*start, *mid, *end); 
-}
-
-
-wpi::optional<char> readNextChar(){ return{}; }
-
-void run(wpi::optional<std::string>) {}
-void run(std::complex<double>) {}
-
-
-template <class T>
-void assign_norebind(tr2::optional<T&>& optref, T& obj)
-{
-  if (optref) *optref = obj;
-  else        optref.emplace(obj);
-}
-
-template <typename T> void unused(T&&) {}
-
-TEST(Optional, example_conceptual_model)
-{
-  using namespace wpi;
-  
-  optional<int> oi = 0;
-  optional<int> oj = 1;
-  optional<int> ok = nullopt;
-
-  oi = 1;
-  oj = nullopt;
-  ok = 0;
-
-  unused(oi == nullopt);
-  unused(oj == 0);
-  unused(ok == 1);
-}
-
-TEST(Optional, example_rationale)
-{
-  using namespace wpi;
-  if (optional<char> ch = readNextChar()) {
-    unused(ch);
-    // ...
-  }
-  
-  //////////////////////////////////
-  optional<int> opt1 = nullopt; 
-  optional<int> opt2 = {}; 
-
-  opt1 = nullopt;
-  opt2 = {};
-
-  if (opt1 == nullopt) {}
-  if (!opt2) {}
-  if (opt2 == optional<int>{}) {}
-  
-  
-  
-  ////////////////////////////////
-
-  run(nullopt);            // pick the second overload
-  // FAILS: run({});              // ambiguous
-
-  if (opt1 == nullopt) {} // fine
-  // FAILS: if (opt2 == {}) {}   // ilegal
-  
-  ////////////////////////////////
-  assert (optional<unsigned>{}  < optional<unsigned>{0});
-  assert (optional<unsigned>{0} < optional<unsigned>{1});
-  assert (!(optional<unsigned>{}  < optional<unsigned>{}) );
-  assert (!(optional<unsigned>{1} < optional<unsigned>{1}));
-
-  assert (optional<unsigned>{}  != optional<unsigned>{0});
-  assert (optional<unsigned>{0} != optional<unsigned>{1});
-  assert (optional<unsigned>{}  == optional<unsigned>{} );
-  assert (optional<unsigned>{0} == optional<unsigned>{0});
-  
-  /////////////////////////////////
-  optional<int> o;
-  o = make_optional(1);         // copy/move assignment
-  o = 1;           // assignment from T
-  o.emplace(1);    // emplacement 
-  
-  ////////////////////////////////////
-  int isas = 0, i = 9;
-  optional<int&> asas = i;
-  assign_norebind(asas, isas);
-  
-  /////////////////////////////////////
-  ////tr2::optional<std::vector<int>> ov2 = {2, 3};
-  ////assert (bool(ov2));
-  ////assert ((*ov2)[1] == 3);
-  ////
-  ////////////////////////////////
-  ////std::vector<int> v = {1, 2, 4, 8};
-  ////optional<std::vector<int>> ov = {1, 2, 4, 8};
-
-  ////assert (v == *ov);
-  ////
-  ////ov = {1, 2, 4, 8};
-
-  ////std::allocator<int> a;
-  ////optional<std::vector<int>> ou { in_place, {1, 2, 4, 8}, a };
-
-  ////assert (ou == ov);
-
-  //////////////////////////////
-  // inconvenient syntax:
-  {
-    
-      tr2::optional<std::vector<int>> ov2{tr2::in_place, {2, 3}};
-    
-      assert (bool(ov2));
-      assert ((*ov2)[1] == 3);
-  
-      ////////////////////////////
-
-      std::vector<int> v = {1, 2, 4, 8};
-      optional<std::vector<int>> ov{tr2::in_place, {1, 2, 4, 8}};
-
-      assert (v == *ov);
-  
-      ov.emplace({1, 2, 4, 8});
-/*
-      std::allocator<int> a;
-      optional<std::vector<int>> ou { in_place, {1, 2, 4, 8}, a };
-
-      assert (ou == ov);
-*/
-  }
-
-  /////////////////////////////////
-  {
-  typedef int T;
-  optional<optional<T>> ot {in_place};
-  optional<optional<T>> ou {in_place, nullopt};
-  optional<optional<T>> ov {optional<T>{}};
-
-  (void) ot;
-  (void) ou;
-  
-  optional<int> oi;
-  auto ooi = make_optional(oi);
-  static_assert( std::is_same<optional<optional<int>>, decltype(ooi)>::value, "");
-  }
-}
-
-
-bool fun(std::string , wpi::optional<int> oi = wpi::nullopt) 
-{
-  return bool(oi);
-}
-
-TEST(Optional, example_converting_ctor)
-{
-  using namespace wpi;
-  
-  assert (true == fun("dog", 2));
-  assert (false == fun("dog"));
-  assert (false == fun("dog", nullopt)); // just to be explicit
-}
-
-
-TEST(Optional, bad_comparison)
-{
-  tr2::optional<int> oi, oj;
-  int i;
-  bool b = (oi == oj);
-  b = (oi >= i);
-  b = (oi == i);
-  unused(b);
-}
-
-
-//// NOT APPLICABLE ANYMORE
-////TEST(Optional, perfect_ctor)
-////{
-////  //tr2::optional<std::string> ois = "OS";
-////  assert (*ois == "OS");
-////  
-////  // FAILS: tr2::optional<ExplicitStr> oes = "OS"; 
-////  tr2::optional<ExplicitStr> oes{"OS"};
-////  assert (oes->s == "OS");
-////}
-
-TEST(Optional, value_or)
-{
-  tr2::optional<int> oi = 1;
-  int i = oi.value_or(0);
-  assert (i == 1);
-  
-  oi = tr2::nullopt;
-  assert (oi.value_or(3) == 3);
-  
-  tr2::optional<std::string> os{"AAA"};
-  assert (os.value_or("BBB") == "AAA");
-  os = {};
-  assert (os.value_or("BBB") == "BBB");
-}
-
-TEST(Optional, reset)
-{
-  using namespace wpi;
-  optional<int> oi {1};
-  oi.reset();
-  assert (!oi);
-
-  int i = 1;
-  optional<const int&> oir {i};
-  oir.reset();
-  assert (!oir);
-}
-
-TEST(Optional, mixed_order)
-{
-  using namespace wpi;
-  
-  optional<int> oN {nullopt};
-  optional<int> o0 {0};
-  optional<int> o1 {1};
-  
-  assert ( (oN <   0));
-  assert ( (oN <   1));
-  assert (!(o0 <   0));
-  assert ( (o0 <   1));
-  assert (!(o1 <   0));
-  assert (!(o1 <   1));
-  
-  assert (!(oN >=  0));
-  assert (!(oN >=  1));
-  assert ( (o0 >=  0));
-  assert (!(o0 >=  1));
-  assert ( (o1 >=  0));
-  assert ( (o1 >=  1));
-  
-  assert (!(oN >   0));
-  assert (!(oN >   1));
-  assert (!(o0 >   0));
-  assert (!(o0 >   1));
-  assert ( (o1 >   0));
-  assert (!(o1 >   1));
-  
-  assert ( (oN <=  0));
-  assert ( (oN <=  1));
-  assert ( (o0 <=  0));
-  assert ( (o0 <=  1));
-  assert (!(o1 <=  0));
-  assert ( (o1 <=  1));
-  
-  assert ( (0 >  oN));
-  assert ( (1 >  oN));
-  assert (!(0 >  o0));
-  assert ( (1 >  o0));
-  assert (!(0 >  o1));
-  assert (!(1 >  o1));
-  
-  assert (!(0 <= oN));
-  assert (!(1 <= oN));
-  assert ( (0 <= o0));
-  assert (!(1 <= o0));
-  assert ( (0 <= o1));
-  assert ( (1 <= o1));
-  
-  assert (!(0 <  oN));
-  assert (!(1 <  oN));
-  assert (!(0 <  o0));
-  assert (!(1 <  o0));
-  assert ( (0 <  o1));
-  assert (!(1 <  o1));
-  
-  assert ( (0 >= oN));
-  assert ( (1 >= oN));
-  assert ( (0 >= o0));
-  assert ( (1 >= o0));
-  assert (!(0 >= o1));
-  assert ( (1 >= o1));
-}
-
-struct BadRelops
-{
-  int i;
-};
-
-constexpr bool operator<(BadRelops a, BadRelops b) { return a.i < b.i; }
-constexpr bool operator>(BadRelops a, BadRelops b) { return a.i < b.i; } // intentional error!
-
-TEST(Optional, bad_relops)
-{
-  using namespace wpi;
-  BadRelops a{1}, b{2};
-  assert (a < b);
-  assert (a > b);
-  
-  optional<BadRelops> oa = a, ob = b;
-  assert (oa < ob);
-  assert (!(oa > ob));
-  
-  assert (oa < b);
-  assert (oa > b);
-  
-  optional<BadRelops&> ra = a, rb = b;
-  assert (ra < rb);
-  assert (!(ra > rb));
-  
-  assert (ra < b);
-  assert (ra > b);
-}
-
-
-TEST(Optional, mixed_equality)
-{
-  using namespace wpi;
-  
-  assert (make_optional(0) == 0);
-  assert (make_optional(1) == 1);
-  assert (make_optional(0) != 1);
-  assert (make_optional(1) != 0);
-  
-  optional<int> oN {nullopt};
-  optional<int> o0 {0};
-  optional<int> o1 {1};
-  
-  assert (o0 ==  0);
-  assert ( 0 == o0);
-  assert (o1 ==  1);
-  assert ( 1 == o1);
-  assert (o1 !=  0);
-  assert ( 0 != o1);
-  assert (o0 !=  1);
-  assert ( 1 != o0);
-  
-  assert ( 1 != oN);
-  assert ( 0 != oN);
-  assert (oN !=  1);
-  assert (oN !=  0);
-  assert (!( 1 == oN));
-  assert (!( 0 == oN));
-  assert (!(oN ==  1));
-  assert (!(oN ==  0));
-  
-  std::string cat{"cat"}, dog{"dog"};
-  optional<std::string> oNil{}, oDog{"dog"}, oCat{"cat"};
-  
-  assert (oCat ==  cat);
-  assert ( cat == oCat);
-  assert (oDog ==  dog);
-  assert ( dog == oDog);
-  assert (oDog !=  cat);
-  assert ( cat != oDog);
-  assert (oCat !=  dog);
-  assert ( dog != oCat);
-  
-  assert ( dog != oNil);
-  assert ( cat != oNil);
-  assert (oNil !=  dog);
-  assert (oNil !=  cat);
-  assert (!( dog == oNil));
-  assert (!( cat == oNil));
-  assert (!(oNil ==  dog));
-  assert (!(oNil ==  cat));
-}
-
-TEST(Optional, const_propagation)
-{
-  using namespace wpi;
-  
-  optional<int> mmi{0};
-  static_assert(std::is_same<decltype(*mmi), int&>::value, "WTF");
-  
-  const optional<int> cmi{0};
-  static_assert(std::is_same<decltype(*cmi), const int&>::value, "WTF");
-  
-  optional<const int> mci{0};
-  static_assert(std::is_same<decltype(*mci), const int&>::value, "WTF");
-  
-  optional<const int> cci{0};
-  static_assert(std::is_same<decltype(*cci), const int&>::value, "WTF");
-}
-
-
-static_assert(std::is_base_of<std::logic_error, wpi::bad_optional_access>::value, "");
-
-TEST(Optional, safe_value)
-{
-  using namespace wpi;
-  
-  try {
-    optional<int> ovN{}, ov1{1};
-    
-    int& r1 = ov1.value();
-    assert (r1 == 1);
-    
-    try { 
-      ovN.value();
-      assert (false);
-    }
-    catch (bad_optional_access const&) {
-    }
-    
-    { // ref variant
-      int i1 = 1;
-      optional<int&> orN{}, or1{i1};
-      
-      int& r2 = or1.value();
-      assert (r2 == 1);
-      
-      try { 
-        orN.value();
-        assert (false);
-      }
-      catch (bad_optional_access const&) {
-      }
-    }
-  }  
-  catch(...) {
-    assert (false);
-  }
-}
-
-TEST(Optional, optional_ref)
-{
-  using namespace tr2;
-  // FAILS: optional<int&&> orr;
-  // FAILS: optional<nullopt_t&> on;
-  int i = 8;
-  optional<int&> ori;
-  assert (!ori);
-  ori.emplace(i);
-  assert (bool(ori));
-  assert (*ori == 8);
-  assert (&*ori == &i);
-  *ori = 9;
-  assert (i == 9);
-  
-  // FAILS: int& ir = ori.value_or(i);
-  int ii = ori.value_or(i);
-  assert (ii == 9);
-  ii = 7;
-  assert (*ori == 9);
-  
-  int j = 22;
-  auto&& oj = make_optional(std::ref(j));
-  *oj = 23;
-  assert (&*oj == &j);
-  assert (j == 23);
-}
-
-TEST(Optional, optional_ref_const_propagation)
-{
-  using namespace wpi;
-  
-  int i = 9;
-  const optional<int&> mi = i;
-  int& r = *mi; 
-  optional<const int&> ci = i;
-  static_assert(std::is_same<decltype(*mi), int&>::value, "WTF");
-  static_assert(std::is_same<decltype(*ci), const int&>::value, "WTF");
-  
-  unused(r);
-}
-
-TEST(Optional, optional_ref_assign)
-{
-  using namespace wpi;
-  
-  int i = 9;
-  optional<int&> ori = i;
-  
-  int j = 1;
-  ori = optional<int&>{j};
-  ori = {j};
-  // FAILS: ori = j;
-  
-  optional<int&> orx = ori;
-  ori = orx;
-  
-  optional<int&> orj = j;
-  
-  assert (ori);
-  assert (*ori == 1);
-  assert (ori == orj);
-  assert (i == 9);
-  
-  *ori = 2;
-  assert (*ori == 2);
-  assert (ori == 2);
-  assert (2 == ori);
-  assert (ori != 3);
-  
-  assert (ori == orj);
-  assert (j == 2);
-  assert (i == 9);
-  
-  ori = {};
-  assert (!ori);
-  assert (ori != orj);
-  assert (j == 2);
-  assert (i == 9);
-}
-
-TEST(Optional, optional_swap)
-{
-  namespace tr2 = wpi;
-  tr2::optional<int> oi {1}, oj {};
-  swap(oi, oj);
-  assert (oj);
-  assert (*oj == 1);
-  assert (!oi);
-  static_assert(noexcept(swap(oi, oj)), "swap() is not noexcept");
-}
-
-
-TEST(Optional, optional_ref_swap)
-{
-  using namespace wpi;
-  int i = 0;
-  int j = 1;
-  optional<int&> oi = i;
-  optional<int&> oj = j;
-  
-  assert (&*oi == &i);
-  assert (&*oj == &j);
-  
-  swap(oi, oj);
-  assert (&*oi == &j);
-  assert (&*oj == &i);
-}
-
-TEST(Optional, optional_initialization)
-{
-    using namespace tr2;
-    using std::string;
-    string s = "STR";
-
-    optional<string> os{s};
-    optional<string> ot = s;
-    optional<string> ou{"STR"};
-    optional<string> ov = string{"STR"};
-    
-}
-
-#include <unordered_set>
-
-TEST(Optional, optional_hashing)
-{
-    using namespace tr2;
-    using std::string;
-    
-    std::hash<int> hi;
-    std::hash<optional<int>> hoi;
-    std::hash<string> hs;
-    std::hash<optional<string>> hos;
-    
-    assert (hi(0) == hoi(optional<int>{0}));
-    assert (hi(1) == hoi(optional<int>{1}));
-    assert (hi(3198) == hoi(optional<int>{3198}));
-    
-    assert (hs("") == hos(optional<string>{""}));
-    assert (hs("0") == hos(optional<string>{"0"}));
-    assert (hs("Qa1#") == hos(optional<string>{"Qa1#"}));
-    
-    std::unordered_set<optional<string>> set;
-    assert(set.find({"Qa1#"}) == set.end());
-    
-    set.insert({"0"});
-    assert(set.find({"Qa1#"}) == set.end());
-    
-    set.insert({"Qa1#"});
-    assert(set.find({"Qa1#"}) != set.end());
-}
-
-
-// optional_ref_emulation
-template <class T>
-struct generic
-{
-  typedef T type;
-};
-
-template <class U>
-struct generic<U&>
-{
-  typedef std::reference_wrapper<U> type;
-};
-
-template <class T>
-using Generic = typename generic<T>::type;
-
-template <class X>
-bool generic_fun()
-{
-  wpi::optional<Generic<X>> op;
-  return bool(op);
-}
-
-TEST(Optional, optional_ref_emulation)
-{
-  using namespace wpi;
-  optional<Generic<int>> oi = 1;
-  assert (*oi == 1);
-  
-  int i = 8;
-  int j = 4;
-  optional<Generic<int&>> ori {i};
-  assert (*ori == 8);
-  assert ((void*)&*ori != (void*)&i); // !DIFFERENT THAN optional<T&>
-
-  *ori = j;
-  assert (*ori == 4);
-}
-
-
-TEST(Optional, moved_on_value_or)
-{
-  using namespace tr2;
-  optional<Oracle> oo{in_place};
-  
-  assert (oo);
-  assert (oo->s == sDefaultConstructed);
-  
-  Oracle o = std::move(oo).value_or( Oracle{OracleVal{}} );
-  assert (oo);
-  assert (oo->s == sMovedFrom);
-  assert (o.s == sMoveConstructed);
-  
-  optional<MoveAware<int>> om {in_place, 1};
-  assert (om);
-  assert (om->moved == false);
-  
-  /*MoveAware<int> m =*/ std::move(om).value_or( MoveAware<int>{1} );
-  assert (om);
-  assert (om->moved == true);
-
-# if OPTIONAL_HAS_MOVE_ACCESSORS == 1  
-  {
-    Date d = optional<Date>{in_place, 1}.value();
-    assert (d.i); // to silence compiler warning
-	
-	Date d2 = *optional<Date>{in_place, 1};
-    assert (d2.i); // to silence compiler warning
-  }
-# endif
-}
-
-
-TEST(Optional, optional_ref_hashing)
-{
-    using namespace tr2;
-    using std::string;
-    
-    std::hash<int> hi;
-    std::hash<optional<int&>> hoi;
-    std::hash<string> hs;
-    std::hash<optional<string&>> hos;
-    
-    int i0 = 0;
-    int i1 = 1;
-    assert (hi(0) == hoi(optional<int&>{i0}));
-    assert (hi(1) == hoi(optional<int&>{i1}));
-    
-    string s{""};
-    string s0{"0"};
-    string sCAT{"CAT"};
-    assert (hs("") == hos(optional<string&>{s}));
-    assert (hs("0") == hos(optional<string&>{s0}));
-    assert (hs("CAT") == hos(optional<string&>{sCAT}));
-    
-    std::unordered_set<optional<string&>> set;
-    assert(set.find({sCAT}) == set.end());
-    
-    set.insert({s0});
-    assert(set.find({sCAT}) == set.end());
-    
-    set.insert({sCAT});
-    assert(set.find({sCAT}) != set.end());
-}
-
-struct Combined
-{
-  int m = 0;
-  int n = 1;
-  
-  constexpr Combined() : m{5}, n{6} {}
-  constexpr Combined(int m, int n) : m{m}, n{n} {}
-};
-
-struct Nasty
-{
-  int m = 0;
-  int n = 1;
-  
-  constexpr Nasty() : m{5}, n{6} {}
-  constexpr Nasty(int m, int n) : m{m}, n{n} {}
-  
-  int operator&() { return n; }
-  int operator&() const { return n; }
-};
-
-TEST(Optional, arrow_operator)
-{
-  using namespace wpi;
-  
-  optional<Combined> oc1{in_place, 1, 2};
-  assert (oc1);
-  assert (oc1->m == 1);
-  assert (oc1->n == 2);
-  
-  optional<Nasty> on{in_place, 1, 2};
-  assert (on);
-  assert (on->m == 1);
-  assert (on->n == 2);
-}
-
-TEST(Optional, arrow_wit_optional_ref)
-{
-  using namespace wpi;
-  
-  Combined c{1, 2};
-  optional<Combined&> oc = c;
-  assert (oc);
-  assert (oc->m == 1);
-  assert (oc->n == 2);
-  
-  Nasty n{1, 2};
-  Nasty m{3, 4};
-  Nasty p{5, 6};
-  
-  optional<Nasty&> on{n};
-  assert (on);
-  assert (on->m == 1);
-  assert (on->n == 2);
-  
-  on = {m};
-  assert (on);
-  assert (on->m == 3);
-  assert (on->n == 4);
-  
-  on.emplace(p);
-  assert (on);
-  assert (on->m == 5);
-  assert (on->n == 6);
-  
-  optional<Nasty&> om{in_place, n};
-  assert (om);
-  assert (om->m == 1);
-  assert (om->n == 2);
-}
-
-TEST(Optional, no_dangling_reference_in_value)
-{
-  // this mostly tests compiler warnings
-  using namespace wpi;
-  optional<int> oi {2};
-  unused (oi.value());
-  const optional<int> coi {3};
-  unused (coi.value());
-}
-
-struct CountedObject
-{
-  static int _counter;
-  bool _throw;
-  CountedObject(bool b) : _throw(b) { ++_counter; }
-  CountedObject(CountedObject const& rhs) : _throw(rhs._throw) { if (_throw) throw int(); }
-  ~CountedObject() { --_counter; }
-};
-
-int CountedObject::_counter = 0;
-
-TEST(Optional, exception_safety)
-{
-  using namespace wpi;
-  try {
-    optional<CountedObject> oo(in_place, true); // throw
-    optional<CountedObject> o1(oo);
-  }
-  catch(...)
-  {
-    //
-  }
-  assert(CountedObject::_counter == 0);
-  
-  try {
-    optional<CountedObject> oo(in_place, true); // throw
-    optional<CountedObject> o1(std::move(oo));  // now move
-  }
-  catch(...)
-  {
-    //
-  }
-  assert(CountedObject::_counter == 0);
-}
-
-TEST(Optional, nested_optional)
-{
-   using namespace wpi;
-	
-   optional<optional<optional<int>>> o1 {nullopt};
-   assert (!o1);
-    
-   optional<optional<optional<int>>> o2 {in_place, nullopt};
-   assert (o2);
-   assert (!*o2);
-    
-   optional<optional<optional<int>>> o3 (in_place, in_place, nullopt);
-   assert (o3);
-   assert (*o3);
-   assert (!**o3);
-}
-
-TEST(Optional, three_ways_of_having_value)
-{
-  using namespace wpi;
-  optional<int> oN, o1 (1);
-  
-  assert (!oN);
-  assert (!oN.has_value());
-  assert (oN == nullopt);
-  
-  assert (o1);
-  assert (o1.has_value());
-  assert (o1 != nullopt);
-  
-  assert (bool(oN) == oN.has_value());
-  assert (bool(o1) == o1.has_value());
-  
-  int i = 1;
-  optional<int&> rN, r1 (i);
-  
-  assert (!rN);
-  assert (!rN.has_value());
-  assert (rN == nullopt);
-  
-  assert (r1);
-  assert (r1.has_value());
-  assert (r1 != nullopt);
-  
-  assert (bool(rN) == rN.has_value());
-  assert (bool(r1) == r1.has_value());
-}
-
-//// constexpr tests
-
-// these 4 classes have different noexcept signatures in move operations
-struct NothrowBoth {
-  NothrowBoth(NothrowBoth&&) noexcept(true) {};
-  void operator=(NothrowBoth&&) noexcept(true) {};
-};
-struct NothrowCtor {
-  NothrowCtor(NothrowCtor&&) noexcept(true) {};
-  void operator=(NothrowCtor&&) noexcept(false) {};
-};
-struct NothrowAssign {
-  NothrowAssign(NothrowAssign&&) noexcept(false) {};
-  void operator=(NothrowAssign&&) noexcept(true) {};
-};
-struct NothrowNone {
-  NothrowNone(NothrowNone&&) noexcept(false) {};
-  void operator=(NothrowNone&&) noexcept(false) {};
-};
-
-void test_noexcept()
-{
-  {
-    tr2::optional<NothrowBoth> b1, b2;
-    static_assert(noexcept(tr2::optional<NothrowBoth>{tr2::constexpr_move(b1)}), "bad noexcept!");
-    static_assert(noexcept(b1 = tr2::constexpr_move(b2)), "bad noexcept!");
-  }
-  {
-    tr2::optional<NothrowCtor> c1, c2;
-    static_assert(noexcept(tr2::optional<NothrowCtor>{tr2::constexpr_move(c1)}), "bad noexcept!");
-    static_assert(!noexcept(c1 = tr2::constexpr_move(c2)), "bad noexcept!");
-  }
-  {
-    tr2::optional<NothrowAssign> a1, a2;
-    static_assert(!noexcept(tr2::optional<NothrowAssign>{tr2::constexpr_move(a1)}), "bad noexcept!");
-    static_assert(!noexcept(a1 = tr2::constexpr_move(a2)), "bad noexcept!");
-  }
-  {
-    tr2::optional<NothrowNone> n1, n2;
-    static_assert(!noexcept(tr2::optional<NothrowNone>{tr2::constexpr_move(n1)}), "bad noexcept!");
-    static_assert(!noexcept(n1 = tr2::constexpr_move(n2)), "bad noexcept!");
-  }
-}
-
-
-void constexpr_test_disengaged()
-{
-  constexpr tr2::optional<int> g0{};
-  constexpr tr2::optional<int> g1{tr2::nullopt};
-  static_assert( !g0, "initialized!" );
-  static_assert( !g1, "initialized!" );
-  
-  static_assert( bool(g1) == bool(g0), "ne!" );
-  
-  static_assert( g1 == g0, "ne!" );
-  static_assert( !(g1 != g0), "ne!" );
-  static_assert( g1 >= g0, "ne!" );
-  static_assert( !(g1 > g0), "ne!" );
-  static_assert( g1 <= g0, "ne!" );
-  static_assert( !(g1 <g0), "ne!" );
-  
-  static_assert( g1 == tr2::nullopt, "!" );
-  static_assert( !(g1 != tr2::nullopt), "!" );
-  static_assert( g1 <= tr2::nullopt, "!" );
-  static_assert( !(g1 < tr2::nullopt), "!" );
-  static_assert( g1 >= tr2::nullopt, "!" );
-  static_assert( !(g1 > tr2::nullopt), "!" );
-  
-  static_assert(  (tr2::nullopt == g0), "!" );
-  static_assert( !(tr2::nullopt != g0), "!" );
-  static_assert(  (tr2::nullopt >= g0), "!" );
-  static_assert( !(tr2::nullopt >  g0), "!" );
-  static_assert(  (tr2::nullopt <= g0), "!" );
-  static_assert( !(tr2::nullopt <  g0), "!" );
-  
-  static_assert(  (g1 != tr2::optional<int>(1)), "!" );
-  static_assert( !(g1 == tr2::optional<int>(1)), "!" );
-  static_assert(  (g1 <  tr2::optional<int>(1)), "!" );
-  static_assert(  (g1 <= tr2::optional<int>(1)), "!" );
-  static_assert( !(g1 >  tr2::optional<int>(1)), "!" );
-  static_assert( !(g1 >  tr2::optional<int>(1)), "!" );
-}
-
-
-constexpr tr2::optional<int> g0{};
-constexpr tr2::optional<int> g2{2};
-static_assert( g2, "not initialized!" );
-static_assert( *g2 == 2, "not 2!" );
-static_assert( g2 == tr2::optional<int>(2), "not 2!" );
-static_assert( g2 != g0, "eq!" );
-
-# if OPTIONAL_HAS_MOVE_ACCESSORS == 1
-static_assert( *tr2::optional<int>{3} == 3, "WTF!" );
-static_assert( tr2::optional<int>{3}.value() == 3, "WTF!" );
-static_assert( tr2::optional<int>{3}.value_or(1) == 3, "WTF!" );
-static_assert( tr2::optional<int>{}.value_or(4) == 4, "WTF!" );
-# endif
-
-constexpr tr2::optional<Combined> gc0{tr2::in_place};
-static_assert(gc0->n == 6, "WTF!");
-
-// optional refs
-int gi = 0;
-constexpr tr2::optional<int&> gori = gi;
-constexpr tr2::optional<int&> gorn{};
-constexpr int& gri = *gori;
-static_assert(gori, "WTF");
-static_assert(!gorn, "WTF");
-static_assert(gori != tr2::nullopt, "WTF");
-static_assert(gorn == tr2::nullopt, "WTF");
-static_assert(&gri == &*gori, "WTF");
-
-constexpr int gci = 1;
-constexpr tr2::optional<int const&> gorci = gci;
-constexpr tr2::optional<int const&> gorcn{};
-
-static_assert(gorcn <  gorci, "WTF");
-static_assert(gorcn <= gorci, "WTF");
-static_assert(gorci == gorci, "WTF");
-static_assert(*gorci == 1, "WTF");
-static_assert(gorci == gci, "WTF");
-
-namespace constexpr_optional_ref_and_arrow 
-{
-  using namespace wpi;
-  constexpr Combined c{1, 2};
-  constexpr optional<Combined const&> oc = c;
-  static_assert(oc, "WTF!");
-  static_assert(oc->m == 1, "WTF!");
-  static_assert(oc->n == 2, "WTF!");
-}
-
-#if OPTIONAL_HAS_CONSTEXPR_INIT_LIST
-
-namespace InitList
-{
-  using namespace wpi;
-  
-  struct ConstInitLister
-  {
-    template <typename T>
-	constexpr ConstInitLister(std::initializer_list<T> il) : len (il.size()) {}
-    size_t len;
-  };
-  
-  constexpr ConstInitLister CIL {2, 3, 4};
-  static_assert(CIL.len == 3, "WTF!");
-  
-  constexpr optional<ConstInitLister> oil {in_place, {4, 5, 6, 7}};
-  static_assert(oil, "WTF!");
-  static_assert(oil->len == 4, "WTF!");
-}
-
-#endif // OPTIONAL_HAS_CONSTEXPR_INIT_LIST
-
-// end constexpr tests
-
-
-#include <string>
-
-
-struct VEC
-{
-    std::vector<int> v;
-    template <typename... X>
-    VEC( X&&...x) : v(std::forward<X>(x)...) {}
-
-    template <typename U, typename... X>
-    VEC(std::initializer_list<U> il, X&&...x) : v(il, std::forward<X>(x)...) {}
-};
-
-
-
-TEST(Optional, Vector) {
-  tr2::optional<int> oi = 1;
-  assert (bool(oi));
-  oi.operator=({});
-  assert (!oi);
-
-  VEC v = {5, 6};
-
-  if (OPTIONAL_HAS_CONSTEXPR_INIT_LIST)
-    std::cout << "Optional has constexpr initializer_list" << std::endl;
-  else
-    std::cout << "Optional doesn't have constexpr initializer_list" << std::endl;
-
-  if (OPTIONAL_HAS_MOVE_ACCESSORS)
-    std::cout << "Optional has constexpr move accessors" << std::endl;
-  else
-    std::cout << "Optional doesn't have constexpr move accessors" << std::endl;	
-}
-
diff --git a/wpiutil/src/test/native/cpp/uv/UvLoopWalkTest.cpp b/wpiutil/src/test/native/cpp/uv/UvLoopWalkTest.cpp
index 78abf69..5d2e120 100644
--- a/wpiutil/src/test/native/cpp/uv/UvLoopWalkTest.cpp
+++ b/wpiutil/src/test/native/cpp/uv/UvLoopWalkTest.cpp
@@ -1,5 +1,5 @@
 /*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
 /* Open Source Software - may be modified and shared by FRC teams. The code   */
 /* must be accompanied by the FIRST BSD license file in the root directory of */
 /* the project.                                                               */
@@ -45,7 +45,7 @@
 
   timer->error.connect([](Error) { FAIL(); });
 
-  timer->timeout.connect([&, theTimer = timer.get() ] {
+  timer->timeout.connect([&, theTimer = timer.get()] {
     theTimer->GetLoopRef().Walk([&](Handle& it) {
       if (&it == timer.get()) seen_timer_handle++;
     });
diff --git a/wpiutil/src/test/native/cpp/uv/UvTimerTest.cpp b/wpiutil/src/test/native/cpp/uv/UvTimerTest.cpp
index 706e1eb..6e1bd5c 100644
--- a/wpiutil/src/test/native/cpp/uv/UvTimerTest.cpp
+++ b/wpiutil/src/test/native/cpp/uv/UvTimerTest.cpp
@@ -1,5 +1,5 @@
 /*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
 /* Open Source Software - may be modified and shared by FRC teams. The code   */
 /* must be accompanied by the FIRST BSD license file in the root directory of */
 /* the project.                                                               */
@@ -24,7 +24,7 @@
   handleRepeat->error.connect([](Error) { FAIL(); });
 
   handleNoRepeat->timeout.connect(
-      [&checkTimerNoRepeatEvent, handle = handleNoRepeat.get() ] {
+      [&checkTimerNoRepeatEvent, handle = handleNoRepeat.get()] {
         ASSERT_FALSE(checkTimerNoRepeatEvent);
         checkTimerNoRepeatEvent = true;
         handle->Stop();
@@ -33,7 +33,7 @@
       });
 
   handleRepeat->timeout.connect(
-      [&checkTimerRepeatEvent, handle = handleRepeat.get() ] {
+      [&checkTimerRepeatEvent, handle = handleRepeat.get()] {
         if (checkTimerRepeatEvent) {
           handle->Stop();
           handle->Close();