Merge commit 'ec04d9c03ddf12e46dca5348d4ca57b526e425f1'

This commit is contained in:
Mateusz Pusz
2023-06-14 21:13:07 +03:00
444 changed files with 18133 additions and 38078 deletions

View File

@@ -132,8 +132,11 @@ IncludeCategories:
# InsertTrailingCommas: None # InsertTrailingCommas: None
# IntegerLiteralSeparator: # IntegerLiteralSeparator:
# Binary: 0 # Binary: 0
# BinaryMinDigits: 0
# Decimal: 0 # Decimal: 0
# DecimalMinDigits: 0
# Hex: 0 # Hex: 0
# HexMinDigits: 0
# JavaScriptQuotes: Leave # JavaScriptQuotes: Leave
# JavaScriptWrapImports: true # JavaScriptWrapImports: true
# KeepEmptyLinesAtTheStartOfBlocks: false # KeepEmptyLinesAtTheStartOfBlocks: false

View File

@@ -35,7 +35,7 @@ env:
jobs: jobs:
build: build:
name: ${{ matrix.config.name }} ${{ matrix.build_type }} [downcast=${{ matrix.downcast_mode }}] name: ${{ matrix.config.name }} ${{ matrix.build_type }}
runs-on: ${{ matrix.config.os }} runs-on: ${{ matrix.config.os }}
strategy: strategy:
fail-fast: false fail-fast: false
@@ -190,7 +190,6 @@ jobs:
conan-config: "", conan-config: "",
} }
build_type: ["Release", "Debug"] build_type: ["Release", "Debug"]
downcast_mode: ["on", "auto"]
env: env:
CC: ${{ matrix.config.compiler.cc }} CC: ${{ matrix.config.compiler.cc }}
@@ -214,7 +213,7 @@ jobs:
- uses: hendrikmuhs/ccache-action@v1.2 - uses: hendrikmuhs/ccache-action@v1.2
if: runner.os == 'Linux' if: runner.os == 'Linux'
with: with:
key: ${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.downcast_mode }} key: ${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-${{ matrix.config.lib }}-${{ matrix.build_type }}
max-size: 50M max-size: 50M
- name: Install gcc-12 - name: Install gcc-12
if: matrix.config.compiler.type == 'GCC' && matrix.config.compiler.version == '12' if: matrix.config.compiler.type == 'GCC' && matrix.config.compiler.version == '12'
@@ -282,7 +281,7 @@ jobs:
run: | run: |
conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \ conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \
-b mp-units/* -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" \ -b mp-units/* -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" \
-o downcast_mode=${{ matrix.downcast_mode }} -c user.build:all=True -c user.build:skip_docs=True ${{ matrix.config.conan-config }} -c user.build:all=True -c user.build:skip_docs=True ${{ matrix.config.conan-config }}
- name: Obtain package reference - name: Obtain package reference
id: get-package-ref id: get-package-ref
shell: bash shell: bash

View File

@@ -36,8 +36,7 @@ vscode:
- vivaxy.vscode-conventional-commits - vivaxy.vscode-conventional-commits
- hbenl.vscode-test-explorer - hbenl.vscode-test-explorer
- matepek.vscode-catch2-test-adapter - matepek.vscode-catch2-test-adapter
- trond-snekvik.simple-rst - redhat.vscode-yaml
- lextudio.restructuredtext
- ritwickdey.liveserver - ritwickdey.liveserver
- ms-python.python - ms-python.python
@@ -60,7 +59,17 @@ tasks:
"restructuredtext.preview.scrollPreviewWithEditor": false, "restructuredtext.preview.scrollPreviewWithEditor": false,
"liveServer.settings.root": "/build/docs/docs/sphinx/", "liveServer.settings.root": "/build/docs/docs/sphinx/",
"esbonio.sphinx.confDir": "${workspaceFolder}/docs", "esbonio.sphinx.confDir": "${workspaceFolder}/docs",
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools" "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"yaml.schemas": {
"https://squidfunk.github.io/mkdocs-material/schema.json": "mkdocs.yml"
},
"yaml.customTags": [
"!ENV scalar",
"!ENV sequence",
"tag:yaml.org,2002:python/name:materialx.emoji.to_svg",
"tag:yaml.org,2002:python/name:materialx.emoji.twemoji",
"tag:yaml.org,2002:python/name:pymdownx.superfences.fence_code_format"
]
} }
EOF EOF

View File

@@ -11,7 +11,7 @@ repos:
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
- repo: https://github.com/pre-commit/mirrors-clang-format - repo: https://github.com/pre-commit/mirrors-clang-format
rev: v16.0.0 rev: v16.0.4
hooks: hooks:
- id: clang-format - id: clang-format
- repo: https://github.com/cheshirekow/cmake-format-precommit - repo: https://github.com/cheshirekow/cmake-format-precommit

View File

@@ -27,6 +27,9 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
set(projectPrefix UNITS_) set(projectPrefix UNITS_)
option(${projectPrefix}BUILD_LA "Build code depending on the linear algebra library" ON)
message(STATUS "${projectPrefix}BUILD_LA: ${${projectPrefix}BUILD_LA}")
# make sure that the file is being used as an entry point # make sure that the file is being used as an entry point
include(modern_project_structure) include(modern_project_structure)
ensure_entry_point() ensure_entry_point()
@@ -44,6 +47,7 @@ add_compile_definitions($<$<CONFIG:Debug>:gsl_CONFIG_CONTRACT_CHECKING_AUDIT>)
# enable include-what-you-use # enable include-what-you-use
option(${projectPrefix}IWYU "Enables include-what-you-use" OFF) option(${projectPrefix}IWYU "Enables include-what-you-use" OFF)
if(${projectPrefix}IWYU) if(${projectPrefix}IWYU)
include(include-what-you-use) include(include-what-you-use)
enable_iwyu( enable_iwyu(
@@ -52,12 +56,13 @@ if(${projectPrefix}IWYU)
MAX_LINE_LENGTH 120 MAX_LINE_LENGTH 120
NO_COMMENTS NO_COMMENTS
) )
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(${projectPrefix}AS_SYSTEM_HEADERS ON) set(${projectPrefix}AS_SYSTEM_HEADERS ON)
endif() endif()
endif() endif()
#enable_clang_tidy() # enable_clang_tidy()
# add project code # add project code
add_subdirectory(src) add_subdirectory(src)
@@ -66,10 +71,11 @@ add_subdirectory(src)
add_subdirectory(example) add_subdirectory(example)
# generate project documentation # generate project documentation
add_subdirectory(docs) # add_subdirectory(docs)
# add unit tests # add unit tests
enable_testing() enable_testing()
add_subdirectory(test) add_subdirectory(test)
# tests for standalone headers # tests for standalone headers

View File

@@ -115,6 +115,7 @@ function(add_header_test target)
set(source "${CMAKE_CURRENT_BINARY_DIR}/headers/${directory}/${filename}.cpp") set(source "${CMAKE_CURRENT_BINARY_DIR}/headers/${directory}/${filename}.cpp")
if(NOT EXISTS "${source}") if(NOT EXISTS "${source}")
file(WRITE "${source}" "#include <${header}>") file(WRITE "${source}" "#include <${header}>")
file(APPEND "${source}" "\n#include <${header}>") # do it twice to ensure that header guards are provided
endif() endif()
list(APPEND sources "${source}") list(APPEND sources "${source}")
endforeach() endforeach()

View File

@@ -54,8 +54,6 @@ class MPUnitsConan(ConanFile):
license = "MIT" license = "MIT"
url = "https://github.com/mpusz/units" url = "https://github.com/mpusz/units"
settings = "os", "arch", "compiler", "build_type" settings = "os", "arch", "compiler", "build_type"
options = {"downcast_mode": ["off", "on", "auto"]}
default_options = {"downcast_mode": "on"}
exports = ["LICENSE.md"] exports = ["LICENSE.md"]
exports_sources = [ exports_sources = [
"docs/*", "docs/*",
@@ -86,7 +84,7 @@ class MPUnitsConan(ConanFile):
@property @property
def _skip_docs(self): def _skip_docs(self):
return bool(self.conf.get("user.build:skip_docs", default=False)) return bool(self.conf.get("user.build:skip_docs", default=True))
@property @property
def _use_libfmt(self): def _use_libfmt(self):
@@ -117,7 +115,7 @@ class MPUnitsConan(ConanFile):
def build_requirements(self): def build_requirements(self):
if self._build_all: if self._build_all:
self.test_requires("catch2/3.1.0") self.test_requires("catch2/3.3.2")
if not self._skip_la: if not self._skip_la:
self.test_requires("wg21-linear_algebra/0.7.3") self.test_requires("wg21-linear_algebra/0.7.3")
if not self._skip_docs: if not self._skip_docs:
@@ -144,7 +142,6 @@ class MPUnitsConan(ConanFile):
def generate(self): def generate(self):
tc = CMakeToolchain(self) tc = CMakeToolchain(self)
tc.variables["UNITS_DOWNCAST_MODE"] = str(self.options.downcast_mode).upper()
tc.variables["UNITS_BUILD_LA"] = self._build_all and not self._skip_la tc.variables["UNITS_BUILD_LA"] = self._build_all and not self._skip_la
tc.variables["UNITS_BUILD_DOCS"] = self._build_all and not self._skip_docs tc.variables["UNITS_BUILD_DOCS"] = self._build_all and not self._skip_docs
tc.variables["UNITS_USE_LIBFMT"] = self._use_libfmt tc.variables["UNITS_USE_LIBFMT"] = self._use_libfmt
@@ -188,29 +185,32 @@ class MPUnitsConan(ConanFile):
self.cpp_info.components["core-fmt"].requires = ["core"] self.cpp_info.components["core-fmt"].requires = ["core"]
if self._use_libfmt: if self._use_libfmt:
self.cpp_info.components["core-fmt"].requires.append("fmt::fmt") self.cpp_info.components["core-fmt"].requires.append("fmt::fmt")
self.cpp_info.components["utility"].requires = ["core", "isq", "si", "angular"]
self.cpp_info.components["isq"].requires = ["core"] self.cpp_info.components["isq"].requires = ["core"]
self.cpp_info.components["isq-natural"].requires = ["isq"] self.cpp_info.components["angular"].requires = ["isq"]
self.cpp_info.components["isq_angular"].requires = ["isq", "angular"]
self.cpp_info.components["natural"].requires = ["isq"]
self.cpp_info.components["si"].requires = ["isq"] self.cpp_info.components["si"].requires = ["isq"]
self.cpp_info.components["si-cgs"].requires = ["si"] self.cpp_info.components["cgs"].requires = ["si"]
self.cpp_info.components["si-fps"].requires = ["si-international"] self.cpp_info.components["hep"].requires = ["si"]
self.cpp_info.components["si-hep"].requires = ["si"] self.cpp_info.components["iau"].requires = ["si"]
self.cpp_info.components["si-iau"].requires = ["si"] self.cpp_info.components["imperial"].requires = ["si"]
self.cpp_info.components["si-imperial"].requires = ["si"] self.cpp_info.components["international"].requires = ["si"]
self.cpp_info.components["si-international"].requires = ["si"] self.cpp_info.components["typographic"].requires = ["usc"]
self.cpp_info.components["si-typographic"].requires = ["si"] self.cpp_info.components["usc"].requires = ["international"]
self.cpp_info.components["si-uscs"].requires = ["si"] self.cpp_info.components["iec80000"].requires = ["isq", "si"]
self.cpp_info.components["isq-iec80000"].requires = ["si"]
self.cpp_info.components["systems"].requires = [ self.cpp_info.components["systems"].requires = [
"isq", "isq",
"isq-natural", "angular",
"isq_angular",
"natural",
"si", "si",
"si-cgs", "cgs",
"si-fps", "hep",
"si-hep", "iau",
"si-iau", "imperial",
"si-imperial", "international",
"si-international", "typographic",
"si-typographic", "usc",
"si-uscs", "iec80000",
"isq-iec80000",
] ]

View File

@@ -6,6 +6,11 @@ quantity
Interface Interface
--------- ---------
The value of a quantity is generally expressed as the product of a number and a unit. The
unit is simply a particular example of the quantity concerned which is used as a reference,
and the number is the ratio of the value of the quantity to the unit.
`quantity` class template provides a similar interface to `quantity` class template provides a similar interface to
`std::chrono::duration <https://en.cppreference.com/w/cpp/chrono/duration>`_. `std::chrono::duration <https://en.cppreference.com/w/cpp/chrono/duration>`_.
The difference is that it uses ``double`` as a default representation and has The difference is that it uses ``double`` as a default representation and has

View File

@@ -139,3 +139,68 @@ In order to obtain the base/coherent unit of any dimension type a
static_assert(is_same_v<dimension_unit<si::dim_length>, si::metre>); static_assert(is_same_v<dimension_unit<si::dim_length>, si::metre>);
static_assert(is_same_v<dimension_unit<si::dim_speed>, si::metre_per_second>); static_assert(is_same_v<dimension_unit<si::dim_speed>, si::metre_per_second>);
How do you feel about:
inline constexpr second second;
?
We could do the same to dimensions to not have to type dim_length{} all the time.
6 replies 2 new
@JohelEGP
JohelEGP
2 days ago
mp_units::units::second is a variable. The reference isq::si::second could stay a type and s continue to be a variable.
@mpusz
mpusz
2 days ago
Maintainer
Author
The problem is the following:
Variables are really needed for a new design. We can ask users to put {} after every dimension unit, and reference types but it would be really nasty and a lot of boilerplate.
Short names have to be opt-in as they collide with a lot of code (especially on MSVC).
@JohelEGP
JohelEGP
yesterday
As pointed out by #389 (reply in thread), I think it'd be OK suffix _t to the types.
@mpusz
mpusz
10 hours ago
Maintainer
Author
It will be visible in the compilation errors, which is inconvenient :-(
Instead of:
quantity<reference<derived_dimension<length_dim, per<time_dim>>, derived_unit<metre, per<second>>>
we will have something like:
quantity<reference<derived_dimension<length_dim_t, per<time_dim_t>>, derived_unit<metre_t, per<second_t>>>
Personally, I like the first output more.
Note that definition like:
inline constexpr second second;
besides, being really unconventional, is allowed by the language and will not impact our users at all. A user is about to always work with values in the code and observe types in the compilation errors. Using the same nicely blends those two domains together.
@mpusz
mpusz
10 hours ago
Maintainer
Author
I could use a list of values instead of types as template parameters in the dervied_dimension and derived_unit but in such case the error would look like:
quantity<reference<derived_dimension<length_dim{}, per<time_dim{}>{}>{}, derived_unit<metre{}, per<second{}>{}>{}>>
which also is not that nice.
@JohelEGP
JohelEGP
6 hours ago
That's convincing. This justification should definitely be part of the documentation.

View File

@@ -436,3 +436,18 @@ class template::
To learn more about unknown units please refer to the To learn more about unknown units please refer to the
:ref:`use_cases/unknown_dimensions:Working with Unknown Dimensions and Their Units` chapter. :ref:`use_cases/unknown_dimensions:Working with Unknown Dimensions and Their Units` chapter.
## System reference
"It is important to emphasize that each physical quantity has only one coherent SI unit, even
though this unit can be expressed in different forms by using some of the special names and
symbols."
The converse, however, is not true, because in general several different quantities may
share the same SI unit.
For example, for the quantity heat capacity as well as for the
quantity entropy the SI unit is joule per kelvin. Similarly, for the base quantity electric
current as well as the derived quantity magnetomotive force the SI unit is the ampere. It is
therefore important not to use the unit alone to specify the quantity.

View File

@@ -1,10 +1,10 @@
Math Math
==== ====
.. doxygenfunction:: units::pow .. doxygenfunction:: mp_units::pow
.. doxygenfunction:: units::sqrt .. doxygenfunction:: mp_units::sqrt
.. doxygenfunction:: units::abs .. doxygenfunction:: mp_units::abs
.. doxygenfunction:: units::epsilon .. doxygenfunction:: mp_units::epsilon

View File

@@ -33,17 +33,40 @@ function(add_example target)
target_link_libraries(${target} PRIVATE ${ARGN}) target_link_libraries(${target} PRIVATE ${ARGN})
endfunction() endfunction()
add_example(avg_speed mp-units::core-io mp-units::si mp-units::cgs mp-units::usc)
add_example(capacitor_time_curve mp-units::core-io mp-units::si mp-units::utility)
add_example(
clcpp_response
mp-units::core-fmt
mp-units::core-io
mp-units::si
mp-units::iau
mp-units::imperial
mp-units::international
mp-units::typographic
mp-units::usc
)
add_example(conversion_factor mp-units::core-fmt mp-units::core-io mp-units::si) add_example(conversion_factor mp-units::core-fmt mp-units::core-io mp-units::si)
add_example(custom_systems mp-units::core-io mp-units::si) add_example(currency mp-units::core-io)
add_example(hello_units mp-units::core-fmt mp-units::core-io mp-units::si mp-units::si-international) add_example(foot_pound_second mp-units::core-fmt mp-units::international mp-units::imperial)
add_example(glide_computer mp-units::core-fmt mp-units::international mp-units::utility glide_computer_lib)
add_example(hello_units mp-units::core-fmt mp-units::core-io mp-units::si mp-units::usc)
add_example(measurement mp-units::core-io mp-units::si) add_example(measurement mp-units::core-io mp-units::si)
add_example(si_constants mp-units::core-fmt mp-units::si) add_example(si_constants mp-units::core-fmt mp-units::si)
add_example(storage_tank mp-units::core-fmt mp-units::si)
add_example(
strong_angular_quantities mp-units::core-fmt mp-units::core-io mp-units::si mp-units::isq_angle mp-units::utility
)
add_example(total_energy mp-units::core-io mp-units::si mp-units::natural mp-units::utility)
add_example(
unmanned_aerial_vehicle
mp-units::core-fmt
mp-units::core-io
mp-units::si
mp-units::international
mp-units::utility
example_utils
)
if(NOT ${projectPrefix}LIBCXX) add_subdirectory(glide_computer)
add_subdirectory(glide_computer)
endif()
add_subdirectory(aliases)
add_subdirectory(kalman_filter) add_subdirectory(kalman_filter)
add_subdirectory(literals)
add_subdirectory(references)

View File

@@ -1,65 +0,0 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Mateusz Pusz
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
cmake_minimum_required(VERSION 3.2)
#
# add_example(target <depependencies>...)
#
function(add_example target)
add_executable(${target}-aliases ${target}.cpp)
target_link_libraries(${target}-aliases PRIVATE ${ARGN})
target_compile_definitions(${target}-aliases PRIVATE ${projectPrefix}NO_LITERALS ${projectPrefix}NO_REFERENCES)
endfunction()
add_example(avg_speed mp-units::core-io mp-units::si mp-units::si-cgs mp-units::si-international)
add_example(box_example mp-units::core-fmt mp-units::si)
add_example(capacitor_time_curve mp-units::core-io mp-units::si)
add_example(
clcpp_response
mp-units::core-fmt
mp-units::core-io
mp-units::si
mp-units::si-iau
mp-units::si-imperial
mp-units::si-international
mp-units::si-typographic
mp-units::si-uscs
)
add_example(experimental_angle mp-units::core-fmt mp-units::core-io mp-units::si)
add_example(foot_pound_second mp-units::core-fmt mp-units::si-fps)
add_example(measurement mp-units::core-io mp-units::si)
add_example(total_energy mp-units::core-io mp-units::si mp-units::isq-natural)
add_example(unknown_dimension mp-units::core-io mp-units::si)
if(NOT ${projectPrefix}LIBCXX)
add_example(glide_computer_example mp-units::core-fmt mp-units::si-international glide_computer)
target_compile_definitions(
glide_computer_example-aliases PRIVATE ${projectPrefix}NO_LITERALS ${projectPrefix}NO_REFERENCES
)
if(${projectPrefix}BUILD_LA)
find_package(wg21_linear_algebra CONFIG REQUIRED)
add_example(linear_algebra mp-units::core-fmt mp-units::core-io mp-units::si)
target_link_libraries(linear_algebra-aliases PRIVATE wg21_linear_algebra::wg21_linear_algebra)
endif()
endif()

View File

@@ -1,192 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/isq/si/cgs/length.h>
#include <units/isq/si/cgs/speed.h> // IWYU pragma: keep
#include <units/isq/si/international/length.h>
#include <units/isq/si/international/speed.h> // IWYU pragma: keep
#include <units/isq/si/length.h> // IWYU pragma: keep
#include <units/isq/si/speed.h>
#include <units/isq/si/time.h>
#include <units/quantity_io.h>
#include <exception>
#include <iostream>
namespace {
using namespace units::isq;
constexpr si::speed<si::metre_per_second, int> fixed_int_si_avg_speed(si::length<si::metre, int> d,
si::time<si::second, int> t)
{
return d / t;
}
constexpr si::speed<si::metre_per_second> fixed_double_si_avg_speed(si::length<si::metre> d, si::time<si::second> t)
{
return d / t;
}
template<typename U1, typename R1, typename U2, typename R2>
constexpr Speed auto si_avg_speed(si::length<U1, R1> d, si::time<U2, R2> t)
{
return d / t;
}
constexpr Speed auto avg_speed(Length auto d, Time auto t) { return d / t; }
template<Length D, Time T, Speed V>
void print_result(D distance, T duration, V speed)
{
const auto result_in_kmph = units::quantity_cast<si::speed<si::kilometre_per_hour>>(speed);
std::cout << "Average speed of a car that makes " << distance << " in " << duration << " is " << result_in_kmph
<< ".\n";
}
void example()
{
// SI (int)
{
using namespace units::aliases::isq::si;
constexpr auto distance = km<int>(220);
constexpr auto duration = h<int>(2);
std::cout << "SI units with 'int' as representation\n";
print_result(distance, duration, fixed_int_si_avg_speed(distance, duration));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
print_result(distance, duration, si_avg_speed(distance, duration));
print_result(distance, duration, avg_speed(distance, duration));
}
// SI (double)
{
using namespace units::aliases::isq::si;
constexpr auto distance = km<double>(220.);
constexpr auto duration = h<double>(2.);
std::cout << "\nSI units with 'double' as representation\n";
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
print_result(distance, duration,
fixed_int_si_avg_speed(quantity_cast<int>(distance), quantity_cast<int>(duration)));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
print_result(distance, duration, si_avg_speed(distance, duration));
print_result(distance, duration, avg_speed(distance, duration));
}
// Customary Units (int)
{
using namespace units::aliases::isq::si::international;
using namespace units::aliases::isq::si;
constexpr auto distance = mi<int>(140);
constexpr auto duration = h<int>(2);
std::cout << "\nUS Customary Units with 'int' as representation\n";
// it is not possible to make a lossless conversion of miles to meters on an integral type
// (explicit cast needed)
print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast<si::metre>(distance), duration));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
print_result(distance, duration, si_avg_speed(distance, duration));
print_result(distance, duration, avg_speed(distance, duration));
}
// Customary Units (double)
{
using namespace units::aliases::isq::si::international;
using namespace units::aliases::isq::si;
constexpr auto distance = mi<double>(140.);
constexpr auto duration = h<double>(2.);
std::cout << "\nUS Customary Units with 'double' as representation\n";
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
// also it is not possible to make a lossless conversion of miles to meters on an integral type
// (explicit cast needed)
print_result(
distance, duration,
fixed_int_si_avg_speed(quantity_cast<si::length<si::metre, int>>(distance), quantity_cast<int>(duration)));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
print_result(distance, duration, si_avg_speed(distance, duration));
print_result(distance, duration, avg_speed(distance, duration));
}
// CGS (int)
{
using namespace units::aliases::isq::si::cgs;
using namespace units::aliases::isq::si::time;
constexpr auto distance = cm<int>(22'000'000);
constexpr auto duration = h<int>(2);
std::cout << "\nCGS units with 'int' as representation\n";
// it is not possible to make a lossless conversion of centimeters to meters on an integral type
// (explicit cast needed)
print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast<si::metre>(distance), duration));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
// not possible to convert both a dimension and a unit with implicit cast
print_result(distance, duration, si_avg_speed(quantity_cast<si::dim_length>(distance), duration));
print_result(distance, duration, avg_speed(distance, duration));
}
// CGS (double)
{
using namespace units::aliases::isq::si::cgs;
using namespace units::aliases::isq::si::time;
constexpr auto distance = cm<double>(22'000'000.);
constexpr auto duration = h<double>(2.);
std::cout << "\nCGS units with 'double' as representation\n";
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
// it is not possible to make a lossless conversion of centimeters to meters on an integral type
// (explicit cast needed)
print_result(
distance, duration,
fixed_int_si_avg_speed(quantity_cast<si::length<si::metre, int>>(distance), quantity_cast<int>(duration)));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
// not possible to convert both a dimension and a unit with implicit cast
print_result(distance, duration, si_avg_speed(quantity_cast<si::dim_length>(distance), duration));
print_result(distance, duration, avg_speed(distance, duration));
}
}
} // namespace
int main()
{
try {
example();
} catch (const std::exception& ex) {
std::cerr << "Unhandled std exception caught: " << ex.what() << '\n';
} catch (...) {
std::cerr << "Unhandled unknown exception caught\n";
}
}

View File

@@ -1,108 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/format.h>
#include <units/generic/dimensionless.h>
#include <units/isq/si/amount_of_substance.h>
#include <units/isq/si/area.h>
#include <units/isq/si/constants.h>
#include <units/isq/si/density.h>
#include <units/isq/si/force.h>
#include <units/isq/si/length.h>
#include <units/isq/si/mass.h>
#include <units/isq/si/speed.h> // IWYU pragma: keep
#include <units/isq/si/time.h>
#include <units/isq/si/volume.h>
#include <cassert>
#include <iostream>
#include <utility>
namespace {
using namespace units::aliases::isq::si;
inline constexpr auto g = units::isq::si::standard_gravity<>; // NOLINT(readability-identifier-length)
inline constexpr auto air_density = kg_per_m3<>(1.225);
class Box {
area::m2<> base_;
length::m<> height_;
density::kg_per_m3<> density_ = air_density;
public:
constexpr Box(const length::m<>& length, const length::m<>& width, length::m<> height) :
base_(length * width), height_(std::move(height))
{
}
[[nodiscard]] constexpr force::N<> filled_weight() const
{
const volume::m3<> volume = base_ * height_;
const mass::kg<> mass = density_ * volume;
return mass * g;
}
[[nodiscard]] constexpr length::m<> fill_level(const mass::kg<>& measured_mass) const
{
return height_ * measured_mass * g / filled_weight();
}
[[nodiscard]] constexpr volume::m3<> spare_capacity(const mass::kg<>& measured_mass) const
{
return (height_ - fill_level(measured_mass)) * base_;
}
constexpr void set_contents_density(const density::kg_per_m3<>& density_in)
{
assert(density_in > air_density);
density_ = density_in;
}
};
} // namespace
int main()
{
using namespace units;
using namespace units::isq;
const length::m<> height(mm<>(200.0));
auto box = Box(mm<>(1000.0), mm<>(500.0), height);
box.set_contents_density(kg_per_m3<>(1000.0));
const auto fill_time = s<>(200.0); // time since starting fill
const auto measured_mass = kg<>(20.0); // measured mass at fill_time
const Length auto fill_level = box.fill_level(measured_mass);
const Dimensionless auto fill_percent = quantity_cast<percent>(fill_level / height);
const Volume auto spare_capacity = box.spare_capacity(measured_mass);
const auto input_flow_rate = measured_mass / fill_time; // unknown dimension
const Speed auto float_rise_rate = fill_level / fill_time;
const Time auto fill_time_left = (height / fill_level - 1) * fill_time;
std::cout << "mp-units box example...\n";
std::cout << UNITS_STD_FMT::format("fill height at {} = {} ({} full)\n", fill_time, fill_level, fill_percent);
std::cout << UNITS_STD_FMT::format("spare_capacity at {} = {}\n", fill_time, spare_capacity);
std::cout << UNITS_STD_FMT::format("input flow rate after {} = {}\n", fill_time, input_flow_rate);
std::cout << UNITS_STD_FMT::format("float rise rate = {}\n", float_rise_rate);
std::cout << UNITS_STD_FMT::format("box full E.T.A. at current flow rate = {}\n", fill_time_left);
}

View File

@@ -1,62 +0,0 @@
/*
Copyright (c) 2003-2020 Andy Little.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses./
*/
/*
capacitor discharge curve using compile_time
physical_quantities
*/
#include <units/generic/dimensionless.h>
#include <units/isq/si/capacitance.h>
#include <units/isq/si/resistance.h>
#include <units/isq/si/time.h>
#include <units/isq/si/voltage.h>
#include <units/math.h> // IWYU pragma: keep
#include <units/quantity_io.h>
#include <iostream>
int main()
{
using namespace units::isq;
using namespace units::aliases::isq::si;
std::cout << "mp-units capacitor time curve example...\n";
std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
std::cout.precision(3);
constexpr auto C = capacitance::uF<>(0.47);
constexpr auto V0 = voltage::V<>(5.0);
constexpr auto R = resistance::kR<>(4.7);
for (auto t = ms<int>(0); t <= ms<int>(50); ++t) {
const Voltage auto Vt = V0 * units::exp(-t / (R * C));
std::cout << "at " << t << " voltage is ";
if (Vt >= V<>(1))
std::cout << Vt;
else if (Vt >= mV<>(1))
std::cout << mV<>(Vt);
else if (Vt >= uV<>(1))
std::cout << uV<>(Vt);
else if (Vt >= nV<>(1))
std::cout << nV<>(Vt);
else
std::cout << pV<>(Vt);
std::cout << "\n";
}
}

View File

@@ -1,151 +0,0 @@
/*
Copyright (c) 2003-2019 Andy Little.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses./
*/
#include <units/format.h>
#include <units/isq/si/area.h>
#include <units/isq/si/iau/length.h>
#include <units/isq/si/imperial/length.h>
#include <units/isq/si/international/length.h>
#include <units/isq/si/length.h>
#include <units/isq/si/time.h>
#include <units/isq/si/typographic/length.h>
#include <units/isq/si/uscs/length.h>
#include <units/quantity_io.h>
#include <iostream>
namespace {
using namespace units;
void simple_quantities()
{
using namespace units::aliases::isq;
using namespace units::aliases::isq::si;
using namespace units::aliases::isq::si::international;
using distance = si::length::m<>;
using duration = time::s<>;
constexpr distance km = si::length::km<>(1.0);
constexpr distance miles = mi<>(1.0);
constexpr duration sec = s<>(1);
constexpr duration min = time::min<>(1);
constexpr duration hr = h<>(1);
std::cout << "A physical quantities library can choose the simple\n";
std::cout << "option to provide output using a single type for each base unit:\n\n";
std::cout << km << '\n';
std::cout << miles << '\n';
std::cout << sec << '\n';
std::cout << min << '\n';
std::cout << hr << "\n\n";
}
void quantities_with_typed_units()
{
using namespace units::aliases::isq;
using namespace units::aliases::isq::si;
using namespace units::aliases::isq::si::international;
constexpr si::length::km<> km = si::km<>(1.0);
constexpr si::international::length::mi<> miles = mi<>(1.0);
std::cout.precision(6);
constexpr time::s<> sec = s<>(1);
constexpr time::min<> min = si::min<>(1);
constexpr time::h<> hr = h<>(1);
std::cout << "A more flexible option is to provide separate types for each unit,\n\n";
std::cout << km << '\n';
std::cout << miles << '\n';
std::cout << sec << '\n';
std::cout << min << '\n';
std::cout << hr << "\n\n";
constexpr si::length::m<> meter = m<>(1);
std::cout << "then a wide range of pre-defined units can be defined and converted,\n"
" for consistency and repeatability across applications:\n\n";
std::cout << meter << '\n';
std::cout << " = " << au<>(meter) << '\n';
std::cout << " = " << iau::angstrom<>(meter) << '\n';
std::cout << " = " << imperial::ch<>(meter) << '\n';
std::cout << " = " << international::fathom<>(meter) << '\n';
std::cout << " = " << uscs::fathom<>(meter) << '\n';
std::cout << " = " << international::ft<>(meter) << '\n';
std::cout << " = " << uscs::ft<>(meter) << '\n';
std::cout << " = " << international::in<>(meter) << '\n';
std::cout << " = " << iau::ly<>(meter) << '\n';
std::cout << " = " << international::mi<>(meter) << '\n';
std::cout << " = " << international::mi_naut<>(meter) << '\n';
std::cout << " = " << iau::pc<>(meter) << '\n';
std::cout << " = " << typographic::pica_comp<>(meter) << '\n';
std::cout << " = " << typographic::pica_prn<>(meter) << '\n';
std::cout << " = " << typographic::point_comp<>(meter) << '\n';
std::cout << " = " << typographic::point_prn<>(meter) << '\n';
std::cout << " = " << imperial::rd<>(meter) << '\n';
std::cout << " = " << international::yd<>(meter) << '\n';
}
void calcs_comparison()
{
using namespace units::aliases::isq::si;
std::cout << "\nA distinct unit for each type is efficient and accurate\n"
"when adding two values of the same very big\n"
"or very small type:\n\n";
length::fm<float> L1A = fm<>(2.f);
length::fm<float> L2A = fm<>(3.f);
length::fm<float> LrA = L1A + L2A;
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n + {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, LrA);
std::cout << "The single unit method must convert large\n"
"or small values in other units to the base unit.\n"
"This is both inefficient and inaccurate\n\n";
length::m<float> L1B = L1A;
length::m<float> L2B = L2A;
length::m<float> LrB = L1B + L2B;
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n + {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1B, L2B, LrB);
std::cout << "In multiplication and division:\n\n";
area::fm2<float> ArA = L1A * L2A;
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n * {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, ArA);
std::cout << "similar problems arise\n\n";
area::m2<float> ArB = L1B * L2B;
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n * {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1B, L2B, ArB);
}
} // namespace
int main()
{
std::cout << "This demo was originally posted on com.lang.c++.moderated in 2006\n";
std::cout << "http://compgroups.net/comp.lang.c++.moderated/dimensional-analysis-units/51712\n";
std::cout << "Here converted to use mp-units library.\n\n";
simple_quantities();
quantities_with_typed_units();
calcs_comparison();
}

View File

@@ -1,45 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/generic/angle.h>
#include <units/isq/si/force.h>
#include <units/isq/si/length.h>
#include <units/isq/si/torque.h>
#include <units/math.h>
#include <units/quantity_io.h>
#include <iostream>
int main()
{
using namespace units;
using namespace units::isq;
using namespace units::aliases;
using namespace units::aliases::isq::si;
const Length auto lever = cm<>(20);
const Force auto force = N<>(500);
const Angle auto angle = deg<>(90);
const Torque auto torque = lever * force * sin(angle) / cotes_angle<>;
std::cout << "Applying a perpendicular force of " << force << " to a " << lever << " long lever results in "
<< quantity_cast<si::newton_metre_per_radian>(torque) << " of torque.\n";
}

View File

@@ -1,129 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/format.h>
#include <units/isq/si/fps/density.h>
#include <units/isq/si/fps/length.h>
#include <units/isq/si/fps/mass.h>
#include <units/isq/si/fps/power.h>
#include <units/isq/si/fps/speed.h>
#include <units/isq/si/international/speed.h>
#include <units/isq/si/length.h>
#include <units/isq/si/mass.h>
#include <units/isq/si/power.h>
#include <units/isq/si/speed.h>
#include <units/isq/si/volume.h>
#include <iostream>
#include <string_view>
using namespace units::aliases::isq;
// Some basic specs for the warship
struct Ship {
si::fps::length::ft<> length;
si::fps::length::ft<> draft;
si::fps::length::ft<> beam;
si::fps::speed::ft_per_s<> speed;
si::fps::mass::lb<> mass;
si::fps::length::in<> mainGuns;
si::fps::mass::lb<> shellMass;
si::fps::speed::ft_per_s<> shellSpeed;
si::fps::power::ft_pdl_per_s<> power;
};
// Print 'a' in its current units and print its value cast to the units in each of Args
template<class... Args, units::Quantity Q>
auto fmt_line(const Q a)
{
return UNITS_STD_FMT::format("{:22}", a) + (UNITS_STD_FMT::format(",{:20}", units::quantity_cast<Args>(a)) + ...);
}
// Print the ship details in the units as defined in the Ship struct, in other si::imperial units, and in SI
void print_details(std::string_view description, const Ship& ship)
{
const auto waterDensity = si::fps::density::lb_per_ft3<>(62.4);
std::cout << UNITS_STD_FMT::format("{}\n", description);
std::cout
<< UNITS_STD_FMT::format("{:20} : {}\n", "length", fmt_line<si::fps::length::yd<>, si::length::m<>>(ship.length))
<< UNITS_STD_FMT::format("{:20} : {}\n", "draft", fmt_line<si::fps::length::yd<>, si::length::m<>>(ship.draft))
<< UNITS_STD_FMT::format("{:20} : {}\n", "beam", fmt_line<si::fps::length::yd<>, si::length::m<>>(ship.beam))
<< UNITS_STD_FMT::format("{:20} : {}\n", "mass", fmt_line<si::fps::mass::lton<>, si::mass::t<>>(ship.mass))
<< UNITS_STD_FMT::format("{:20} : {}\n", "speed",
fmt_line<si::international::speed::kn<>, si::speed::km_per_h<>>(ship.speed))
<< UNITS_STD_FMT::format("{:20} : {}\n", "power", fmt_line<si::fps::power::hp<>, si::power::kW<>>(ship.power))
<< UNITS_STD_FMT::format("{:20} : {}\n", "main guns",
fmt_line<si::fps::length::in<>, si::length::mm<>>(ship.mainGuns))
<< UNITS_STD_FMT::format("{:20} : {}\n", "fire shells weighing",
fmt_line<si::fps::mass::lton<>, si::mass::kg<>>(ship.shellMass))
<< UNITS_STD_FMT::format("{:20} : {}\n", "fire shells at",
fmt_line<si::fps::speed::mph<>, si::speed::km_per_h<>>(ship.shellSpeed))
<< UNITS_STD_FMT::format("{:20} : {}\n", "volume underwater",
fmt_line<si::volume::m3<>, si::volume::l<>>(ship.mass / waterDensity));
}
int main()
{
using namespace units::aliases::isq::si;
using namespace units::aliases::isq::si::fps;
using units::aliases::isq::si::fps::length::ft; // to disambiguate from si::femptotonne
// KMS Bismark, using the units the Germans would use, taken from Wiki
auto bismark = Ship{.length{m<>(251.)},
.draft{m<>(9.3)},
.beam{m<>(36)},
.speed{km_per_h<>(56)},
.mass{t<>(50'300)},
.mainGuns{mm<>(380)},
.shellMass{kg<>(800)},
.shellSpeed{m_per_s<>(820.)},
.power{kW<>(110.45)}};
// USS Iowa, using units from the foot-pound-second system
auto iowa = Ship{.length{ft<>(860.)},
.draft{ft<>(37.) + in<>(2.)},
.beam{ft<>(108.) + in<>(2.)},
.speed{international::kn<>(33)},
.mass{lton<>(57'540)},
.mainGuns{in<>(16)},
.shellMass{lb<>(2700)},
.shellSpeed{ft_per_s<>(2690.)},
.power{hp<>(212'000)}};
// HMS King George V, using units from the foot-pound-second system
auto kgv = Ship{.length{ft<>(745.1)},
.draft{ft<>(33.) + in<>(7.5)},
.beam{ft<>(103.2) + in<>(2.5)},
.speed{international::kn<>(28.3)},
.mass{lton<>(42'245)},
.mainGuns{in<>(14)},
.shellMass{lb<>(1'590)},
.shellSpeed{ft_per_s<>(2483)},
.power{hp<>(110'000)}};
print_details("KMS Bismark, defined in appropriate units from the SI system", bismark);
std::cout << "\n\n";
print_details("USS Iowa, defined in appropriate units foot-pound-second system", iowa);
std::cout << "\n\n";
print_details("HMS King George V, defined in appropriate units foot-pound-second system", kgv);
}

View File

@@ -1,232 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/format.h>
#include <units/isq/si/energy.h> // IWYU pragma: keep
#include <units/isq/si/force.h>
#include <units/isq/si/length.h>
#include <units/isq/si/speed.h> // IWYU pragma: keep
#include <units/quantity_io.h>
#include <iostream>
#include <matrix>
template<typename Rep = double>
using vector = STD_LA::fixed_size_column_vector<Rep, 3>;
template<typename Rep = double>
using matrix = STD_LA::fixed_size_matrix<Rep, 3, 3>;
namespace STD_LA {
template<typename Rep>
std::ostream& operator<<(std::ostream& os, const ::vector<Rep>& v)
{
os << "|";
for (auto i = 0U; i < v.size(); ++i) {
os << UNITS_STD_FMT::format(" {:>9}", v(i));
}
os << " |";
return os;
}
template<typename Rep>
std::ostream& operator<<(std::ostream& os, const ::matrix<Rep>& v)
{
for (auto i = 0U; i < v.rows(); ++i) {
os << "|";
for (auto j = 0U; j < v.columns(); ++j) {
os << UNITS_STD_FMT::format(" {:>9}", v(i, j));
}
os << (i != v.rows() - 1U ? " |\n" : " |");
}
return os;
}
} // namespace STD_LA
namespace {
using namespace units::aliases::isq::si;
void vector_of_quantity_add()
{
std::cout << "\nvector_of_quantity_add:\n";
vector<length::m<>> v = {m<>(4), m<>(8), m<>(12)};
vector<length::m<>> u = {m<>(3), m<>(2), m<>(1)};
vector<length::km<>> t = {km<>(3), km<>(2), km<>(1)};
std::cout << "v = " << v << "\n";
std::cout << "u = " << u << "\n";
std::cout << "t = " << t << "\n";
std::cout << "v + u = " << v + u << "\n";
std::cout << "v + t = " << v + t << "\n";
std::cout << "t[m] = " << vector<length::m<>>(t) << "\n";
}
void vector_of_quantity_divide_by_scalar()
{
std::cout << "\nvector_of_quantity_divide_by_scalar:\n";
vector<length::m<>> v = {m<>(4), m<>(8), m<>(12)};
std::cout << "v = " << v << "\n";
std::cout << "v / s<>(2) = " << v / s<>(2) << "\n";
std::cout << "v / 2 = " << v / 2 << "\n";
}
void vector_of_quantity_tests()
{
vector_of_quantity_add();
vector_of_quantity_divide_by_scalar();
}
void matrix_of_quantity_add()
{
std::cout << "\nmatrix_of_quantity_add:\n";
matrix<length::m<>> v = {{m<>(1), m<>(2), m<>(3)}, {m<>(4), m<>(5), m<>(6)}, {m<>(7), m<>(8), m<>(9)}};
matrix<length::m<>> u = {{m<>(3), m<>(2), m<>(1)}, {m<>(3), m<>(2), m<>(1)}, {m<>(3), m<>(2), m<>(1)}};
matrix<length::mm<>> t = {{mm<>(3), mm<>(2), mm<>(1)}, {mm<>(3), mm<>(2), mm<>(1)}, {mm<>(3), mm<>(2), mm<>(1)}};
std::cout << "v =\n" << v << "\n";
std::cout << "u =\n" << u << "\n";
std::cout << "t =\n" << t << "\n";
std::cout << "v + u =\n" << v + u << "\n";
std::cout << "v + t =\n" << v + t << "\n";
std::cout << "v[mm] =\n" << matrix<length::mm<>>(v) << "\n";
}
void matrix_of_quantity_divide_by_scalar()
{
std::cout << "\nmatrix_of_quantity_divide_by_scalar:\n";
matrix<length::m<>> v = {{m<>(2), m<>(4), m<>(6)}, {m<>(4), m<>(6), m<>(8)}, {m<>(8), m<>(4), m<>(2)}};
std::cout << "v =\n" << v << "\n";
std::cout << "v / s<>(2) =\n" << v / s<>(2) << "\n";
std::cout << "v / 2 =\n" << v / 2 << "\n";
}
void matrix_of_quantity_tests()
{
matrix_of_quantity_add();
matrix_of_quantity_divide_by_scalar();
}
using namespace units::isq;
template<units::Unit U = si::metre, units::Representation Rep = double>
using length_v = si::length<U, vector<Rep>>;
template<units::Unit U = si::newton, units::Representation Rep = double>
using force_v = si::force<U, vector<Rep>>;
void quantity_of_vector_add()
{
std::cout << "\nquantity_of_vector_add:\n";
length_v<> v(vector<>{4, 8, 12});
length_v<> u(vector<>{3, 2, 1});
length_v<si::kilometre> t(vector<>{3, 2, 1});
std::cout << "v = " << v << "\n";
std::cout << "u = " << u << "\n";
std::cout << "t = " << t << "\n";
std::cout << "v + u = " << v + u << "\n";
std::cout << "v + t = " << v + t << "\n";
std::cout << "t[m] = " << quantity_cast<si::metre>(t) << "\n";
}
void quantity_of_vector_divide_by_scalar()
{
std::cout << "\nquantity_of_vector_divide_by_scalar:\n";
length_v<> v(vector<>{4, 8, 12});
std::cout << "v = " << v << "\n";
std::cout << "v / s<>(2) = " << v / s<>(2) << "\n";
std::cout << "v / 2 = " << v / 2 << "\n";
}
void quantity_of_vector_tests()
{
quantity_of_vector_add();
quantity_of_vector_divide_by_scalar();
}
template<units::Unit U = si::metre, units::Representation Rep = double>
using length_m = si::length<U, matrix<Rep>>;
void quantity_of_matrix_add()
{
std::cout << "\nquantity_of_matrix_add:\n";
length_m<> v(matrix<>{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
length_m<> u(matrix<>{{3, 2, 1}, {3, 2, 1}, {3, 2, 1}});
length_m<si::kilometre> t(matrix<>{{3, 2, 1}, {3, 2, 1}, {3, 2, 1}});
std::cout << "v =\n" << v << "\n";
std::cout << "u =\n" << u << "\n";
std::cout << "t =\n" << t << "\n";
std::cout << "v + u =\n" << v + u << "\n";
std::cout << "v + t =\n" << v + t << "\n";
// TODO Fix it
// std::cout << "v[mm] =\n" << matrix<length::mm<>>(v) << "\n";
}
void quantity_of_matrix_divide_by_scalar()
{
std::cout << "\nquantity_of_matrix_divide_by_scalar:\n";
length_m<> v(matrix<>{{2, 4, 6}, {4, 6, 8}, {8, 4, 2}});
std::cout << "v =\n" << v << "\n";
std::cout << "v / s<>(2) =\n" << v / s<>(2) << "\n";
std::cout << "v / 2 =\n" << v / 2 << "\n";
}
void quantity_of_matrix_tests()
{
quantity_of_matrix_add();
quantity_of_matrix_divide_by_scalar();
}
} // namespace
int main()
{
vector_of_quantity_tests();
matrix_of_quantity_tests();
quantity_of_vector_tests();
quantity_of_matrix_tests();
}

View File

@@ -1,156 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/isq/si/acceleration.h>
#include <units/isq/si/length.h>
#include <units/isq/si/speed.h>
#include <units/isq/si/time.h>
#include <units/quantity_io.h>
#include <cmath>
#include <exception>
#include <iostream>
namespace {
template<class T>
class measurement {
public:
using value_type = T;
measurement() = default;
constexpr explicit measurement(const value_type& val, const value_type& err = {}) : value_(val)
{
// it sucks that using declaration cannot be provided for a constructor initializer list
using namespace std;
uncertainty_ = abs(err);
}
constexpr const value_type& value() const { return value_; }
constexpr const value_type& uncertainty() const { return uncertainty_; }
constexpr value_type relative_uncertainty() const { return uncertainty() / value(); }
constexpr value_type lower_bound() const { return value() - uncertainty(); }
constexpr value_type upper_bound() const { return value() + uncertainty(); }
[[nodiscard]] constexpr measurement operator-() const { return measurement(-value(), uncertainty()); }
[[nodiscard]] friend constexpr measurement operator+(const measurement& lhs, const measurement& rhs)
{
using namespace std;
return measurement(lhs.value() + rhs.value(), hypot(lhs.uncertainty(), rhs.uncertainty()));
}
[[nodiscard]] friend constexpr measurement operator-(const measurement& lhs, const measurement& rhs)
{
using namespace std;
return measurement(lhs.value() - rhs.value(), hypot(lhs.uncertainty(), rhs.uncertainty()));
}
[[nodiscard]] friend constexpr measurement operator*(const measurement& lhs, const measurement& rhs)
{
const auto val = lhs.value() * rhs.value();
using namespace std;
return measurement(val, val * hypot(lhs.relative_uncertainty(), rhs.relative_uncertainty()));
}
[[nodiscard]] friend constexpr measurement operator*(const measurement& lhs, const value_type& value)
{
const auto val = lhs.value() * value;
return measurement(val, val * lhs.relative_uncertainty());
}
[[nodiscard]] friend constexpr measurement operator*(const value_type& value, const measurement& rhs)
{
const auto val = rhs.value() * value;
return measurement(val, val * rhs.relative_uncertainty());
}
[[nodiscard]] friend constexpr measurement operator/(const measurement& lhs, const measurement& rhs)
{
const auto val = lhs.value() / rhs.value();
using namespace std;
return measurement(val, val * hypot(lhs.relative_uncertainty(), rhs.relative_uncertainty()));
}
[[nodiscard]] friend constexpr measurement operator/(const measurement& lhs, const value_type& value)
{
const auto val = lhs.value() / value;
return measurement(val, val * lhs.relative_uncertainty());
}
[[nodiscard]] friend constexpr measurement operator/(const value_type& value, const measurement& rhs)
{
const auto val = value / rhs.value();
return measurement(val, val * rhs.relative_uncertainty());
}
[[nodiscard]] constexpr auto operator<=>(const measurement&) const = default;
friend std::ostream& operator<<(std::ostream& os, const measurement& v)
{
return os << v.value() << " ± " << v.uncertainty();
}
private:
value_type value_{};
value_type uncertainty_{};
};
} // namespace
namespace {
static_assert(units::Representation<measurement<double>>);
void example()
{
using namespace units::isq;
using namespace units::aliases::isq::si;
const auto a = acceleration::m_per_s2<measurement<double>>(measurement(9.8, 0.1));
const auto t = time::s<measurement<double>>(measurement(1.2, 0.1));
const Speed auto v1 = a * t;
#if UNITS_DOWNCAST_MODE == 0
std::cout << a << " * " << t << " = " << v1 << " = " << km_per_h<measurement<double>>(v1) << '\n';
#else
std::cout << a << " * " << t << " = " << v1 << " = " << km_per_h<measurement<double>>(v1) << '\n';
#endif
length::m<measurement<double>> length(measurement(123., 1.));
std::cout << "10 * " << length << " = " << 10 * length << '\n';
}
} // namespace
int main()
{
try {
example();
} catch (const std::exception& ex) {
std::cerr << "Unhandled std exception caught: " << ex.what() << '\n';
} catch (...) {
std::cerr << "Unhandled unknown exception caught\n";
}
}

View File

@@ -1,100 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/isq/natural/natural.h>
#include <units/isq/si/constants.h>
#include <units/isq/si/energy.h>
#include <units/isq/si/mass.h>
#include <units/isq/si/momentum.h>
#include <units/isq/si/speed.h> // IWYU pragma: keep
#include <units/math.h>
#include <units/quantity_io.h>
#include <exception>
#include <iostream>
namespace {
using namespace units::isq;
Energy auto total_energy(Momentum auto p, Mass auto m, Speed auto c)
{
return sqrt(pow<2>(p * c) + pow<2>(m * pow<2>(c)));
}
void si_example()
{
using namespace units::aliases::isq::si;
constexpr Speed auto c = units::isq::si::si2019::speed_of_light<>;
std::cout << "\n*** SI units (c = " << c << ") ***\n";
const Momentum auto p = GeV<>(4.) / c;
const Mass auto m = GeV<>(3.) / pow<2>(c);
const Energy auto E = total_energy(p, m, c);
std::cout << "[in GeV]\n"
<< "p = " << p << "\n"
<< "m = " << m << "\n"
<< "E = " << E << "\n";
const momentum::kg_m_per_s<> p_si = p;
const mass::kg<> m_si = m;
const energy::J<> E_si = total_energy(p_si, m_si, c);
std::cout << "\n[in SI units]\n"
<< "p = " << p_si << "\n"
<< "m = " << m_si << "\n"
<< "E = " << E_si << "\n";
std::cout << "\n[converted from SI units back to GeV]\n"
<< "E = " << GeV<>(E_si) << "\n";
}
void natural_example()
{
using namespace units::aliases::isq::natural;
constexpr Speed auto c = units::isq::natural::speed_of_light<>;
const momentum::GeV<> p(4);
const mass::GeV<> m(3);
const Energy auto E = total_energy(p, m, c);
std::cout << "\n*** Natural units (c = " << c << ") ***\n"
<< "p = " << p << "\n"
<< "m = " << m << "\n"
<< "E = " << E << "\n";
}
} // namespace
int main()
{
try {
si_example();
natural_example();
} catch (const std::exception& ex) {
std::cerr << "Unhandled std exception caught: " << ex.what() << '\n';
} catch (...) {
std::cerr << "Unhandled unknown exception caught\n";
}
}

View File

@@ -1,71 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/isq/si/length.h>
#include <units/isq/si/speed.h> // IWYU pragma: keep
#include <units/isq/si/time.h>
#include <units/quantity_io.h>
#include <exception>
#include <iostream>
namespace {
template<units::isq::Length D, units::isq::Time T>
constexpr units::isq::Speed auto avg_speed(D d, T t)
{
return d / t;
}
void example()
{
using namespace units::isq;
using namespace units::aliases::isq::si;
Length auto d1 = m<>(123);
Time auto t1 = s<>(10);
Speed auto v1 = avg_speed(d1, t1);
auto temp1 =
v1 * m<>(50); // produces intermediate unknown dimension with 'unknown_coherent_unit' as its 'coherent_unit'
Speed auto v2 = temp1 / m<>(100); // back to known dimensions again
Length auto d2 = v2 * s<>(60);
std::cout << "d1 = " << d1 << '\n';
std::cout << "t1 = " << t1 << '\n';
std::cout << "v1 = " << v1 << '\n';
std::cout << "temp1 = " << temp1 << '\n';
std::cout << "v2 = " << v2 << '\n';
std::cout << "d2 = " << d2 << '\n';
}
} // namespace
int main()
{
try {
example();
} catch (const std::exception& ex) {
std::cerr << "Unhandled std exception caught: " << ex.what() << '\n';
} catch (...) {
std::cerr << "Unhandled unknown exception caught\n";
}
}

View File

@@ -20,53 +20,49 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
#include <units/isq/si/cgs/length.h> #include <mp-units/iostream.h>
#include <units/isq/si/cgs/speed.h> // IWYU pragma: keep #include <mp-units/systems/cgs/cgs.h>
#include <units/isq/si/international/length.h> #include <mp-units/systems/international/international.h>
#include <units/isq/si/international/speed.h> // IWYU pragma: keep #include <mp-units/systems/isq/space_and_time.h>
#include <units/isq/si/length.h> // IWYU pragma: keep #include <mp-units/systems/si/unit_symbols.h>
#include <units/isq/si/speed.h>
#include <units/isq/si/time.h>
#include <units/quantity_io.h>
#include <exception> #include <exception>
#include <iostream> #include <iostream>
namespace { namespace {
using namespace units::isq; using namespace mp_units;
constexpr si::speed<si::metre_per_second, int> fixed_int_si_avg_speed(si::length<si::metre, int> d, constexpr quantity<si::metre / si::second, int> fixed_int_si_avg_speed(quantity<si::metre, int> d,
si::time<si::second, int> t) quantity<si::second, int> t)
{ {
return d / t; return d / t;
} }
constexpr si::speed<si::metre_per_second> fixed_double_si_avg_speed(si::length<si::metre> d, si::time<si::second> t) constexpr quantity<si::metre / si::second> fixed_double_si_avg_speed(quantity<si::metre> d, quantity<si::second> t)
{ {
return d / t; return d / t;
} }
template<typename U1, typename R1, typename U2, typename R2> constexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d, QuantityOf<isq::time> auto t)
constexpr Speed auto si_avg_speed(si::length<U1, R1> d, si::time<U2, R2> t)
{ {
return d / t; return d / t;
} }
constexpr Speed auto avg_speed(Length auto d, Time auto t) { return d / t; } template<QuantityOf<isq::length> D, QuantityOf<isq::time> T, QuantityOf<isq::speed> V>
template<Length D, Time T, Speed V>
void print_result(D distance, T duration, V speed) void print_result(D distance, T duration, V speed)
{ {
const auto result_in_kmph = units::quantity_cast<si::speed<si::kilometre_per_hour>>(speed); using namespace mp_units::si::unit_symbols;
const auto result_in_kmph = value_cast<km / h>(speed);
std::cout << "Average speed of a car that makes " << distance << " in " << duration << " is " << result_in_kmph std::cout << "Average speed of a car that makes " << distance << " in " << duration << " is " << result_in_kmph
<< ".\n"; << ".\n";
} }
void example() void example()
{ {
using namespace mp_units::si::unit_symbols;
// SI (int) // SI (int)
{ {
using namespace units::isq::si::references;
constexpr auto distance = 220 * km; constexpr auto distance = 220 * km;
constexpr auto duration = 2 * h; constexpr auto duration = 2 * h;
@@ -74,31 +70,26 @@ void example()
print_result(distance, duration, fixed_int_si_avg_speed(distance, duration)); print_result(distance, duration, fixed_int_si_avg_speed(distance, duration));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
print_result(distance, duration, si_avg_speed(distance, duration));
print_result(distance, duration, avg_speed(distance, duration)); print_result(distance, duration, avg_speed(distance, duration));
} }
// SI (double) // SI (double)
{ {
using namespace units::isq::si::references;
constexpr auto distance = 220. * km; constexpr auto distance = 220. * km;
constexpr auto duration = 2. * h; constexpr auto duration = 2. * h;
std::cout << "\nSI units with 'double' as representation\n"; std::cout << "\nSI units with 'double' as representation\n";
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
print_result(distance, duration, print_result(distance, duration, fixed_int_si_avg_speed(value_cast<int>(distance), value_cast<int>(duration)));
fixed_int_si_avg_speed(quantity_cast<int>(distance), quantity_cast<int>(duration)));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
print_result(distance, duration, si_avg_speed(distance, duration));
print_result(distance, duration, avg_speed(distance, duration)); print_result(distance, duration, avg_speed(distance, duration));
} }
// Customary Units (int) // Customary Units (int)
{ {
using namespace units::isq::si::time_references; using namespace mp_units::international::unit_symbols;
using namespace units::isq::si::international::length_references;
constexpr auto distance = 140 * mi; constexpr auto distance = 140 * mi;
constexpr auto duration = 2 * h; constexpr auto duration = 2 * h;
@@ -106,16 +97,15 @@ void example()
// it is not possible to make a lossless conversion of miles to meters on an integral type // it is not possible to make a lossless conversion of miles to meters on an integral type
// (explicit cast needed) // (explicit cast needed)
print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast<si::metre>(distance), duration)); print_result(distance, duration, fixed_int_si_avg_speed(value_cast<si::metre>(distance), duration));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
print_result(distance, duration, si_avg_speed(distance, duration));
print_result(distance, duration, avg_speed(distance, duration)); print_result(distance, duration, avg_speed(distance, duration));
} }
// Customary Units (double) // Customary Units (double)
{ {
using namespace units::isq::si::time_references; using namespace mp_units::international::unit_symbols;
using namespace units::isq::si::international::length_references;
constexpr auto distance = 140. * mi; constexpr auto distance = 140. * mi;
constexpr auto duration = 2. * h; constexpr auto duration = 2. * h;
@@ -124,56 +114,40 @@ void example()
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
// also it is not possible to make a lossless conversion of miles to meters on an integral type // also it is not possible to make a lossless conversion of miles to meters on an integral type
// (explicit cast needed) // (explicit cast needed)
print_result( print_result(distance, duration,
distance, duration, fixed_int_si_avg_speed(value_cast<int>(value_cast<si::metre>(distance)), value_cast<int>(duration)));
fixed_int_si_avg_speed(quantity_cast<si::length<si::metre, int>>(distance), quantity_cast<int>(duration)));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
print_result(distance, duration, si_avg_speed(distance, duration));
print_result(distance, duration, avg_speed(distance, duration)); print_result(distance, duration, avg_speed(distance, duration));
} }
// CGS (int) // CGS (int)
{ {
using namespace units::isq::si::time_references; constexpr auto distance = 22'000'000 * cgs::centimetre;
using namespace units::isq::si::cgs::length_references; constexpr auto duration = 7200 * cgs::second;
constexpr auto distance = 22'000'000 * cm;
constexpr auto duration = 2 * h;
std::cout << "\nCGS units with 'int' as representation\n"; std::cout << "\nCGS units with 'int' as representation\n";
// it is not possible to make a lossless conversion of centimeters to meters on an integral type // it is not possible to make a lossless conversion of centimeters to meters on an integral type
// (explicit cast needed) // (explicit cast needed)
print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast<si::metre>(distance), duration)); print_result(distance, duration, fixed_int_si_avg_speed(value_cast<si::metre>(distance), duration));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
// not possible to convert both a dimension and a unit with implicit cast
print_result(distance, duration, si_avg_speed(quantity_cast<si::dim_length>(distance), duration));
print_result(distance, duration, avg_speed(distance, duration)); print_result(distance, duration, avg_speed(distance, duration));
} }
// CGS (double) // CGS (double)
{ {
using namespace units::isq::si::time_references; constexpr auto distance = 22'000'000. * cgs::centimetre;
using namespace units::isq::si::cgs::length_references; constexpr auto duration = 7200. * cgs::second;
constexpr auto distance = 22'000'000. * cm;
constexpr auto duration = 2. * h;
std::cout << "\nCGS units with 'double' as representation\n"; std::cout << "\nCGS units with 'double' as representation\n";
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
// it is not possible to make a lossless conversion of centimeters to meters on an integral type // it is not possible to make a lossless conversion of centimeters to meters on an integral type
// (explicit cast needed) // (explicit cast needed)
print_result( print_result(distance, duration,
distance, duration, fixed_int_si_avg_speed(value_cast<int>(value_cast<m>(distance)), value_cast<int>(duration)));
fixed_int_si_avg_speed(quantity_cast<si::length<si::metre, int>>(distance), quantity_cast<int>(duration)));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
// not possible to convert both a dimension and a unit with implicit cast
print_result(distance, duration, si_avg_speed(quantity_cast<si::dim_length>(distance), duration));
print_result(distance, duration, avg_speed(distance, duration)); print_result(distance, duration, avg_speed(distance, duration));
} }
} }

View File

@@ -20,47 +20,42 @@
physical_quantities physical_quantities
*/ */
#include <units/generic/dimensionless.h> #include <mp-units/iostream.h>
#include <units/isq/dimensions/electric_current.h> #include <mp-units/math.h> // IWYU pragma: keep
#include <units/isq/si/capacitance.h> #include <mp-units/systems/isq/electromagnetism.h>
#include <units/isq/si/resistance.h> #include <mp-units/systems/si/si.h>
#include <units/isq/si/time.h>
#include <units/isq/si/voltage.h>
#include <units/math.h> // IWYU pragma: keep
#include <units/quantity_io.h>
#include <iostream> #include <iostream>
int main() int main()
{ {
using namespace units::isq; using namespace mp_units;
using namespace units::isq::si::capacitance_references; using namespace mp_units::si::unit_symbols;
using namespace units::isq::si::voltage_references;
using namespace units::isq::si::resistance_references;
using namespace units::isq::si::time_references;
std::cout << "mp-units capacitor time curve example...\n"; std::cout << "mp-units capacitor time curve example...\n";
std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield); std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
std::cout.precision(3); std::cout.precision(3);
constexpr auto C = 0.47 * uF; constexpr auto C = isq::capacitance(0.47 * uF);
constexpr auto V0 = 5.0 * V; constexpr auto V0 = isq::voltage(5.0 * V);
constexpr auto RR = 4.7 * kR; // cannot use just 'R' here constexpr auto R = isq::resistance(4.7 * si::kilo<si::ohm>);
for (auto t = 0 * ms; t <= 50 * ms; ++t) { for (auto t = 0 * ms; t <= 50 * ms; ++t) {
const Voltage auto Vt = V0 * units::exp(-t / (RR * C)); const QuantityOf<isq::voltage> auto Vt = V0 * exp(dimensionless(-t / (R * C)));
// TODO try to make the below work instead
// const QuantityOf<isq::voltage> auto Vt = V0 * exp(-t / (R * C));
std::cout << "at " << t << " voltage is "; std::cout << "at " << t << " voltage is ";
if (Vt >= 1 * V) if (Vt >= 1 * V)
std::cout << Vt; std::cout << Vt[V];
else if (Vt >= 1 * mV) else if (Vt >= 1 * mV)
std::cout << quantity_cast<si::millivolt>(Vt); std::cout << Vt[mV];
else if (Vt >= 1 * uV) else if (Vt >= 1 * uV)
std::cout << quantity_cast<si::microvolt>(Vt); std::cout << Vt[uV];
else if (Vt >= 1 * nV) else if (Vt >= 1 * nV)
std::cout << quantity_cast<si::nanovolt>(Vt); std::cout << Vt[nV];
else else
std::cout << quantity_cast<si::picovolt>(Vt); std::cout << Vt[pV];
std::cout << "\n"; std::cout << "\n";
} }
} }

149
example/clcpp_response.cpp Normal file
View File

@@ -0,0 +1,149 @@
/*
Copyright (c) 2003-2019 Andy Little.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses./
*/
#include <mp-units/format.h>
#include <mp-units/iostream.h>
#include <mp-units/systems/iau/iau.h>
#include <mp-units/systems/imperial/imperial.h>
#include <mp-units/systems/international/international.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si/si.h>
#include <mp-units/systems/typographic/typographic.h>
#include <mp-units/systems/usc/usc.h>
#include <iostream>
namespace {
void simple_quantities()
{
using namespace mp_units;
using namespace mp_units::si;
using namespace mp_units::international;
using distance = quantity<isq::distance[kilo<metre>]>;
using duration = quantity<isq::duration[second]>;
constexpr distance km = 1. * kilo<metre>;
constexpr distance miles = 1. * mile;
constexpr duration sec = 1 * second;
constexpr duration min = 1 * minute;
constexpr duration hr = 1 * hour;
std::cout << "A physical quantities library can choose the simple\n";
std::cout << "option to provide output using a single type for each base unit:\n\n";
std::cout << km << '\n';
std::cout << miles << '\n';
std::cout << sec << '\n';
std::cout << min << '\n';
std::cout << hr << "\n\n";
}
void quantities_with_typed_units()
{
using namespace mp_units;
using namespace mp_units::si;
using namespace mp_units::international;
constexpr auto km = 1.0 * kilo<metre>;
constexpr auto miles = 1.0 * mile;
std::cout.precision(6);
constexpr auto sec = 1 * second;
constexpr auto min = 1 * minute;
constexpr auto hr = 1 * hour;
std::cout << "A more flexible option is to provide separate types for each unit,\n\n";
std::cout << km << '\n';
std::cout << miles << '\n';
std::cout << sec << '\n';
std::cout << min << '\n';
std::cout << hr << "\n\n";
constexpr auto meter = 1. * metre;
std::cout << "then a wide range of pre-defined units can be defined and converted,\n"
" for consistency and repeatability across applications:\n\n";
std::cout << meter << '\n';
std::cout << " = " << meter[astronomical_unit] << '\n';
std::cout << " = " << meter[iau::angstrom] << '\n';
std::cout << " = " << meter[imperial::chain] << '\n';
std::cout << " = " << meter[imperial::fathom] << '\n';
std::cout << " = " << meter[usc::fathom] << '\n';
std::cout << " = " << meter[international::foot] << '\n';
std::cout << " = " << meter[usc::survey1893::us_survey_foot] << '\n';
std::cout << " = " << meter[international::inch] << '\n';
std::cout << " = " << meter[iau::light_year] << '\n';
std::cout << " = " << meter[international::mile] << '\n';
std::cout << " = " << meter[international::nautical_mile] << '\n';
std::cout << " = " << meter[iau::parsec] << '\n';
std::cout << " = " << meter[typographic::pica_dtp] << '\n';
std::cout << " = " << meter[typographic::pica_us] << '\n';
std::cout << " = " << meter[typographic::point_dtp] << '\n';
std::cout << " = " << meter[typographic::point_us] << '\n';
std::cout << " = " << meter[imperial::rod] << '\n';
std::cout << " = " << meter[international::yard] << '\n';
}
void calcs_comparison()
{
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
std::cout << "\nA distinct unit for each type is efficient and accurate\n"
"when adding two values of the same very big\n"
"or very small type:\n\n";
const auto L1A = 2.f * fm;
const auto L2A = 3.f * fm;
const auto LrA = L1A + L2A;
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n + {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, LrA);
std::cout << "The single unit method must convert large\n"
"or small values in other units to the base unit.\n"
"This is both inefficient and inaccurate\n\n";
const auto L1B = L1A[m];
const auto L2B = L2A[m];
const auto LrB = L1B + L2B;
std::cout << UNITS_STD_FMT::format("{:%.30eQ %q}\n + {:%.30eQ %q}\n = {:%.30eQ %q}\n\n", L1B, L2B, LrB);
std::cout << "In multiplication and division:\n\n";
const quantity<isq::area[square(fm)], float> ArA = L1A * L2A;
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n * {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, ArA);
std::cout << "similar problems arise\n\n";
const quantity<isq::area[m2], float> ArB = L1B * L2B;
std::cout << UNITS_STD_FMT::format("{:%.30eQ %q}\n * {:%.30eQ %q}\n = {:%.30eQ %q}\n\n", L1B, L2B, ArB);
}
} // namespace
int main()
{
std::cout << "This demo was originally posted on com.lang.c++.moderated in 2006\n";
std::cout << "https://groups.google.com/g/comp.lang.c++.moderated/c/upv7hZExtf4/m/XruKUk8LhXYJ\n";
std::cout << "Here converted to use mp-units library.\n\n";
simple_quantities();
quantities_with_typed_units();
calcs_comparison();
}

View File

@@ -15,8 +15,10 @@
along with this program. If not, see http://www.gnu.org/licenses./ along with this program. If not, see http://www.gnu.org/licenses./
*/ */
#include <units/format.h> #include <mp-units/format.h>
#include <units/isq/si/length.h> #include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si/unit_symbols.h>
#include <mp-units/systems/si/units.h>
#include <iostream> #include <iostream>
#include <type_traits> #include <type_traits>
@@ -27,27 +29,24 @@
namespace { namespace {
template<units::Quantity Target, units::Quantity Source> template<mp_units::Quantity Target, mp_units::Quantity Source>
requires units::equivalent<typename Source::dimension, typename Target::dimension> requires std::constructible_from<Target, Source>
inline constexpr std::common_type_t<typename Target::rep, typename Source::rep> conversion_factor(Target, Source) inline constexpr double conversion_factor(Target, Source)
{ {
// get quantities looking like inputs but with Q::rep that doesn't have narrowing conversion return value_cast<Target::unit>(1. * Source::reference).number();
typedef std::common_type_t<typename Target::rep, typename Source::rep> rep;
typedef units::quantity<typename Source::dimension, typename Source::unit, rep> source;
typedef units::quantity<typename Target::dimension, typename Target::unit, rep> target;
return target{source{1}}.number();
} }
} // namespace } // namespace
int main() int main()
{ {
using namespace units::isq::si; using namespace mp_units;
using namespace mp_units::si::unit_symbols;
std::cout << "conversion factor in mp-units...\n\n"; std::cout << "conversion factor in mp-units...\n\n";
constexpr length<metre> lengthA(2.0); constexpr auto lengthA = 2.0 * m;
constexpr length<millimetre> lengthB = lengthA; constexpr auto lengthB = lengthA[mm];
std::cout << UNITS_STD_FMT::format("lengthA( {} ) and lengthB( {} )\n", lengthA, lengthB) std::cout << UNITS_STD_FMT::format("lengthA( {} ) and lengthB( {} )\n", lengthA, lengthB)
<< "represent the same length in different units.\n\n"; << "represent the same length in different units.\n\n";

91
example/currency.cpp Normal file
View File

@@ -0,0 +1,91 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <mp-units/iostream.h>
#include <mp-units/quantity.h>
#include <mp-units/quantity_point.h>
#include <iostream>
#include <map>
using namespace mp_units;
// clang-format off
inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency;
QUANTITY_SPEC(currency, dim_currency);
inline constexpr struct euro : named_unit<"EUR", kind_of<currency>> {} euro;
inline constexpr struct us_dollar : named_unit<"USD", kind_of<currency>> {} us_dollar;
inline constexpr struct great_british_pound : named_unit<"GBP", kind_of<currency>> {} great_british_pound;
inline constexpr struct japanese_jen : named_unit<"JPY", kind_of<currency>> {} japanese_jen;
// clang-format on
static_assert(!std::equality_comparable_with<quantity<euro, int>, quantity<us_dollar, int>>);
#if 0
// if you have only a few currencies to handle
template<Unit auto From, Unit auto To>
[[nodiscard]] double exchange_rate()
{
if constexpr (From == us_dollar && To == euro) return 0.9215;
else if constexpr (From == euro && To == us_dollar) return 1.0848;
// ...
}
#else
[[nodiscard]] std::string_view to_string_view(Unit auto u) { return u.symbol.ascii().c_str(); }
template<Unit auto From, Unit auto To>
[[nodiscard]] double exchange_rate()
{
static std::map<std::pair<std::string_view, std::string_view>, double> rates = {
{{"USD", "EUR"}, 0.9215}, {{"EUR", "USD"}, 1.0848},
// ...
};
return rates[std::make_pair(to_string_view(From), to_string_view(To))];
}
#endif
template<ReferenceOf<currency> auto To, ReferenceOf<currency> auto From, typename Rep>
quantity<To, Rep> exchange_to(quantity<From, Rep> q)
{
return static_cast<Rep>(exchange_rate<q.unit, get_unit(To)>() * q.number()) * To;
}
template<ReferenceOf<currency> auto To, ReferenceOf<currency> auto From, auto PO, typename Rep>
quantity_point<To, PO, Rep> exchange_to(quantity_point<From, PO, Rep> q)
{
return quantity_point{static_cast<Rep>(exchange_rate<q.unit, get_unit(To)>() * q.absolute().number()) * To};
}
int main()
{
auto price_usd = quantity_point{100 * us_dollar};
auto price_euro = quantity_point{exchange_to<euro>(price_usd)};
std::cout << price_usd.absolute() << " -> " << price_euro.absolute() << "\n";
// std::cout << price_usd.absolute() + price_euro.absolute() << "\n"; // does not compile
}

View File

@@ -1,117 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/base_dimension.h>
#include <units/isq/si/prefixes.h>
#include <units/quantity.h>
#include <units/quantity_io.h>
#include <units/unit.h>
#include <iostream>
#include <type_traits>
using namespace units;
namespace fps {
struct foot : named_unit<foot, "ft"> {};
struct yard : named_scaled_unit<yard, "yd", mag<3>(), foot> {};
struct dim_length : base_dimension<"L", foot> {};
template<UnitOf<dim_length> U, Representation Rep = double>
using length = quantity<dim_length, U, Rep>;
} // namespace fps
namespace si {
struct metre : named_unit<metre, "m"> {};
struct kilometre : prefixed_unit<kilometre, units::isq::si::kilo, metre> {};
struct dim_length : base_dimension<"L", metre> {};
template<UnitOf<dim_length> U, Representation Rep = double>
using length = quantity<dim_length, U, Rep>;
namespace fps {
struct foot : named_scaled_unit<foot, "ft", mag<ratio{3'048, 10'000}>(), metre> {};
struct yard : named_scaled_unit<yard, "yd", mag<3>(), foot> {};
struct dim_length : base_dimension<"L", foot> {};
template<UnitOf<dim_length> U, Representation Rep = double>
using length = quantity<dim_length, U, Rep>;
} // namespace fps
} // namespace si
template<typename Q, typename U>
concept castable_to = Quantity<Q> && Unit<U> && requires(Q q) { quantity_cast<U>(q); };
void conversions()
{
// fps::yard is not defined in terms of SI units (or vice-versa)
// so the conversion between FPS and SI is not possible
static_assert(!castable_to<fps::length<fps::yard>, si::kilometre>);
// si::fps::yard is defined in terms of SI units
// so the conversion between FPS and SI is possible
static_assert(castable_to<si::fps::length<si::fps::yard>, si::kilometre>);
constexpr auto si_fps_yard = si::fps::length<si::fps::yard>(1.);
std::cout << quantity_cast<si::kilometre>(si_fps_yard) << "\n";
}
void unknown_dimensions()
{
constexpr auto fps_yard = fps::length<fps::yard>(1.);
constexpr auto fps_area = fps_yard * fps_yard;
std::cout << fps_yard << "\n";
std::cout << quantity_cast<decltype(fps_area)::dimension::coherent_unit>(fps_area) << "\n";
constexpr auto si_fps_yard = si::fps::length<si::fps::yard>(1.);
constexpr auto si_fps_area = si_fps_yard * si_fps_yard;
std::cout << si_fps_yard << "\n";
std::cout << quantity_cast<decltype(si_fps_area)::dimension::coherent_unit>(si_fps_area) << "\n";
}
std::ostream& operator<<(std::ostream& os, const ratio& r) { return os << "ratio{" << r.num << ", " << r.den << "}"; }
template<Unit U>
std::ostream& operator<<(std::ostream& os, const U& u)
{
using unit_type = std::remove_cvref_t<decltype(u)>;
return os << as_ratio(unit_type::mag) << " x " << unit_type::reference::symbol.standard();
}
void what_is_your_ratio()
{
std::cout << "fps: " << fps::yard() << "\n";
std::cout << "si::fps: " << si::fps::yard() << "\n";
}
int main()
{
conversions();
unknown_dimensions();
what_is_your_ratio();
}

View File

@@ -0,0 +1,118 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <mp-units/format.h>
#include <mp-units/systems/imperial/imperial.h>
#include <mp-units/systems/international/international.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si/unit_symbols.h>
#include <iostream>
#include <string_view>
using namespace mp_units;
using namespace mp_units::international::unit_symbols;
using namespace mp_units::si::unit_symbols;
// Some basic specs for the warship
struct Ship {
quantity<isq::length[ft]> length;
quantity<isq::length[ft]> draft;
quantity<isq::length[ft]> beam;
quantity<isq::speed[ft / s]> speed;
quantity<isq::mass[lb]> mass;
quantity<isq::length[in]> mainGuns;
quantity<isq::mass[lb]> shellMass;
quantity<isq::speed[ft / s]> shellSpeed;
quantity<isq::power[ft * pdl / s]> power;
};
// Print 'q' in its current units and print its value cast to the units in each of Us
template<Unit auto... Us, Quantity Q>
auto fmt_line(const Q& q)
{
return UNITS_STD_FMT::format("{:22}", q) + (UNITS_STD_FMT::format(",{:20}", value_cast<Us>(q)) + ...);
}
// Print the ship details in the units as defined in the Ship struct, in other si::imperial units, and in SI
void print_details(std::string_view description, const Ship& ship)
{
const auto waterDensity = 62.4 * isq::density[lb / cubic(ft)];
std::cout << UNITS_STD_FMT::format("{}\n", description);
std::cout << UNITS_STD_FMT::format("{:20} : {}\n", "length", fmt_line<yd, m>(ship.length))
<< UNITS_STD_FMT::format("{:20} : {}\n", "draft", fmt_line<yd, m>(ship.draft))
<< UNITS_STD_FMT::format("{:20} : {}\n", "beam", fmt_line<yd, m>(ship.beam))
<< UNITS_STD_FMT::format("{:20} : {}\n", "mass", fmt_line<imperial::long_ton, t>(ship.mass))
<< UNITS_STD_FMT::format("{:20} : {}\n", "speed", fmt_line<kt, km / h>(ship.speed))
<< UNITS_STD_FMT::format("{:20} : {}\n", "power", fmt_line<hp, kW>(ship.power))
<< UNITS_STD_FMT::format("{:20} : {}\n", "main guns", fmt_line<in, mm>(ship.mainGuns))
<< UNITS_STD_FMT::format("{:20} : {}\n", "fire shells weighing",
fmt_line<imperial::long_ton, kg>(ship.shellMass))
<< UNITS_STD_FMT::format("{:20} : {}\n", "fire shells at", fmt_line<mph, km / h>(ship.shellSpeed))
<< UNITS_STD_FMT::format("{:20} : {}\n", "volume underwater", fmt_line<m3, l>(ship.mass / waterDensity));
}
int main()
{
using mp_units::international::unit_symbols::ft; // collides with si::femto<si::tonne>
// KMS Bismark, using the units the Germans would use, taken from Wiki
auto bismark = Ship{.length{251. * m},
.draft{9.3 * m},
.beam{36 * m},
.speed{56 * (km / h)},
.mass{50'300 * t},
.mainGuns{380 * mm},
.shellMass{800 * kg},
.shellSpeed{820. * (m / s)},
.power{110.45 * kW}};
// USS Iowa, using units from the foot-pound-second system
auto iowa = Ship{.length{860. * ft},
.draft{37. * ft + 2. * in},
.beam{108. * ft + 2. * in},
.speed{33 * kt},
.mass{57'540 * imperial::long_ton},
.mainGuns{16 * in},
.shellMass{2700 * lb},
.shellSpeed{2690. * (ft / s)},
.power{212'000 * hp}};
// HMS King George V, using units from the foot-pound-second system
auto kgv = Ship{.length{745.1 * ft},
.draft{33. * ft + 7.5 * in},
.beam{103.2 * ft + 2.5 * in},
.speed{28.3 * kt},
.mass{42'245 * imperial::long_ton},
.mainGuns{14 * in},
.shellMass{1590 * lb},
.shellSpeed{2483. * (ft / s)},
.power{110'000 * hp}};
print_details("KMS Bismark, defined in appropriate units from the SI system", bismark);
std::cout << "\n\n";
print_details("USS Iowa, defined in appropriate units foot-pound-second system", iowa);
std::cout << "\n\n";
print_details("HMS King George V, defined in appropriate units foot-pound-second system", kgv);
}

View File

@@ -21,11 +21,11 @@
// SOFTWARE. // SOFTWARE.
#include "glide_computer.h" #include "glide_computer.h"
#include <units/bits/fmt_hacks.h> #include <mp-units/bits/fmt_hacks.h>
#include <units/chrono.h> #include <mp-units/chrono.h>
#include <units/generic/angle.h> #include <mp-units/math.h>
#include <units/generic/dimensionless.h> #include <mp-units/systems/international/international.h>
#include <units/isq/si/international/length.h> #include <mp-units/systems/si/unit_symbols.h>
#include <array> #include <array>
#include <exception> #include <exception>
#include <iostream> #include <iostream>
@@ -36,41 +36,39 @@
namespace { namespace {
using namespace geographic;
using namespace glide_computer; using namespace glide_computer;
using namespace mp_units;
using namespace units::isq;
auto get_gliders() auto get_gliders()
{ {
using namespace units::aliases::isq::si; using namespace mp_units::si::unit_symbols;
UNITS_DIAGNOSTIC_PUSH UNITS_DIAGNOSTIC_PUSH
UNITS_DIAGNOSTIC_IGNORE_MISSING_BRACES UNITS_DIAGNOSTIC_IGNORE_MISSING_BRACES
static const std::array gliders = { static const std::array gliders = {glider{"SZD-30 Pirat", {83 * (km / h), -0.7389 * (m / s)}},
glider{"SZD-30 Pirat", {velocity(km_per_h<>(83)), rate_of_climb(m_per_s<>(-0.7389))}}, glider{"SZD-51 Junior", {80 * (km / h), -0.6349 * (m / s)}},
glider{"SZD-51 Junior", {velocity(km_per_h<>(80)), rate_of_climb(m_per_s<>(-0.6349))}}, glider{"SZD-48 Jantar Std 3", {110 * (km / h), -0.77355 * (m / s)}},
glider{"SZD-48 Jantar Std 3", {velocity(km_per_h<>(110)), rate_of_climb(m_per_s<>(-0.77355))}}, glider{"SZD-56 Diana", {110 * (km / h), -0.63657 * (m / s)}}};
glider{"SZD-56 Diana", {velocity(km_per_h<>(110)), rate_of_climb(m_per_s<>(-0.63657))}}};
UNITS_DIAGNOSTIC_POP UNITS_DIAGNOSTIC_POP
return gliders; return gliders;
} }
auto get_weather_conditions() auto get_weather_conditions()
{ {
using namespace units::aliases::isq::si; using namespace mp_units::si::unit_symbols;
static const std::array weather_conditions = { static const std::array weather_conditions = {std::pair{"Good", weather{1900 * m, 4.3 * (m / s)}},
std::pair("Good", weather{height(m<>(1900)), rate_of_climb(m_per_s<>(4.3))}), std::pair{"Medium", weather{1550 * m, 2.8 * (m / s)}},
std::pair("Medium", weather{height(m<>(1550)), rate_of_climb(m_per_s<>(2.8))}), std::pair{"Bad", weather{850 * m, 1.8 * (m / s)}}};
std::pair("Bad", weather{height(m<>(850)), rate_of_climb(m_per_s<>(1.8))})};
return weather_conditions; return weather_conditions;
} }
auto get_waypoints() auto get_waypoints()
{ {
using namespace geographic::literals; using namespace geographic::literals;
using namespace units::aliases::isq::si::international; using namespace mp_units::international::unit_symbols;
static const std::array waypoints = { static const std::array waypoints = {
waypoint{"EPPR", {54.24772_N, 18.6745_E}, altitude(ft<>(16))}, // N54°14'51.8" E18°40'28.2" waypoint{"EPPR", {54.24772_N, 18.6745_E}, msl_altitude{16. * ft}}, // N54°14'51.8" E18°40'28.2"
waypoint{"EPGI", {53.52442_N, 18.84947_E}, altitude(ft<>(115))} // N53°31'27.9" E18°50'58.1" waypoint{"EPGI", {53.52442_N, 18.84947_E}, msl_altitude{115. * ft}} // N53°31'27.9" E18°50'58.1"
}; };
return waypoints; return waypoints;
} }
@@ -85,9 +83,12 @@ void print(const R& gliders)
std::cout << "- Name: " << g.name << "\n"; std::cout << "- Name: " << g.name << "\n";
std::cout << "- Polar:\n"; std::cout << "- Polar:\n";
for (const auto& p : g.polar) { for (const auto& p : g.polar) {
const auto ratio = units::quantity_cast<units::one>(glide_ratio(g.polar[0])); const auto ratio = value_cast<one>(glide_ratio(g.polar[0]));
std::cout << UNITS_STD_FMT::format(" * {:%.4Q %q} @ {:%.1Q %q} -> {:%.1Q %q} ({:%.1Q %q})\n", p.climb, p.v, std::cout << UNITS_STD_FMT::format(" * {:%.4Q %q} @ {:%.1Q %q} -> {:%.1Q %q} ({:%.1Q %q})\n", p.climb, p.v,
ratio, units::quantity_cast<units::degree>(asin(1 / ratio))); ratio,
// TODO is it possible to make ADL work below (we need another set of trig
// functions for strong angle in a different namespace)
value_cast<si::degree>(isq::asin(1 / ratio)));
} }
std::cout << "\n"; std::cout << "\n";
} }
@@ -126,12 +127,12 @@ void print(const task& t)
std::cout << "- Start: " << t.get_start().name << "\n"; std::cout << "- Start: " << t.get_start().name << "\n";
std::cout << "- Finish: " << t.get_finish().name << "\n"; std::cout << "- Finish: " << t.get_finish().name << "\n";
std::cout << "- Length: " << UNITS_STD_FMT::format("{:%.1Q %q}", t.get_length()) << "\n"; std::cout << "- Length: " << UNITS_STD_FMT::format("{:%.1Q %q}", t.get_distance()) << "\n";
std::cout << "- Legs: " std::cout << "- Legs: "
<< "\n"; << "\n";
for (const auto& l : t.get_legs()) for (const auto& l : t.get_legs())
std::cout << UNITS_STD_FMT::format(" * {} -> {} ({:%.1Q %q})\n", l.begin().name, l.end().name, l.get_length()); std::cout << UNITS_STD_FMT::format(" * {} -> {} ({:%.1Q %q})\n", l.begin().name, l.end().name, l.get_distance());
std::cout << "\n"; std::cout << "\n";
} }
@@ -155,14 +156,14 @@ void print(const aircraft_tow& tow)
void example() void example()
{ {
using namespace units::aliases::isq::si; using namespace mp_units::si::unit_symbols;
const safety sfty = {height(m<>(300))}; const safety sfty = {300 * m};
const auto gliders = get_gliders(); const auto gliders = get_gliders();
const auto waypoints = get_waypoints(); const auto waypoints = get_waypoints();
const auto weather_conditions = get_weather_conditions(); const auto weather_conditions = get_weather_conditions();
const task t = {waypoints[0], waypoints[1], waypoints[0]}; const task t = {waypoints[0], waypoints[1], waypoints[0]};
const aircraft_tow tow = {height(m<>(400)), rate_of_climb(m_per_s<>(1.6))}; const aircraft_tow tow = {400 * m, 1.6 * (m / s)};
// TODO use C++20 date library when available // TODO use C++20 date library when available
// set `start_time` to 11:00 am today // set `start_time` to 11:00 am today
const timestamp start_time(std::chrono::system_clock::now()); const timestamp start_time(std::chrono::system_clock::now());

View File

@@ -22,6 +22,6 @@
cmake_minimum_required(VERSION 3.2) cmake_minimum_required(VERSION 3.2)
add_library(glide_computer STATIC include/geographic.h glide_computer.cpp include/glide_computer.h) add_library(glide_computer_lib STATIC glide_computer.cpp include/glide_computer.h)
target_link_libraries(glide_computer PRIVATE mp-units::core-fmt PUBLIC mp-units::si example_utils) target_link_libraries(glide_computer_lib PRIVATE mp-units::core-fmt PUBLIC mp-units::si mp-units::utility example_utils)
target_include_directories(glide_computer PUBLIC include) target_include_directories(glide_computer_lib PUBLIC include)

View File

@@ -21,13 +21,14 @@
// SOFTWARE. // SOFTWARE.
#include "glide_computer.h" #include "glide_computer.h"
#include <mp-units/format.h>
#include <iostream> #include <iostream>
#include <numeric> #include <numeric>
#include <string_view> #include <string_view>
namespace glide_computer { namespace glide_computer {
using namespace units::isq; using namespace mp_units;
task::legs task::make_legs(const waypoints& wpts) task::legs task::make_legs(const waypoints& wpts)
{ {
@@ -43,26 +44,27 @@ std::vector<distance> task::make_leg_total_distances(const legs& legs)
{ {
std::vector<distance> res; std::vector<distance> res;
res.reserve(legs.size()); res.reserve(legs.size());
auto to_length = [](const leg& l) { return l.get_length(); }; auto to_length = [](const leg& l) { return l.get_distance(); };
std::transform_inclusive_scan(legs.cbegin(), legs.cend(), std::back_inserter(res), std::plus(), to_length); std::transform_inclusive_scan(legs.cbegin(), legs.cend(), std::back_inserter(res), std::plus(), to_length);
return res; return res;
} }
altitude terrain_level_alt(const task& t, const flight_point& pos) geographic::msl_altitude terrain_level_alt(const task& t, const flight_point& pos)
{ {
const task::leg& l = t.get_legs()[pos.leg_idx]; const task::leg& l = t.get_legs()[pos.leg_idx];
const height alt_diff = l.end().alt - l.begin().alt; const height alt_diff = l.end().alt - l.begin().alt;
return l.begin().alt + alt_diff * ((pos.dist - t.get_leg_dist_offset(pos.leg_idx)) / l.get_length()).common(); return l.begin().alt + alt_diff * ((pos.dist - t.get_leg_dist_offset(pos.leg_idx)) / l.get_distance());
} }
// Returns `x` of the intersection of a glide line and a terrain line. // Returns `x` of the intersection of a glide line and a terrain line.
// y = -x / glide_ratio + pos.alt; // y = -x / glide_ratio + pos.alt;
// y = (finish_alt - ground_alt) / dist_to_finish * x + ground_alt + min_agl_height; // y = (finish_alt - ground_alt) / dist_to_finish * x + ground_alt + min_agl_height;
distance glide_distance(const flight_point& pos, const glider& g, const task& t, const safety& s, altitude ground_alt) distance glide_distance(const flight_point& pos, const glider& g, const task& t, const safety& s,
geographic::msl_altitude ground_alt)
{ {
const auto dist_to_finish = t.get_length() - pos.dist; const auto dist_to_finish = t.get_distance() - pos.dist;
return distance((ground_alt + s.min_agl_height - pos.alt).common() / return quantity_cast<isq::distance>(ground_alt + s.min_agl_height - pos.alt) /
((ground_alt - t.get_finish().alt) / dist_to_finish - 1 / glide_ratio(g.polar[0]))); ((ground_alt - t.get_finish().alt) / dist_to_finish - 1 / glide_ratio(g.polar[0]));
} }
} // namespace glide_computer } // namespace glide_computer
@@ -77,7 +79,7 @@ void print(std::string_view phase_name, timestamp start_ts, const glide_computer
std::cout << UNITS_STD_FMT::format( std::cout << UNITS_STD_FMT::format(
"| {:<12} | {:>9%.1Q %q} (Total: {:>9%.1Q %q}) | {:>8%.1Q %q} (Total: {:>8%.1Q %q}) | {:>7%.0Q %q} ({:>6%.0Q %q}) " "| {:<12} | {:>9%.1Q %q} (Total: {:>9%.1Q %q}) | {:>8%.1Q %q} (Total: {:>8%.1Q %q}) | {:>7%.0Q %q} ({:>6%.0Q %q}) "
"|\n", "|\n",
phase_name, quantity_cast<si::minute>(new_point.ts - point.ts), quantity_cast<si::minute>(new_point.ts - start_ts), phase_name, value_cast<si::minute>(new_point.ts - point.ts), value_cast<si::minute>(new_point.ts - start_ts),
new_point.dist - point.dist, new_point.dist, new_point.alt - point.alt, new_point.alt); new_point.dist - point.dist, new_point.dist, new_point.alt - point.alt, new_point.alt);
} }
@@ -85,7 +87,7 @@ flight_point takeoff(timestamp start_ts, const task& t) { return {start_ts, t.ge
flight_point tow(timestamp start_ts, const flight_point& pos, const aircraft_tow& at) flight_point tow(timestamp start_ts, const flight_point& pos, const aircraft_tow& at)
{ {
const duration d = (at.height_agl / at.performance).common(); const duration d = (at.height_agl / at.performance);
const flight_point new_pos{pos.ts + d, pos.alt + at.height_agl, pos.leg_idx, pos.dist}; const flight_point new_pos{pos.ts + d, pos.alt + at.height_agl, pos.leg_idx, pos.dist};
print("Tow", start_ts, pos, new_pos); print("Tow", start_ts, pos, new_pos);
@@ -98,7 +100,7 @@ flight_point circle(timestamp start_ts, const flight_point& pos, const glider& g
const height h_agl = agl(pos.alt, terrain_level_alt(t, pos)); const height h_agl = agl(pos.alt, terrain_level_alt(t, pos));
const height circling_height = std::min(w.cloud_base - h_agl, height_to_gain); const height circling_height = std::min(w.cloud_base - h_agl, height_to_gain);
const rate_of_climb circling_rate = w.thermal_strength + g.polar[0].climb; const rate_of_climb circling_rate = w.thermal_strength + g.polar[0].climb;
const duration d = (circling_height / circling_rate).common(); const duration d = (circling_height / circling_rate);
const flight_point new_pos{pos.ts + d, pos.alt + circling_height, pos.leg_idx, pos.dist}; const flight_point new_pos{pos.ts + d, pos.alt + circling_height, pos.leg_idx, pos.dist};
height_to_gain -= circling_height; height_to_gain -= circling_height;
@@ -114,7 +116,7 @@ flight_point glide(timestamp start_ts, const flight_point& pos, const glider& g,
const auto new_distance = pos.dist + dist; const auto new_distance = pos.dist + dist;
const auto alt = ground_alt + s.min_agl_height; const auto alt = ground_alt + s.min_agl_height;
const auto l3d = length_3d(dist, pos.alt - alt); const auto l3d = length_3d(dist, pos.alt - alt);
const duration d = l3d / g.polar[0].v.common(); const duration d = l3d / g.polar[0].v;
const flight_point new_pos{pos.ts + d, terrain_level_alt(t, pos) + s.min_agl_height, t.get_leg_index(new_distance), const flight_point new_pos{pos.ts + d, terrain_level_alt(t, pos) + s.min_agl_height, t.get_leg_index(new_distance),
new_distance}; new_distance};
@@ -124,9 +126,9 @@ flight_point glide(timestamp start_ts, const flight_point& pos, const glider& g,
flight_point final_glide(timestamp start_ts, const flight_point& pos, const glider& g, const task& t) flight_point final_glide(timestamp start_ts, const flight_point& pos, const glider& g, const task& t)
{ {
const auto dist = t.get_length() - pos.dist; const auto dist = t.get_distance() - pos.dist;
const auto l3d = length_3d(dist, pos.alt - t.get_finish().alt); const auto l3d = length_3d(dist, pos.alt - t.get_finish().alt);
const duration d = l3d / g.polar[0].v.common(); const duration d = l3d / g.polar[0].v;
const flight_point new_pos{pos.ts + d, t.get_finish().alt, t.get_legs().size() - 1, pos.dist + dist}; const flight_point new_pos{pos.ts + d, t.get_finish().alt, t.get_legs().size() - 1, pos.dist + dist};
print("Final Glide", start_ts, pos, new_pos); print("Final Glide", start_ts, pos, new_pos);
@@ -150,8 +152,9 @@ void estimate(timestamp start_ts, const glider& g, const weather& w, const task&
// estimate aircraft towing // estimate aircraft towing
pos = tow(start_ts, pos, at); pos = tow(start_ts, pos, at);
// estimate the altitude needed to reach the finish line from this place // estimate the msl_altitude needed to reach the finish line from this place
const altitude final_glide_alt = t.get_finish().alt + height(t.get_length().common() / glide_ratio(g.polar[0])); const geographic::msl_altitude final_glide_alt =
t.get_finish().alt + quantity_cast<isq::height>(t.get_distance() / glide_ratio(g.polar[0]));
// how much height we still need to gain in the thermalls to reach the destination? // how much height we still need to gain in the thermalls to reach the destination?
height height_to_gain = final_glide_alt - pos.alt; height height_to_gain = final_glide_alt - pos.alt;

View File

@@ -22,17 +22,11 @@
#pragma once #pragma once
// IWYU pragma: begin_exports
#include "geographic.h" #include "geographic.h"
#include <units/isq/si/length.h> #include <mp-units/chrono.h>
#include <units/isq/si/speed.h> #include <mp-units/math.h> // IWYU pragma: keep
#include <units/isq/si/time.h> #include <mp-units/quantity_point.h>
#include <units/quantity_point_kind.h> #include <mp-units/systems/isq/space_and_time.h>
// IWYU pragma: end_exports
#include <units/chrono.h>
#include <units/format.h>
#include <units/math.h> // IWYU pragma: keep
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <initializer_list> #include <initializer_list>
@@ -56,67 +50,25 @@
// - no ground obstacles (i.e. mountains) to pass // - no ground obstacles (i.e. mountains) to pass
// - flight path exactly on a shortest possible line to destination // - flight path exactly on a shortest possible line to destination
template<units::QuantityKind QK>
struct UNITS_STD_FMT::formatter<QK> : formatter<typename QK::quantity_type> {
template<typename FormatContext>
auto format(const QK& v, FormatContext& ctx)
{
return formatter<typename QK::quantity_type>::format(v.common(), ctx);
}
};
namespace glide_computer { namespace glide_computer {
template<units::QuantityKind QK1, units::QuantityKind QK2>
constexpr units::Dimensionless auto operator/(const QK1& lhs, const QK2& rhs)
requires(!units::QuantityKindRelatedTo<QK1, QK2>) && requires { lhs.common() / rhs.common(); }
{
return lhs.common() / rhs.common();
}
// kinds
using horizontal_kind = geographic::horizontal_kind;
struct vertical_kind : units::kind<vertical_kind, units::isq::si::dim_length> {};
struct vertical_point_kind : units::point_kind<vertical_point_kind, vertical_kind> {};
struct velocity_kind : units::derived_kind<velocity_kind, units::isq::si::dim_speed, horizontal_kind> {};
struct rate_of_climb_kind : units::derived_kind<rate_of_climb_kind, units::isq::si::dim_speed, vertical_kind> {};
// https://en.wikipedia.org/wiki/Flight_planning#Units_of_measurement // https://en.wikipedia.org/wiki/Flight_planning#Units_of_measurement
QUANTITY_SPEC(rate_of_climb_speed, mp_units::isq::speed, mp_units::isq::height / mp_units::isq::time);
// length // length
using distance = units::quantity_kind<horizontal_kind, units::isq::si::kilometre>; using distance = mp_units::quantity<mp_units::isq::distance[mp_units::si::kilo<mp_units::si::metre>]>;
using height = units::quantity_kind<vertical_kind, units::isq::si::metre>; using height = mp_units::quantity<mp_units::isq::height[mp_units::si::metre]>;
using altitude = units::quantity_point_kind<vertical_point_kind, units::isq::si::metre>;
// time // time
using duration = units::isq::si::time<units::isq::si::second>; using duration = mp_units::quantity<mp_units::isq::duration[mp_units::si::second]>;
using timestamp = units::quantity_point<units::clock_origin<std::chrono::system_clock>, units::isq::si::second>; using timestamp = mp_units::quantity_point<mp_units::isq::time[mp_units::si::second],
mp_units::chrono_point_origin<std::chrono::system_clock>>;
// speed // speed
using velocity = units::quantity_kind<velocity_kind, units::isq::si::kilometre_per_hour>; using velocity = mp_units::quantity<mp_units::isq::speed[mp_units::si::kilo<mp_units::si::metre> / mp_units::si::hour]>;
using rate_of_climb = units::quantity_kind<rate_of_climb_kind, units::isq::si::metre_per_second>; using rate_of_climb = mp_units::quantity<rate_of_climb_speed[mp_units::si::metre / mp_units::si::second]>;
// text output
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const altitude& a)
{
return os << a.relative().common() << " AMSL";
}
} // namespace glide_computer
template<>
struct UNITS_STD_FMT::formatter<glide_computer::altitude> : formatter<units::isq::si::length<units::isq::si::metre>> {
template<typename FormatContext>
auto format(glide_computer::altitude a, FormatContext& ctx)
{
formatter<units::isq::si::length<units::isq::si::metre>>::format(a.relative().common(), ctx);
return UNITS_STD_FMT::format_to(ctx.out(), " AMSL");
}
};
// definition of glide computer databases and utilities // definition of glide computer databases and utilities
namespace glide_computer {
struct glider { struct glider {
struct polar_point { struct polar_point {
velocity v; velocity v;
@@ -127,7 +79,10 @@ struct glider {
std::array<polar_point, 1> polar; std::array<polar_point, 1> polar;
}; };
constexpr units::Dimensionless auto glide_ratio(const glider::polar_point& polar) { return polar.v / -polar.climb; } constexpr mp_units::QuantityOf<mp_units::dimensionless> auto glide_ratio(const glider::polar_point& polar)
{
return polar.v / -polar.climb;
}
struct weather { struct weather {
height cloud_base; height cloud_base;
@@ -137,7 +92,7 @@ struct weather {
struct waypoint { struct waypoint {
std::string name; std::string name;
geographic::position<long double> pos; geographic::position<long double> pos;
altitude alt; geographic::msl_altitude alt;
}; };
class task { class task {
@@ -152,7 +107,7 @@ public:
leg(const waypoint& b, const waypoint& e) noexcept : begin_(&b), end_(&e) {} leg(const waypoint& b, const waypoint& e) noexcept : begin_(&b), end_(&e) {}
constexpr const waypoint& begin() const { return *begin_; }; constexpr const waypoint& begin() const { return *begin_; };
constexpr const waypoint& end() const { return *end_; } constexpr const waypoint& end() const { return *end_; }
constexpr const distance get_length() const { return length_; } constexpr distance get_distance() const { return length_; }
}; };
using legs = std::vector<leg>; using legs = std::vector<leg>;
@@ -170,7 +125,7 @@ public:
const waypoint& get_start() const { return waypoints_.front(); } const waypoint& get_start() const { return waypoints_.front(); }
const waypoint& get_finish() const { return waypoints_.back(); } const waypoint& get_finish() const { return waypoints_.back(); }
distance get_length() const { return length_; } distance get_distance() const { return length_; }
distance get_leg_dist_offset(std::size_t leg_index) const distance get_leg_dist_offset(std::size_t leg_index) const
{ {
@@ -203,21 +158,26 @@ struct aircraft_tow {
struct flight_point { struct flight_point {
timestamp ts; timestamp ts;
altitude alt; geographic::msl_altitude alt;
std::size_t leg_idx = 0; std::size_t leg_idx = 0;
distance dist{}; distance dist{};
}; };
altitude terrain_level_alt(const task& t, const flight_point& pos); geographic::msl_altitude terrain_level_alt(const task& t, const flight_point& pos);
constexpr height agl(altitude glider_alt, altitude terrain_level) { return glider_alt - terrain_level; } constexpr height agl(geographic::msl_altitude glider_alt, geographic::msl_altitude terrain_level)
inline units::isq::si::length<units::isq::si::kilometre> length_3d(distance dist, height h)
{ {
return hypot(dist.common(), h.common()); return glider_alt - terrain_level;
} }
distance glide_distance(const flight_point& pos, const glider& g, const task& t, const safety& s, altitude ground_alt); inline mp_units::quantity<mp_units::isq::length[mp_units::si::kilo<mp_units::si::metre>]> length_3d(distance dist,
height h)
{
return hypot(dist, h);
}
distance glide_distance(const flight_point& pos, const glider& g, const task& t, const safety& s,
geographic::msl_altitude ground_alt);
void estimate(timestamp start_ts, const glider& g, const weather& w, const task& t, const safety& s, void estimate(timestamp start_ts, const glider& g, const weather& w, const task& t, const safety& s,
const aircraft_tow& at); const aircraft_tow& at);

View File

@@ -20,37 +20,32 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
#include <units/format.h> #include <mp-units/format.h>
#include <units/isq/si/international/length.h> #include <mp-units/iostream.h>
#include <units/isq/si/international/speed.h> // IWYU pragma: keep #include <mp-units/systems/international/international.h>
#include <units/isq/si/length.h> #include <mp-units/systems/isq/space_and_time.h>
#include <units/isq/si/speed.h> // IWYU pragma: keep #include <mp-units/systems/si/unit_symbols.h>
#include <units/isq/si/time.h>
#include <units/quantity_io.h>
#include <iostream> #include <iostream>
using namespace units::isq; using namespace mp_units;
constexpr Speed auto avg_speed(Length auto d, Time auto t) { return d / t; } constexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::distance> auto d, QuantityOf<isq::duration> auto t)
{
return d / t;
}
int main() int main()
{ {
using namespace units::isq::si::literals; using namespace mp_units::si::unit_symbols;
using namespace units::isq::si::references; using namespace mp_units::international::unit_symbols;
using namespace units::aliases::isq::si::international;
constexpr Speed auto v1 = 110 * (km / h); constexpr auto v1 = 110 * (km / h);
constexpr Speed auto v2 = mi_per_h<>(70.); constexpr auto v2 = 70 * mph;
constexpr Speed auto v3 = avg_speed(220_q_km, 2_q_h); constexpr auto v3 = avg_speed(220. * km, 2 * h);
constexpr Speed auto v4 = avg_speed(si::length<si::international::mile>(140), si::time<si::hour>(2)); constexpr auto v4 = avg_speed(isq::distance(140. * mi), 2 * isq::duration[h]);
#if UNITS_DOWNCAST_MODE == 0 constexpr auto v5 = v3[m / s];
constexpr Speed auto v5 = quantity_cast<si::speed<si::metre_per_second>>(v3); constexpr auto v6 = value_cast<m / s>(v4);
constexpr Speed auto v6 = quantity_cast<si::dim_speed, si::metre_per_second>(v4); constexpr auto v7 = value_cast<int>(v6);
#else
constexpr Speed auto v5 = quantity_cast<si::speed<si::metre_per_second>>(v3);
constexpr Speed auto v6 = quantity_cast<si::metre_per_second>(v4);
#endif
constexpr Speed auto v7 = quantity_cast<int>(v6);
std::cout << v1 << '\n'; // 110 km/h std::cout << v1 << '\n'; // 110 km/h
std::cout << v2 << '\n'; // 70 mi/h std::cout << v2 << '\n'; // 70 mi/h

View File

@@ -23,26 +23,54 @@
#pragma once #pragma once
#include "ranged_representation.h" #include "ranged_representation.h"
#include <units/bits/external/hacks.h> #include <mp-units/bits/fmt_hacks.h>
#include <units/bits/fmt_hacks.h> #include <mp-units/format.h>
#include <units/generic/angle.h> #include <mp-units/math.h>
#include <units/isq/si/length.h> #include <mp-units/quantity.h>
#include <units/quantity_kind.h> #include <mp-units/quantity_point.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si/units.h>
#include <compare>
#include <limits> #include <limits>
#include <numbers> #include <numbers>
#include <ostream> #include <ostream>
// IWYU pragma: begin_exports namespace geographic {
#include <compare>
// IWYU pragma: end_exports inline constexpr struct mean_sea_level : mp_units::absolute_point_origin<mp_units::isq::altitude> {
using mp_units::absolute_point_origin<mp_units::isq::altitude>::absolute_point_origin;
} mean_sea_level;
using msl_altitude = mp_units::quantity_point<mp_units::isq::altitude[mp_units::si::metre], mean_sea_level>;
// text output
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const msl_altitude& a)
{
return os << a.absolute() << " AMSL";
}
} // namespace geographic
template<>
struct UNITS_STD_FMT::formatter<geographic::msl_altitude> : formatter<geographic::msl_altitude::quantity_type> {
template<typename FormatContext>
auto format(const geographic::msl_altitude& a, FormatContext& ctx)
{
formatter<geographic::msl_altitude::quantity_type>::format(a.absolute(), ctx);
return UNITS_STD_FMT::format_to(ctx.out(), " AMSL");
}
};
namespace geographic { namespace geographic {
template<typename T = double> template<typename T = double>
using latitude = units::angle<units::degree, ranged_representation<T, -90, 90>>; using latitude =
mp_units::quantity<mp_units::isq::angular_measure[mp_units::si::degree], ranged_representation<T, -90, 90>>;
template<typename T = double> template<typename T = double>
using longitude = units::angle<units::degree, ranged_representation<T, -180, 180>>; using longitude =
mp_units::quantity<mp_units::isq::angular_measure[mp_units::si::degree], ranged_representation<T, -180, 180>>;
template<class CharT, class Traits, typename T> template<class CharT, class Traits, typename T>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const latitude<T>& lat) std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const latitude<T>& lat)
@@ -64,29 +92,18 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
inline namespace literals { inline namespace literals {
constexpr auto operator"" _N(long double v) { return latitude<long double>(latitude<long double>::rep(v)); } constexpr latitude<long double> operator"" _N(long double v) { return latitude<long double>{v * mp_units::si::degree}; }
constexpr auto operator"" _S(long double v) { return latitude<long double>(latitude<long double>::rep(v)); } constexpr latitude<long double> operator"" _S(long double v)
constexpr auto operator"" _E(long double v) { return longitude<long double>(longitude<long double>::rep(v)); }
constexpr auto operator"" _W(long double v) { return longitude<long double>(longitude<long double>::rep(v)); }
constexpr auto operator"" _N(unsigned long long v)
{ {
gsl_ExpectsAudit(std::in_range<std::int64_t>(v)); return latitude<long double>{-v * mp_units::si::degree};
return latitude<std::int64_t>(latitude<std::int64_t>::rep(static_cast<std::int64_t>(v)));
} }
constexpr auto operator"" _S(unsigned long long v) constexpr longitude<long double> operator"" _E(long double v)
{ {
gsl_ExpectsAudit(std::in_range<std::int64_t>(v)); return longitude<long double>{v * mp_units::si::degree};
return latitude<std::int64_t>(-latitude<std::int64_t>::rep(static_cast<std::int64_t>(v)));
} }
constexpr auto operator"" _E(unsigned long long v) constexpr longitude<long double> operator"" _W(long double v)
{ {
gsl_ExpectsAudit(std::in_range<std::int64_t>(v)); return longitude<long double>{-v * mp_units::si::degree};
return longitude<std::int64_t>(longitude<std::int64_t>::rep(static_cast<std::int64_t>(v)));
}
constexpr auto operator"" _W(unsigned long long v)
{
gsl_ExpectsAudit(std::in_range<std::int64_t>(v));
return longitude<std::int64_t>(-longitude<std::int64_t>::rep(static_cast<std::int64_t>(v)));
} }
} // namespace literals } // namespace literals
@@ -129,8 +146,7 @@ struct UNITS_STD_FMT::formatter<geographic::longitude<T>> : formatter<T> {
namespace geographic { namespace geographic {
struct horizontal_kind : units::kind<horizontal_kind, units::isq::si::dim_length> {}; using distance = mp_units::quantity<mp_units::isq::distance[mp_units::si::kilo<mp_units::si::metre>]>;
using distance = units::quantity_kind<horizontal_kind, units::isq::si::kilometre>;
template<typename T> template<typename T>
struct position { struct position {
@@ -141,31 +157,26 @@ struct position {
template<typename T> template<typename T>
distance spherical_distance(position<T> from, position<T> to) distance spherical_distance(position<T> from, position<T> to)
{ {
using namespace units::isq::si; using namespace mp_units;
constexpr length<kilometre> earth_radius(6371); constexpr auto earth_radius = 6'371 * isq::radius[si::kilo<si::metre>];
constexpr auto p = std::numbers::pi_v<T> / 180; using isq::sin, isq::cos, isq::asin, isq::acos;
const auto lat1_rad = from.lat.number() * p;
const auto lon1_rad = from.lon.number() * p;
const auto lat2_rad = to.lat.number() * p;
const auto lon2_rad = to.lon.number() * p;
using std::sin, std::cos, std::asin, std::acos, std::sqrt;
// https://en.wikipedia.org/wiki/Great-circle_distance#Formulae // https://en.wikipedia.org/wiki/Great-circle_distance#Formulae
if constexpr (sizeof(T) >= 8) { if constexpr (sizeof(T) >= 8) {
// spherical law of cosines // spherical law of cosines
const auto central_angle = const auto central_angle = acos(sin(from.lat) * sin(to.lat) + cos(from.lat) * cos(to.lat) * cos(to.lon - from.lon));
acos(sin(lat1_rad) * sin(lat2_rad) + cos(lat1_rad) * cos(lat2_rad) * cos(lon2_rad - lon1_rad)); // const auto central_angle = 2 * asin(sqrt(0.5 - cos(to.lat - from.lat) / 2 + cos(from.lat) * cos(to.lat) * (1
// const auto central_angle = 2 * asin(sqrt(0.5 - cos(lat2_rad - lat1_rad) / 2 + cos(lat1_rad) * cos(lat2_rad) * (1 // - cos(lon2_rad - from.lon)) / 2));
// - cos(lon2_rad - lon1_rad)) / 2));
return distance(earth_radius * central_angle); return quantity_cast<isq::distance>(earth_radius * central_angle);
} else { } else {
// the haversine formula // the haversine formula
const auto sin_lat = sin((lat2_rad - lat1_rad) / 2); const auto sin_lat = sin((to.lat - from.lat) / 2);
const auto sin_lon = sin((lon2_rad - lon1_rad) / 2); const auto sin_lon = sin((to.lon - from.lon) / 2);
const auto central_angle = 2 * asin(sqrt(sin_lat * sin_lat + cos(lat1_rad) * cos(lat2_rad) * sin_lon * sin_lon)); const auto central_angle = 2 * asin(sqrt(sin_lat * sin_lat + cos(from.lat) * cos(to.lat) * sin_lon * sin_lon));
return distance(earth_radius * central_angle);
return quantity_cast<isq::distance>(earth_radius * central_angle);
} }
} }

View File

@@ -23,7 +23,8 @@
#pragma once #pragma once
#include "validated_type.h" #include "validated_type.h"
#include <units/bits/external/hacks.h> #include <mp-units/bits/external/hacks.h>
#include <mp-units/customization_points.h>
#include <algorithm> #include <algorithm>
#include <concepts> #include <concepts>
#include <type_traits> #include <type_traits>
@@ -51,9 +52,4 @@ public:
}; };
template<typename T, auto Min, auto Max> template<typename T, auto Min, auto Max>
struct std::common_type<std::intmax_t, ranged_representation<T, Min, Max>> : inline constexpr bool mp_units::is_scalar<ranged_representation<T, Min, Max>> = mp_units::is_scalar<T>;
std::type_identity<ranged_representation<std::common_type_t<std::intmax_t, T>, Min, Max>> {};
template<typename T, auto Min, auto Max>
struct std::common_type<ranged_representation<T, Min, Max>, std::intmax_t> :
std::type_identity<ranged_representation<std::common_type_t<T, std::intmax_t>, Min, Max>> {};

View File

@@ -23,7 +23,7 @@
#pragma once #pragma once
#include <gsl/gsl-lite.hpp> #include <gsl/gsl-lite.hpp>
#include <units/bits/external/hacks.h> #include <mp-units/bits/external/hacks.h>
#include <utility> #include <utility>
inline constexpr struct validated_tag { inline constexpr struct validated_tag {

View File

@@ -28,17 +28,13 @@ cmake_minimum_required(VERSION 3.2)
function(add_example target) function(add_example target)
add_executable(${target} ${target}.cpp) add_executable(${target} ${target}.cpp)
target_link_libraries(${target} PRIVATE ${ARGN}) target_link_libraries(${target} PRIVATE ${ARGN})
target_compile_definitions(${target} PRIVATE ${projectPrefix}NO_LITERALS ${projectPrefix}NO_ALIASES)
endfunction() endfunction()
add_example(kalman_filter-example_1 mp-units::core-fmt mp-units::si) add_example(kalman_filter-example_1 mp-units::core-fmt mp-units::si mp-units::utility)
add_example(kalman_filter-example_2 mp-units::core-fmt mp-units::si) add_example(kalman_filter-example_2 mp-units::core-fmt mp-units::si mp-units::utility)
add_example(kalman_filter-example_3 mp-units::core-fmt mp-units::si) add_example(kalman_filter-example_3 mp-units::core-fmt mp-units::si mp-units::utility)
add_example(kalman_filter-example_4 mp-units::core-fmt mp-units::si) add_example(kalman_filter-example_4 mp-units::core-fmt mp-units::si mp-units::utility)
add_example(kalman_filter-example_5 mp-units::core-fmt mp-units::si) add_example(kalman_filter-example_5 mp-units::core-fmt mp-units::si mp-units::utility)
add_example(kalman_filter-example_6 mp-units::core-fmt mp-units::si mp-units::utility)
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") add_example(kalman_filter-example_7 mp-units::core-fmt mp-units::si mp-units::utility)
add_example(kalman_filter-example_6 mp-units::core-fmt mp-units::si) add_example(kalman_filter-example_8 mp-units::core-fmt mp-units::si mp-units::utility)
add_example(kalman_filter-example_7 mp-units::core-fmt mp-units::si)
add_example(kalman_filter-example_8 mp-units::core-fmt mp-units::si)
endif()

View File

@@ -22,43 +22,39 @@
#pragma once #pragma once
#include <units/bits/fmt_hacks.h> #include <mp-units/bits/fmt_hacks.h>
#include <units/format.h> #include <mp-units/format.h>
#include <units/generic/dimensionless.h> #include <mp-units/math.h>
#include <units/isq/dimensions/time.h> #include <mp-units/quantity.h>
#include <units/math.h> #include <mp-units/quantity_point.h>
#include <units/quantity.h> #include <mp-units/systems/isq/space_and_time.h>
#include <units/quantity_point.h>
#include <tuple> #include <tuple>
namespace kalman { namespace kalman {
template<typename T> template<typename T>
concept QuantityOrQuantityPoint = concept QuantityOrQuantityPoint = mp_units::Quantity<T> || mp_units::QuantityPoint<T>;
units::Quantity<T> || units::QuantityPoint<T>; // TODO Should it also account for `kinds`?
template<typename... Qs> template<mp_units::Dimension auto... Ds>
inline constexpr bool are_time_derivatives = false; inline constexpr bool are_time_derivatives = false;
template<typename Q> template<mp_units::Dimension auto D>
inline constexpr bool are_time_derivatives<Q> = true; inline constexpr bool are_time_derivatives<D> = true;
template<typename Q1, typename Q2, typename... Qs> template<mp_units::Dimension auto D1, mp_units::Dimension auto D2, mp_units::Dimension auto... Ds>
inline constexpr bool are_time_derivatives<Q1, Q2, Qs...> = inline constexpr bool are_time_derivatives<D1, D2, Ds...> =
units::DimensionOfT<typename decltype(Q1::reference / Q2::reference)::dimension, (D1 / D2 == mp_units::isq::dim_time) && are_time_derivatives<D2, Ds...>;
units::isq::dim_time> && // TODO Think on how to simplify this
are_time_derivatives<Q2, Qs...>;
// state // state
template<QuantityOrQuantityPoint... QQPs> template<QuantityOrQuantityPoint... QQPs>
requires(sizeof...(QQPs) > 0) && (sizeof...(QQPs) <= 3) && are_time_derivatives<QQPs...> requires(sizeof...(QQPs) > 0) && (sizeof...(QQPs) <= 3) && are_time_derivatives<QQPs::dimension...>
struct state { struct state {
std::tuple<QQPs...> variables_; std::tuple<QQPs...> variables_;
constexpr state(QQPs... qqps) : variables_(std::move(qqps)...) {} constexpr state(QQPs... qqps) : variables_(std::move(qqps)...) {}
}; };
template<typename T> template<typename T>
concept State = units::is_specialization_of<T, state>; concept State = mp_units::is_specialization_of<T, state>;
template<std::size_t Idx, typename... Qs> template<std::size_t Idx, typename... Qs>
constexpr auto& get(state<Qs...>& s) constexpr auto& get(state<Qs...>& s)
@@ -76,38 +72,35 @@ constexpr const auto& get(const state<Qs...>& s)
template<QuantityOrQuantityPoint QQP, QuantityOrQuantityPoint... QQPs> template<QuantityOrQuantityPoint QQP, QuantityOrQuantityPoint... QQPs>
struct estimation { struct estimation {
private: private:
using uncertainty_ref = decltype(QQP::reference * QQP::reference); static constexpr auto uncertainty_ref = QQP::reference * QQP::reference;
using uncertainty_type = using uncertainty_type = mp_units::quantity<uncertainty_ref, typename QQP::rep>;
units::quantity<typename uncertainty_ref::dimension, typename uncertainty_ref::unit, typename QQP::rep>;
public: public:
kalman::state<QQP, QQPs...> state; // TODO extend kalman functions to work with this variadic patermater list kalman::state<QQP, QQPs...> state; // TODO extend kalman functions to work with this variadic parameter list
uncertainty_type uncertainty; uncertainty_type uncertainty;
}; };
#if UNITS_COMP_MSVC || (UNITS_COMP_CLANG && UNITS_COMP_CLANG <= 16) template<QuantityOrQuantityPoint QQP, mp_units::Quantity U>
template<QuantityOrQuantityPoint QQP, units::Quantity U>
estimation(state<QQP>, U) -> estimation<QQP>; estimation(state<QQP>, U) -> estimation<QQP>;
#endif
// kalman gain // kalman gain
template<units::Quantity Q> template<mp_units::Quantity Q>
constexpr units::dimensionless<units::one> kalman_gain(Q estimate_uncertainty, Q measurement_uncertainty) constexpr mp_units::quantity<mp_units::dimensionless[mp_units::one]> kalman_gain(Q estimate_uncertainty,
Q measurement_uncertainty)
{ {
return estimate_uncertainty / (estimate_uncertainty + measurement_uncertainty); return estimate_uncertainty / (estimate_uncertainty + measurement_uncertainty);
} }
// state update // state update
template<typename Q, QuantityOrQuantityPoint QM, units::Dimensionless K> template<typename Q, QuantityOrQuantityPoint QM, mp_units::QuantityOf<mp_units::dimensionless> K>
requires units::equivalent<typename Q::dimension, typename QM::dimension> requires(Q::quantity_spec == QM::quantity_spec)
constexpr state<Q> state_update(const state<Q>& predicted, QM measured, K gain) constexpr state<Q> state_update(const state<Q>& predicted, QM measured, K gain)
{ {
return {get<0>(predicted) + gain * (measured - get<0>(predicted))}; return {get<0>(predicted) + gain * (measured - get<0>(predicted))};
} }
template<typename Q1, typename Q2, QuantityOrQuantityPoint QM, units::Dimensionless K, units::isq::Time T> template<typename Q1, typename Q2, QuantityOrQuantityPoint QM, mp_units::QuantityOf<mp_units::dimensionless> K,
requires units::equivalent<typename Q1::dimension, typename QM::dimension> mp_units::QuantityOf<mp_units::isq::time> T>
requires(Q1::quantity_spec == QM::quantity_spec)
constexpr state<Q1, Q2> state_update(const state<Q1, Q2>& predicted, QM measured, std::array<K, 2> gain, T interval) constexpr state<Q1, Q2> state_update(const state<Q1, Q2>& predicted, QM measured, std::array<K, 2> gain, T interval)
{ {
const auto q1 = get<0>(predicted) + get<0>(gain) * (measured - get<0>(predicted)); const auto q1 = get<0>(predicted) + get<0>(gain) * (measured - get<0>(predicted));
@@ -115,8 +108,9 @@ constexpr state<Q1, Q2> state_update(const state<Q1, Q2>& predicted, QM measured
return {q1, q2}; return {q1, q2};
} }
template<typename Q1, typename Q2, typename Q3, QuantityOrQuantityPoint QM, units::Dimensionless K, units::isq::Time T> template<typename Q1, typename Q2, typename Q3, QuantityOrQuantityPoint QM,
requires units::equivalent<typename Q1::dimension, typename QM::dimension> mp_units::QuantityOf<mp_units::dimensionless> K, mp_units::QuantityOf<mp_units::isq::time> T>
requires(Q1::quantity_spec == QM::quantity_spec)
constexpr state<Q1, Q2, Q3> state_update(const state<Q1, Q2, Q3>& predicted, QM measured, std::array<K, 3> gain, constexpr state<Q1, Q2, Q3> state_update(const state<Q1, Q2, Q3>& predicted, QM measured, std::array<K, 3> gain,
T interval) T interval)
{ {
@@ -127,14 +121,14 @@ constexpr state<Q1, Q2, Q3> state_update(const state<Q1, Q2, Q3>& predicted, QM
} }
// covariance update // covariance update
template<units::Quantity Q, units::Dimensionless K> template<mp_units::Quantity Q, mp_units::QuantityOf<mp_units::dimensionless> K>
constexpr Q covariance_update(Q uncertainty, K gain) constexpr Q covariance_update(Q uncertainty, K gain)
{ {
return (1 - gain) * uncertainty; return (1 - gain) * uncertainty;
} }
// state extrapolation // state extrapolation
template<typename Q1, typename Q2, units::isq::Time T> template<typename Q1, typename Q2, mp_units::QuantityOf<mp_units::isq::time> T>
constexpr state<Q1, Q2> state_extrapolation(const state<Q1, Q2>& estimated, T interval) constexpr state<Q1, Q2> state_extrapolation(const state<Q1, Q2>& estimated, T interval)
{ {
const auto q1 = get<0>(estimated) + get<1>(estimated) * interval; const auto q1 = get<0>(estimated) + get<1>(estimated) * interval;
@@ -142,7 +136,7 @@ constexpr state<Q1, Q2> state_extrapolation(const state<Q1, Q2>& estimated, T in
return {q1, q2}; return {q1, q2};
} }
template<typename Q1, typename Q2, typename Q3, units::isq::Time T> template<typename Q1, typename Q2, typename Q3, mp_units::QuantityOf<mp_units::isq::time> T>
constexpr state<Q1, Q2, Q3> state_extrapolation(const state<Q1, Q2, Q3>& estimated, T interval) constexpr state<Q1, Q2, Q3> state_extrapolation(const state<Q1, Q2, Q3>& estimated, T interval)
{ {
const auto q1 = get<0>(estimated) + get<1>(estimated) * interval + get<2>(estimated) * pow<2>(interval) / 2; const auto q1 = get<0>(estimated) + get<1>(estimated) * interval + get<2>(estimated) * pow<2>(interval) / 2;
@@ -152,7 +146,7 @@ constexpr state<Q1, Q2, Q3> state_extrapolation(const state<Q1, Q2, Q3>& estimat
} }
// covariance extrapolation // covariance extrapolation
template<units::Quantity Q> template<mp_units::Quantity Q>
constexpr Q covariance_extrapolation(Q uncertainty, Q process_noise_variance) constexpr Q covariance_extrapolation(Q uncertainty, Q process_noise_variance)
{ {
return uncertainty + process_noise_variance; return uncertainty + process_noise_variance;
@@ -164,8 +158,8 @@ template<typename... Qs>
struct UNITS_STD_FMT::formatter<kalman::state<Qs...>> { struct UNITS_STD_FMT::formatter<kalman::state<Qs...>> {
constexpr auto parse(format_parse_context& ctx) constexpr auto parse(format_parse_context& ctx)
{ {
units::detail::dynamic_specs_handler handler(specs, ctx); mp_units::detail::dynamic_specs_handler handler(specs, ctx);
return units::detail::parse_format_specs(ctx.begin(), ctx.end(), handler); return mp_units::detail::parse_format_specs(ctx.begin(), ctx.end(), handler);
} }
template<typename FormatContext> template<typename FormatContext>
@@ -193,28 +187,28 @@ struct UNITS_STD_FMT::formatter<kalman::state<Qs...>> {
} }
std::string global_format_buffer; std::string global_format_buffer;
units::detail::quantity_global_format_specs<char> global_specs = {specs.fill, specs.align, specs.width}; mp_units::detail::quantity_global_format_specs<char> global_specs = {specs.fill, specs.align, specs.width};
units::detail::format_global_buffer(std::back_inserter(global_format_buffer), global_specs); mp_units::detail::format_global_buffer(std::back_inserter(global_format_buffer), global_specs);
return UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer, UNITS_STD_FMT::make_format_args(value_buffer)); return UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer, UNITS_STD_FMT::make_format_args(value_buffer));
} }
private: private:
units::detail::dynamic_format_specs<char> specs; mp_units::detail::dynamic_format_specs<char> specs;
}; };
template<typename Q> template<typename Q>
struct UNITS_STD_FMT::formatter<kalman::estimation<Q>> { struct UNITS_STD_FMT::formatter<kalman::estimation<Q>> {
constexpr auto parse(format_parse_context& ctx) constexpr auto parse(format_parse_context& ctx)
{ {
units::detail::dynamic_specs_handler handler(specs, ctx); mp_units::detail::dynamic_specs_handler handler(specs, ctx);
return units::detail::parse_format_specs(ctx.begin(), ctx.end(), handler); return mp_units::detail::parse_format_specs(ctx.begin(), ctx.end(), handler);
} }
template<typename FormatContext> template<typename FormatContext>
auto format(kalman::estimation<Q> e, FormatContext& ctx) auto format(kalman::estimation<Q> e, FormatContext& ctx)
{ {
units::Quantity auto q = [](const Q& t) { mp_units::Quantity auto q = [](const Q& t) {
if constexpr (units::Quantity<Q>) if constexpr (mp_units::Quantity<Q>)
return t; return t;
else else
return t.relative(); return t.relative();
@@ -230,11 +224,11 @@ struct UNITS_STD_FMT::formatter<kalman::estimation<Q>> {
} }
std::string global_format_buffer; std::string global_format_buffer;
units::detail::quantity_global_format_specs<char> global_specs = {specs.fill, specs.align, specs.width}; mp_units::detail::quantity_global_format_specs<char> global_specs = {specs.fill, specs.align, specs.width};
units::detail::format_global_buffer(std::back_inserter(global_format_buffer), global_specs); mp_units::detail::format_global_buffer(std::back_inserter(global_format_buffer), global_specs);
return UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer, UNITS_STD_FMT::make_format_args(value_buffer)); return UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer, UNITS_STD_FMT::make_format_args(value_buffer));
} }
private: private:
units::detail::dynamic_format_specs<char> specs; mp_units::detail::dynamic_format_specs<char> specs;
}; };

View File

@@ -21,15 +21,15 @@
// SOFTWARE. // SOFTWARE.
#include "kalman.h" #include "kalman.h"
#include <units/format.h> #include <mp-units/format.h>
#include <units/generic/dimensionless.h> #include <mp-units/systems/isq/space_and_time.h>
#include <units/isq/si/mass.h> #include <mp-units/systems/si/unit_symbols.h>
#include <array> #include <array>
#include <iostream> #include <iostream>
// Based on: https://www.kalmanfilter.net/alphabeta.html#ex1 // Based on: https://www.kalmanfilter.net/alphabeta.html#ex1
using namespace units; using namespace mp_units;
void print_header(const kalman::State auto& initial) void print_header(const kalman::State auto& initial)
{ {
@@ -38,27 +38,26 @@ void print_header(const kalman::State auto& initial)
"Curr. Estimate", "Next Estimate"); "Curr. Estimate", "Next Estimate");
} }
void print(auto iteration, Dimensionless auto gain, Quantity auto measured, const kalman::State auto& current, void print(auto iteration, QuantityOf<dimensionless> auto gain, Quantity auto measured,
const kalman::State auto& next) const kalman::State auto& current, const kalman::State auto& next)
{ {
std::cout << UNITS_STD_FMT::format("{:2} | {:9} | {:8} | {:14} | {:14}\n", iteration, gain, measured, current, next); std::cout << UNITS_STD_FMT::format("{:2} | {:9} | {:8} | {:14} | {:14}\n", iteration, gain, measured, current, next);
} }
int main() int main()
{ {
using namespace units::isq; using namespace mp_units::si::unit_symbols;
using namespace units::isq::si::references; using state = kalman::state<quantity<isq::mass[g]>>;
using state = kalman::state<si::mass<si::gram>>;
const state initial = {1 * kg}; const state initial = {1 * kg};
const std::array measurements = {1030 * g, 989 * g, 1017 * g, 1009 * g, 1013 * g, const std::array measurements = {1'030 * g, 989 * g, 1'017 * g, 1'009 * g, 1'013 * g,
979 * g, 1008 * g, 1042 * g, 1012 * g, 1011 * g}; 979 * g, 1'008 * g, 1'042 * g, 1'012 * g, 1'011 * g};
print_header(initial); print_header(initial);
state next = initial; state next = initial;
for (int index = 1; const auto& m : measurements) { for (int index = 1; const auto& m : measurements) {
const auto& previous = next; const auto& previous = next;
const dimensionless<one> gain = 1. / index; const auto gain = 1. / index * one;
const auto current = state_update(previous, m, gain); const auto current = state_update(previous, m, gain);
next = current; next = current;
print(index++, gain, m, current, next); print(index++, gain, m, current, next);

View File

@@ -21,17 +21,19 @@
// SOFTWARE. // SOFTWARE.
#include "kalman.h" #include "kalman.h"
#include <units/format.h> #include <mp-units/format.h>
#include <units/generic/dimensionless.h> #include <mp-units/systems/isq/space_and_time.h>
#include <units/isq/si/length.h> #include <mp-units/systems/si/unit_symbols.h>
#include <units/isq/si/speed.h>
#include <units/isq/si/time.h>
#include <array> #include <array>
#include <iostream> #include <iostream>
// Based on: https://www.kalmanfilter.net/alphabeta.html#ex2 // Based on: https://www.kalmanfilter.net/alphabeta.html#ex2
using namespace units; template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;
void print_header(const kalman::State auto& initial) void print_header(const kalman::State auto& initial)
{ {
@@ -47,15 +49,15 @@ void print(auto iteration, Quantity auto measured, const kalman::State auto& cur
int main() int main()
{ {
using namespace units::isq; using namespace mp_units::si::unit_symbols;
using namespace units::isq::si::references; using state = kalman::state<quantity<isq::position_vector[m]>, quantity<isq::velocity[m / s]>>;
using state = kalman::state<si::length<si::metre>, si::speed<si::metre_per_second>>;
const auto interval = 5 * s; const auto interval = isq::duration(5 * s);
const state initial = {30 * km, 40 * (m / s)}; const state initial = {30 * km, 40 * (m / s)};
const std::array measurements = {30110 * m, 30265 * m, 30740 * m, 30750 * m, 31135 * m, const quantity<isq::position_vector[m], int> measurements[] = {30'110 * m, 30'265 * m, 30'740 * m, 30'750 * m,
31015 * m, 31180 * m, 31610 * m, 31960 * m, 31865 * m}; 31'135 * m, 31'015 * m, 31'180 * m, 31'610 * m,
std::array gain = {dimensionless<one>(0.2), dimensionless<one>(0.1)}; 31'960 * m, 31'865 * m};
std::array gain = {0.2 * one, 0.1 * one};
print_header(initial); print_header(initial);
state next = state_extrapolation(initial, interval); state next = state_extrapolation(initial, interval);

View File

@@ -21,17 +21,19 @@
// SOFTWARE. // SOFTWARE.
#include "kalman.h" #include "kalman.h"
#include <units/format.h> #include <mp-units/format.h>
#include <units/generic/dimensionless.h> #include <mp-units/systems/isq/space_and_time.h>
#include <units/isq/si/length.h> #include <mp-units/systems/si/unit_symbols.h>
#include <units/isq/si/speed.h>
#include <units/isq/si/time.h>
#include <array> #include <array>
#include <iostream> #include <iostream>
// Based on: https://www.kalmanfilter.net/alphabeta.html#ex3 // Based on: https://www.kalmanfilter.net/alphabeta.html#ex3
using namespace units; template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;
void print_header(const kalman::State auto& initial) void print_header(const kalman::State auto& initial)
{ {
@@ -47,15 +49,15 @@ void print(auto iteration, Quantity auto measured, const kalman::State auto& cur
int main() int main()
{ {
using namespace units::isq; using namespace mp_units::si::unit_symbols;
using namespace units::isq::si::references; using state = kalman::state<quantity<isq::position_vector[m]>, quantity<isq::velocity[m / s]>>;
using state = kalman::state<si::length<si::metre>, si::speed<si::metre_per_second>>;
const auto interval = 5 * s; const auto interval = isq::duration(5 * s);
const state initial = {30 * km, 50 * (m / s)}; const state initial = {30 * km, 50 * (m / s)};
const std::array measurements = {30160 * m, 30365 * m, 30890 * m, 31050 * m, 31785 * m, const quantity<isq::position_vector[m], int> measurements[] = {30'160 * m, 30'365 * m, 30'890 * m, 31'050 * m,
32215 * m, 33130 * m, 34510 * m, 36010 * m, 37265 * m}; 31'785 * m, 32'215 * m, 33'130 * m, 34'510 * m,
std::array gain = {dimensionless<one>(0.2), dimensionless<one>(0.1)}; 36'010 * m, 37'265 * m};
std::array gain = {0.2 * one, 0.1 * one};
print_header(initial); print_header(initial);
state next = state_extrapolation(initial, interval); state next = state_extrapolation(initial, interval);

View File

@@ -21,18 +21,19 @@
// SOFTWARE. // SOFTWARE.
#include "kalman.h" #include "kalman.h"
#include <units/format.h> #include <mp-units/format.h>
#include <units/generic/dimensionless.h> #include <mp-units/systems/isq/space_and_time.h>
#include <units/isq/si/acceleration.h> #include <mp-units/systems/si/unit_symbols.h>
#include <units/isq/si/length.h>
#include <units/isq/si/speed.h>
#include <units/isq/si/time.h>
#include <array> #include <array>
#include <iostream> #include <iostream>
// Based on: https://www.kalmanfilter.net/alphabeta.html#ex4 // Based on: https://www.kalmanfilter.net/alphabeta.html#ex4
using namespace units; template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;
void print_header(const kalman::State auto& initial) void print_header(const kalman::State auto& initial)
{ {
@@ -48,18 +49,16 @@ void print(auto iteration, Quantity auto measured, const kalman::State auto& cur
int main() int main()
{ {
using namespace units::isq; using namespace mp_units::si::unit_symbols;
using namespace units::isq::si::references; using state = kalman::state<quantity<isq::position_vector[m]>, quantity<isq::velocity[m / s]>,
using state = quantity<isq::acceleration[m / s2]>>;
kalman::state<si::length<si::metre>, si::speed<si::metre_per_second>, si::acceleration<si::metre_per_second_sq>>; const auto interval = isq::duration(5. * s);
constexpr auto mps = m / s; const state initial = {30 * km, 50 * (m / s), 0 * (m / s2)};
constexpr auto mps2 = mps / s;
const auto interval = 5. * s; const quantity<isq::position_vector[m], int> measurements[] = {30'160 * m, 30'365 * m, 30'890 * m, 31'050 * m,
const state initial = {30 * km, 50 * mps, 0 * mps2}; 31'785 * m, 32'215 * m, 33'130 * m, 34'510 * m,
const std::array measurements = {30160 * m, 30365 * m, 30890 * m, 31050 * m, 31785 * m, 36'010 * m, 37'265 * m};
32215 * m, 33130 * m, 34510 * m, 36010 * m, 37265 * m}; std::array gain = {0.5 * one, 0.4 * one, 0.1 * one};
std::array gain = {dimensionless<one>(0.5), dimensionless<one>(0.4), dimensionless<one>(0.1)};
print_header(initial); print_header(initial);
state next = state_extrapolation(initial, interval); state next = state_extrapolation(initial, interval);

View File

@@ -21,15 +21,16 @@
// SOFTWARE. // SOFTWARE.
#include "kalman.h" #include "kalman.h"
#include <units/format.h> #include <mp-units/format.h>
#include <units/isq/si/length.h> #include <mp-units/math.h>
#include <units/math.h> #include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si/unit_symbols.h>
#include <array> #include <array>
#include <iostream> #include <iostream>
// Based on: https://www.kalmanfilter.net/kalman1d.html#ex5 // Based on: https://www.kalmanfilter.net/kalman1d.html#ex5
using namespace units; using namespace mp_units;
template<Quantity Q> template<Quantity Q>
void print_header(kalman::estimation<Q> initial) void print_header(kalman::estimation<Q> initial)
@@ -39,7 +40,7 @@ void print_header(kalman::estimation<Q> initial)
"Curr. Estimate", "Next Estimate"); "Curr. Estimate", "Next Estimate");
} }
template<Quantity Q, Dimensionless K> template<Quantity Q, QuantityOf<dimensionless> K>
void print(auto iteration, K gain, Q measured, kalman::estimation<Q> current, kalman::estimation<Q> next) void print(auto iteration, K gain, Q measured, kalman::estimation<Q> current, kalman::estimation<Q> next)
{ {
std::cout << UNITS_STD_FMT::format("{:2} | {:5%.2Q} | {:8} | {:>16.2} | {:>16.2}\n", iteration, gain, measured, std::cout << UNITS_STD_FMT::format("{:2} | {:5%.2Q} | {:8} | {:>16.2} | {:>16.2}\n", iteration, gain, measured,
@@ -49,16 +50,16 @@ void print(auto iteration, K gain, Q measured, kalman::estimation<Q> current, ka
int main() int main()
{ {
using namespace kalman; using namespace kalman;
using namespace units::isq; using namespace mp_units::si::unit_symbols;
using namespace units::isq::si::references;
const estimation initial = {state{60. * m}, pow<2>(15. * m)}; const estimation initial = {state{isq::height(60. * m)}, pow<2>(isq::height(15. * m))};
const std::array measurements = {48.54 * m, 47.11 * m, 55.01 * m, 55.15 * m, 49.89 * m, const quantity<isq::height[m]> measurements[] = {48.54 * m, 47.11 * m, 55.01 * m, 55.15 * m, 49.89 * m,
40.85 * m, 46.72 * m, 50.05 * m, 51.27 * m, 49.95 * m}; 40.85 * m, 46.72 * m, 50.05 * m, 51.27 * m, 49.95 * m};
const auto measurement_uncertainty = pow<2>(5. * m); const auto measurement_uncertainty = pow<2>(isq::height(5. * m));
auto update = [=]<Quantity Q>(const estimation<Q>& previous, const Q& meassurement, Dimensionless auto gain) { auto update = [=]<Quantity Q>(const estimation<Q>& previous, const Q& measurement,
return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)}; QuantityOf<dimensionless> auto gain) {
return estimation{state_update(previous.state, measurement, gain), covariance_update(previous.uncertainty, gain)};
}; };
auto predict = []<Quantity Q>(const estimation<Q>& current) { return current; }; auto predict = []<Quantity Q>(const estimation<Q>& current) { return current; };

View File

@@ -21,36 +21,17 @@
// SOFTWARE. // SOFTWARE.
#include "kalman.h" #include "kalman.h"
#include <units/format.h> #include <mp-units/format.h>
#include <units/isq/si/thermodynamic_temperature.h> #include <mp-units/math.h>
#include <units/math.h> #include <mp-units/quantity_point.h>
#include <units/quantity_point.h> #include <mp-units/systems/isq/thermodynamics.h>
#include <units/unit.h> #include <mp-units/systems/si/unit_symbols.h>
#include <array> #include <array>
#include <iostream> #include <iostream>
// TODO Fix when Celsius is properly supported (#232)
namespace units::isq::si {
struct degree_celsius : alias_unit<kelvin, basic_symbol_text{"°C", "deg_C"}> {};
namespace thermodynamic_temperature_references {
inline constexpr auto deg_C = reference<dim_thermodynamic_temperature, degree_celsius>{};
} // namespace thermodynamic_temperature_references
namespace references {
using namespace thermodynamic_temperature_references;
} // namespace references
} // namespace units::isq::si
// Based on: https://www.kalmanfilter.net/kalman1d.html#ex6 // Based on: https://www.kalmanfilter.net/kalman1d.html#ex6
using namespace units; using namespace mp_units;
template<QuantityPoint QP> template<QuantityPoint QP>
void print_header(kalman::estimation<QP> initial) void print_header(kalman::estimation<QP> initial)
@@ -60,7 +41,7 @@ void print_header(kalman::estimation<QP> initial)
"Curr. Estimate", "Next Estimate"); "Curr. Estimate", "Next Estimate");
} }
template<QuantityPoint QP, Dimensionless K> template<QuantityPoint QP, QuantityOf<dimensionless> K>
void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next) void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next)
{ {
std::cout << UNITS_STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain, std::cout << UNITS_STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain,
@@ -69,20 +50,21 @@ void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current,
int main() int main()
{ {
constexpr auto deg_C = isq::Celsius_temperature[si::degree_Celsius];
using namespace kalman; using namespace kalman;
using namespace units::isq;
using namespace units::isq::si::references;
const auto process_noise_variance = 0.0001 * (deg_C * deg_C); const auto process_noise_variance = 0.0001 * (deg_C * deg_C);
const estimation initial = {state{quantity_point(10. * deg_C)}, pow<2>(100. * deg_C)}; const estimation initial = {state{quantity_point{10. * deg_C}}, pow<2>(100. * deg_C)};
const std::array measurements = {quantity_point(49.95 * deg_C), quantity_point(49.967 * deg_C), const std::array measurements = {quantity_point{49.95 * deg_C}, quantity_point{49.967 * deg_C},
quantity_point(50.1 * deg_C), quantity_point(50.106 * deg_C), quantity_point{50.1 * deg_C}, quantity_point{50.106 * deg_C},
quantity_point(49.992 * deg_C), quantity_point(49.819 * deg_C), quantity_point{49.992 * deg_C}, quantity_point{49.819 * deg_C},
quantity_point(49.933 * deg_C), quantity_point(50.007 * deg_C), quantity_point{49.933 * deg_C}, quantity_point{50.007 * deg_C},
quantity_point(50.023 * deg_C), quantity_point(49.99 * deg_C)}; quantity_point{50.023 * deg_C}, quantity_point{49.99 * deg_C}};
const auto measurement_uncertainty = pow<2>(0.1 * deg_C); const auto measurement_uncertainty = pow<2>(0.1 * deg_C);
auto update = [=]<QuantityPoint QP>(const estimation<QP>& previous, const QP& meassurement, Dimensionless auto gain) { auto update = [=]<QuantityPoint QP>(const estimation<QP>& previous, const QP& meassurement,
QuantityOf<dimensionless> auto gain) {
return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)}; return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)};
}; };

View File

@@ -21,36 +21,17 @@
// SOFTWARE. // SOFTWARE.
#include "kalman.h" #include "kalman.h"
#include <units/format.h> #include <mp-units/format.h>
#include <units/isq/si/thermodynamic_temperature.h> #include <mp-units/math.h>
#include <units/math.h> #include <mp-units/quantity_point.h>
#include <units/quantity_point.h> #include <mp-units/systems/isq/thermodynamics.h>
#include <units/unit.h> #include <mp-units/systems/si/unit_symbols.h>
#include <array> #include <array>
#include <iostream> #include <iostream>
// TODO Fix when Celsius is properly supported (#232)
namespace units::isq::si {
struct degree_celsius : alias_unit<kelvin, basic_symbol_text{"°C", "deg_C"}> {};
namespace thermodynamic_temperature_references {
inline constexpr auto deg_C = reference<dim_thermodynamic_temperature, degree_celsius>{};
} // namespace thermodynamic_temperature_references
namespace references {
using namespace thermodynamic_temperature_references;
} // namespace references
} // namespace units::isq::si
// Based on: https://www.kalmanfilter.net/kalman1d.html#ex7 // Based on: https://www.kalmanfilter.net/kalman1d.html#ex7
using namespace units; using namespace mp_units;
template<QuantityPoint QP> template<QuantityPoint QP>
void print_header(kalman::estimation<QP> initial) void print_header(kalman::estimation<QP> initial)
@@ -60,7 +41,7 @@ void print_header(kalman::estimation<QP> initial)
"Curr. Estimate", "Next Estimate"); "Curr. Estimate", "Next Estimate");
} }
template<QuantityPoint QP, Dimensionless K> template<QuantityPoint QP, QuantityOf<dimensionless> K>
void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next) void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next)
{ {
std::cout << UNITS_STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain, std::cout << UNITS_STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain,
@@ -69,20 +50,21 @@ void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current,
int main() int main()
{ {
constexpr auto deg_C = isq::Celsius_temperature[si::degree_Celsius];
using namespace kalman; using namespace kalman;
using namespace units::isq;
using namespace units::isq::si::references;
const auto process_noise_variance = 0.0001 * (deg_C * deg_C); const auto process_noise_variance = 0.0001 * (deg_C * deg_C);
const estimation initial = {state{quantity_point(10. * deg_C)}, pow<2>(100. * deg_C)}; const estimation initial = {state{quantity_point{10. * deg_C}}, pow<2>(100. * deg_C)};
const std::array measurements = {quantity_point(50.45 * deg_C), quantity_point(50.967 * deg_C), const std::array measurements = {quantity_point{50.45 * deg_C}, quantity_point{50.967 * deg_C},
quantity_point(51.6 * deg_C), quantity_point(52.106 * deg_C), quantity_point{51.6 * deg_C}, quantity_point{52.106 * deg_C},
quantity_point(52.492 * deg_C), quantity_point(52.819 * deg_C), quantity_point{52.492 * deg_C}, quantity_point{52.819 * deg_C},
quantity_point(53.433 * deg_C), quantity_point(54.007 * deg_C), quantity_point{53.433 * deg_C}, quantity_point{54.007 * deg_C},
quantity_point(54.523 * deg_C), quantity_point(54.99 * deg_C)}; quantity_point{54.523 * deg_C}, quantity_point{54.99 * deg_C}};
const auto measurement_uncertainty = pow<2>(0.1 * deg_C); const auto measurement_uncertainty = pow<2>(0.1 * deg_C);
auto update = [=]<QuantityPoint QP>(const estimation<QP>& previous, const QP& meassurement, Dimensionless auto gain) { auto update = [=]<QuantityPoint QP>(const estimation<QP>& previous, const QP& meassurement,
QuantityOf<dimensionless> auto gain) {
return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)}; return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)};
}; };

View File

@@ -21,36 +21,17 @@
// SOFTWARE. // SOFTWARE.
#include "kalman.h" #include "kalman.h"
#include <units/format.h> #include <mp-units/format.h>
#include <units/isq/si/thermodynamic_temperature.h> #include <mp-units/math.h>
#include <units/math.h> #include <mp-units/quantity_point.h>
#include <units/quantity_point.h> #include <mp-units/systems/isq/thermodynamics.h>
#include <units/unit.h> #include <mp-units/systems/si/unit_symbols.h>
#include <array> #include <array>
#include <iostream> #include <iostream>
// TODO Fix when Celsius is properly supported (#232)
namespace units::isq::si {
struct degree_celsius : alias_unit<kelvin, basic_symbol_text{"°C", "deg_C"}> {};
namespace thermodynamic_temperature_references {
inline constexpr auto deg_C = reference<dim_thermodynamic_temperature, degree_celsius>{};
} // namespace thermodynamic_temperature_references
namespace references {
using namespace thermodynamic_temperature_references;
} // namespace references
} // namespace units::isq::si
// Based on: https://www.kalmanfilter.net/kalman1d.html#ex8 // Based on: https://www.kalmanfilter.net/kalman1d.html#ex8
using namespace units; using namespace mp_units;
template<QuantityPoint QP> template<QuantityPoint QP>
void print_header(kalman::estimation<QP> initial) void print_header(kalman::estimation<QP> initial)
@@ -60,7 +41,7 @@ void print_header(kalman::estimation<QP> initial)
"Curr. Estimate", "Next Estimate"); "Curr. Estimate", "Next Estimate");
} }
template<QuantityPoint QP, Dimensionless K> template<QuantityPoint QP, QuantityOf<dimensionless> K>
void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next) void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next)
{ {
std::cout << UNITS_STD_FMT::format("{:2} | {:7%.3Q} | {:10%.3Q %q} | {:>16.2} | {:>16.2}\n", iteration, gain, std::cout << UNITS_STD_FMT::format("{:2} | {:7%.3Q} | {:10%.3Q %q} | {:>16.2} | {:>16.2}\n", iteration, gain,
@@ -69,20 +50,21 @@ void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current,
int main() int main()
{ {
constexpr auto deg_C = isq::Celsius_temperature[si::degree_Celsius];
using namespace kalman; using namespace kalman;
using namespace units::isq;
using namespace units::isq::si::references;
const auto process_noise_variance = 0.15 * (deg_C * deg_C); const auto process_noise_variance = 0.15 * (deg_C * deg_C);
const estimation initial = {state{quantity_point(10. * deg_C)}, pow<2>(100. * deg_C)}; const estimation initial = {state{quantity_point{10. * deg_C}}, pow<2>(100. * deg_C)};
const std::array measurements = {quantity_point(50.45 * deg_C), quantity_point(50.967 * deg_C), const std::array measurements = {quantity_point{50.45 * deg_C}, quantity_point{50.967 * deg_C},
quantity_point(51.6 * deg_C), quantity_point(52.106 * deg_C), quantity_point{51.6 * deg_C}, quantity_point{52.106 * deg_C},
quantity_point(52.492 * deg_C), quantity_point(52.819 * deg_C), quantity_point{52.492 * deg_C}, quantity_point{52.819 * deg_C},
quantity_point(53.433 * deg_C), quantity_point(54.007 * deg_C), quantity_point{53.433 * deg_C}, quantity_point{54.007 * deg_C},
quantity_point(54.523 * deg_C), quantity_point(54.99 * deg_C)}; quantity_point{54.523 * deg_C}, quantity_point{54.99 * deg_C}};
const auto measurement_uncertainty = pow<2>(0.1 * deg_C); const auto measurement_uncertainty = pow<2>(0.1 * deg_C);
auto update = [=]<QuantityPoint QP>(const estimation<QP>& previous, const QP& meassurement, Dimensionless auto gain) { auto update = [=]<QuantityPoint QP>(const estimation<QP>& previous, const QP& meassurement,
QuantityOf<dimensionless> auto gain) {
return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)}; return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)};
}; };

View File

@@ -1,64 +0,0 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Mateusz Pusz
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
cmake_minimum_required(VERSION 3.2)
#
# add_example(target <depependencies>...)
#
function(add_example target)
add_executable(${target}-literals ${target}.cpp)
target_link_libraries(${target}-literals PRIVATE ${ARGN})
target_compile_definitions(${target}-literals PRIVATE ${projectPrefix}NO_REFERENCES ${projectPrefix}NO_ALIASES)
endfunction()
add_example(avg_speed mp-units::core-io mp-units::si mp-units::si-cgs mp-units::si-international)
add_example(box_example mp-units::core-fmt mp-units::si)
add_example(capacitor_time_curve mp-units::core-io mp-units::si)
add_example(
clcpp_response
mp-units::core-fmt
mp-units::core-io
mp-units::si
mp-units::si-iau
mp-units::si-imperial
mp-units::si-international
mp-units::si-typographic
mp-units::si-uscs
)
add_example(experimental_angle mp-units::core-fmt mp-units::core-io mp-units::si)
add_example(foot_pound_second mp-units::core-fmt mp-units::si-fps)
add_example(total_energy mp-units::core-io mp-units::si mp-units::isq-natural)
add_example(unknown_dimension mp-units::core-io mp-units::si)
if(NOT ${projectPrefix}LIBCXX)
add_example(glide_computer_example mp-units::core-fmt mp-units::si-international glide_computer)
target_compile_definitions(
glide_computer_example-literals PRIVATE ${projectPrefix}NO_REFERENCES ${projectPrefix}NO_ALIASES
)
if(${projectPrefix}BUILD_LA)
find_package(wg21_linear_algebra CONFIG REQUIRED)
add_example(linear_algebra mp-units::core-fmt mp-units::core-io mp-units::si)
target_link_libraries(linear_algebra-literals PRIVATE wg21_linear_algebra::wg21_linear_algebra)
endif()
endif()

View File

@@ -1,190 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/isq/si/cgs/length.h>
#include <units/isq/si/cgs/speed.h> // IWYU pragma: keep
#include <units/isq/si/international/length.h>
#include <units/isq/si/international/speed.h> // IWYU pragma: keep
#include <units/isq/si/length.h> // IWYU pragma: keep
#include <units/isq/si/speed.h>
#include <units/isq/si/time.h>
#include <units/quantity_io.h>
#include <exception>
#include <iostream>
namespace {
using namespace units::isq;
constexpr si::speed<si::metre_per_second, int> fixed_int_si_avg_speed(si::length<si::metre, int> d,
si::time<si::second, int> t)
{
return d / t;
}
constexpr si::speed<si::metre_per_second> fixed_double_si_avg_speed(si::length<si::metre> d, si::time<si::second> t)
{
return d / t;
}
template<typename U1, typename R1, typename U2, typename R2>
constexpr Speed auto si_avg_speed(si::length<U1, R1> d, si::time<U2, R2> t)
{
return d / t;
}
constexpr Speed auto avg_speed(Length auto d, Time auto t) { return d / t; }
template<Length D, Time T, Speed V>
void print_result(D distance, T duration, V speed)
{
const auto result_in_kmph = units::quantity_cast<si::speed<si::kilometre_per_hour>>(speed);
std::cout << "Average speed of a car that makes " << distance << " in " << duration << " is " << result_in_kmph
<< ".\n";
}
void example()
{
// SI (int)
{
using namespace units::isq::si::literals;
constexpr auto distance = 220_q_km;
constexpr auto duration = 2_q_h;
std::cout << "SI units with 'int' as representation\n";
print_result(distance, duration, fixed_int_si_avg_speed(distance, duration));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
print_result(distance, duration, si_avg_speed(distance, duration));
print_result(distance, duration, avg_speed(distance, duration));
}
// SI (double)
{
using namespace units::isq::si::literals;
constexpr auto distance = 220._q_km;
constexpr auto duration = 2._q_h;
std::cout << "\nSI units with 'double' as representation\n";
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
print_result(distance, duration,
fixed_int_si_avg_speed(quantity_cast<int>(distance), quantity_cast<int>(duration)));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
print_result(distance, duration, si_avg_speed(distance, duration));
print_result(distance, duration, avg_speed(distance, duration));
}
// Customary Units (int)
{
using namespace units::isq::si::international::literals;
using namespace units::isq::si::literals;
constexpr auto distance = 140_q_mi;
constexpr auto duration = 2_q_h;
std::cout << "\nUS Customary Units with 'int' as representation\n";
// it is not possible to make a lossless conversion of miles to meters on an integral type
// (explicit cast needed)
print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast<si::metre>(distance), duration));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
print_result(distance, duration, si_avg_speed(distance, duration));
print_result(distance, duration, avg_speed(distance, duration));
}
// Customary Units (double)
{
using namespace units::isq::si::international::literals;
using namespace units::isq::si::literals;
constexpr auto distance = 140._q_mi;
constexpr auto duration = 2._q_h;
std::cout << "\nUS Customary Units with 'double' as representation\n";
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
// also it is not possible to make a lossless conversion of miles to meters on an integral type
// (explicit cast needed)
print_result(
distance, duration,
fixed_int_si_avg_speed(quantity_cast<si::length<si::metre, int>>(distance), quantity_cast<int>(duration)));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
print_result(distance, duration, si_avg_speed(distance, duration));
print_result(distance, duration, avg_speed(distance, duration));
}
// CGS (int)
{
using namespace units::isq::si::cgs::literals;
constexpr auto distance = 22'000'000_q_cm;
constexpr si::cgs::time<si::hour, int> duration(2); // cannot use an SI literal here
std::cout << "\nCGS units with 'int' as representation\n";
// it is not possible to make a lossless conversion of centimeters to meters on an integral type
// (explicit cast needed)
print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast<si::metre>(distance), duration));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
// not possible to convert both a dimension and a unit with implicit cast
print_result(distance, duration, si_avg_speed(quantity_cast<si::dim_length>(distance), duration));
print_result(distance, duration, avg_speed(distance, duration));
}
// CGS (double)
{
using namespace units::isq::si::cgs::literals;
constexpr auto distance = 22'000'000._q_cm;
constexpr si::cgs::time<si::hour, double> duration(2); // cannot use an SI literal here
std::cout << "\nCGS units with 'double' as representation\n";
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
// it is not possible to make a lossless conversion of centimeters to meters on an integral type
// (explicit cast needed)
print_result(
distance, duration,
fixed_int_si_avg_speed(quantity_cast<si::length<si::metre, int>>(distance), quantity_cast<int>(duration)));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
// not possible to convert both a dimension and a unit with implicit cast
print_result(distance, duration, si_avg_speed(quantity_cast<si::dim_length>(distance), duration));
print_result(distance, duration, avg_speed(distance, duration));
}
}
} // namespace
int main()
{
try {
example();
} catch (const std::exception& ex) {
std::cerr << "Unhandled std exception caught: " << ex.what() << '\n';
} catch (...) {
std::cerr << "Unhandled unknown exception caught\n";
}
}

View File

@@ -1,116 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/format.h>
#include <units/generic/dimensionless.h>
#include <units/isq/si/amount_of_substance.h>
#include <units/isq/si/area.h>
#include <units/isq/si/constants.h>
#include <units/isq/si/density.h>
#include <units/isq/si/force.h>
#include <units/isq/si/length.h>
#include <units/isq/si/mass.h>
#include <units/isq/si/speed.h> // IWYU pragma: keep
#include <units/isq/si/time.h>
#include <units/isq/si/volume.h>
#include <cassert>
#include <iostream>
#include <utility>
namespace {
using namespace units::isq;
using m = si::metre;
using m2 = si::square_metre;
using m3 = si::cubic_metre;
using kg = si::kilogram;
using N = si::newton;
using kgpm3 = si::kilogram_per_metre_cub;
inline constexpr auto g = si::standard_gravity<>;
inline constexpr si::density<kgpm3> air_density(1.225);
class Box {
si::area<m2> base_;
si::length<m> height_;
si::density<kgpm3> density_ = air_density;
public:
constexpr Box(const si::length<m>& length, const si::length<m>& width, si::length<m> height) :
base_(length * width), height_(std::move(height))
{
}
[[nodiscard]] constexpr si::force<N> filled_weight() const
{
const si::volume<m3> volume = base_ * height_;
const si::mass<kg> mass = density_ * volume;
return mass * g;
}
[[nodiscard]] constexpr si::length<m> fill_level(const si::mass<kg>& measured_mass) const
{
return height_ * measured_mass * g / filled_weight();
}
[[nodiscard]] constexpr si::volume<m3> spare_capacity(const si::mass<kg>& measured_mass) const
{
return (height_ - fill_level(measured_mass)) * base_;
}
constexpr void set_contents_density(const si::density<kgpm3>& density_in)
{
assert(density_in > air_density);
density_ = density_in;
}
};
} // namespace
int main()
{
using namespace units;
using namespace si::literals;
const si::length<m> height(200.0_q_mm);
auto box = Box(1000.0_q_mm, 500.0_q_mm, height);
box.set_contents_density(1000.0_q_kg_per_m3);
const auto fill_time = 200.0_q_s; // time since starting fill
const auto measured_mass = 20.0_q_kg; // measured mass at fill_time
const Length auto fill_level = box.fill_level(measured_mass);
const Dimensionless auto fill_percent = quantity_cast<percent>(fill_level / height);
const Volume auto spare_capacity = box.spare_capacity(measured_mass);
const auto input_flow_rate = measured_mass / fill_time; // unknown dimension
const Speed auto float_rise_rate = fill_level / fill_time;
const Time auto fill_time_left = (height / fill_level - 1) * fill_time;
std::cout << "mp-units box example...\n";
std::cout << UNITS_STD_FMT::format("fill height at {} = {} ({} full)\n", fill_time, fill_level, fill_percent);
std::cout << UNITS_STD_FMT::format("spare_capacity at {} = {}\n", fill_time, spare_capacity);
std::cout << UNITS_STD_FMT::format("input flow rate after {} = {}\n", fill_time, input_flow_rate);
std::cout << UNITS_STD_FMT::format("float rise rate = {}\n", float_rise_rate);
std::cout << UNITS_STD_FMT::format("box full E.T.A. at current flow rate = {}\n", fill_time_left);
}

View File

@@ -1,63 +0,0 @@
/*
Copyright (c) 2003-2020 Andy Little.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses./
*/
/*
capacitor discharge curve using compile_time
physical_quantities
*/
#include <units/generic/dimensionless.h>
#include <units/isq/dimensions/electric_current.h>
#include <units/isq/si/capacitance.h>
#include <units/isq/si/resistance.h>
#include <units/isq/si/time.h>
#include <units/isq/si/voltage.h>
#include <units/math.h> // IWYU pragma: keep
#include <units/quantity_io.h>
#include <iostream>
int main()
{
using namespace units::isq;
using namespace units::isq::si;
std::cout << "mp-units capacitor time curve example...\n";
std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
std::cout.precision(3);
constexpr auto C = 0.47_q_uF;
constexpr auto V0 = 5.0_q_V;
constexpr auto R = 4.7_q_kR;
for (auto t = 0_q_ms; t <= 50_q_ms; ++t) {
const Voltage auto Vt = V0 * units::exp(-t / (R * C));
std::cout << "at " << t << " voltage is ";
if (Vt >= 1_q_V)
std::cout << Vt;
else if (Vt >= 1_q_mV)
std::cout << quantity_cast<millivolt>(Vt);
else if (Vt >= 1_q_uV)
std::cout << quantity_cast<microvolt>(Vt);
else if (Vt >= 1_q_nV)
std::cout << quantity_cast<nanovolt>(Vt);
else
std::cout << quantity_cast<picovolt>(Vt);
std::cout << "\n";
}
}

View File

@@ -1,150 +0,0 @@
/*
Copyright (c) 2003-2019 Andy Little.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses./
*/
#include <units/format.h>
#include <units/isq/si/area.h>
#include <units/isq/si/iau/length.h>
#include <units/isq/si/imperial/length.h>
#include <units/isq/si/international/length.h>
#include <units/isq/si/length.h>
#include <units/isq/si/time.h>
#include <units/isq/si/typographic/length.h>
#include <units/isq/si/uscs/length.h>
#include <units/quantity_io.h>
#include <iostream>
namespace {
using namespace units;
void simple_quantities()
{
using namespace units::isq::si;
using namespace units::isq::si::international;
using distance = length<metre>;
using duration = isq::si::time<second>;
constexpr distance km = 1.0_q_km;
constexpr distance miles = 1.0_q_mi;
constexpr duration sec = 1_q_s;
constexpr duration min = 1_q_min;
constexpr duration hr = 1_q_h;
std::cout << "A physical quantities library can choose the simple\n";
std::cout << "option to provide output using a single type for each base unit:\n\n";
std::cout << km << '\n';
std::cout << miles << '\n';
std::cout << sec << '\n';
std::cout << min << '\n';
std::cout << hr << "\n\n";
}
void quantities_with_typed_units()
{
using namespace units::isq;
using namespace units::isq::si;
using namespace units::isq::si::international;
constexpr length<kilometre> km = 1.0_q_km;
constexpr length<mile> miles = 1.0_q_mi;
std::cout.precision(6);
constexpr si::time<second> sec = 1_q_s;
constexpr si::time<minute> min = 1_q_min;
constexpr si::time<hour> hr = 1_q_h;
std::cout << "A more flexible option is to provide separate types for each unit,\n\n";
std::cout << km << '\n';
std::cout << miles << '\n';
std::cout << sec << '\n';
std::cout << min << '\n';
std::cout << hr << "\n\n";
constexpr length<metre> meter = 1_q_m;
std::cout << "then a wide range of pre-defined units can be defined and converted,\n"
" for consistency and repeatability across applications:\n\n";
std::cout << meter << '\n';
std::cout << " = " << quantity_cast<si::astronomical_unit>(meter) << '\n';
std::cout << " = " << quantity_cast<si::iau::angstrom>(meter) << '\n';
std::cout << " = " << quantity_cast<si::imperial::chain>(meter) << '\n';
std::cout << " = " << quantity_cast<si::international::fathom>(meter) << '\n';
std::cout << " = " << quantity_cast<si::uscs::fathom>(meter) << '\n';
std::cout << " = " << quantity_cast<si::international::foot>(meter) << '\n';
std::cout << " = " << quantity_cast<si::uscs::foot>(meter) << '\n';
std::cout << " = " << quantity_cast<si::international::inch>(meter) << '\n';
std::cout << " = " << quantity_cast<si::iau::light_year>(meter) << '\n';
std::cout << " = " << quantity_cast<si::international::mile>(meter) << '\n';
std::cout << " = " << quantity_cast<si::international::nautical_mile>(meter) << '\n';
std::cout << " = " << quantity_cast<si::iau::parsec>(meter) << '\n';
std::cout << " = " << quantity_cast<si::typographic::pica_comp>(meter) << '\n';
std::cout << " = " << quantity_cast<si::typographic::pica_prn>(meter) << '\n';
std::cout << " = " << quantity_cast<si::typographic::point_comp>(meter) << '\n';
std::cout << " = " << quantity_cast<si::typographic::point_prn>(meter) << '\n';
std::cout << " = " << quantity_cast<si::imperial::rod>(meter) << '\n';
std::cout << " = " << quantity_cast<si::international::yard>(meter) << '\n';
}
void calcs_comparison()
{
using namespace units::isq::si;
std::cout << "\nA distinct unit for each type is efficient and accurate\n"
"when adding two values of the same very big\n"
"or very small type:\n\n";
const length<femtometre, float> L1A = 2._q_fm;
const length<femtometre, float> L2A = 3._q_fm;
const length<femtometre, float> LrA = L1A + L2A;
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n + {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, LrA);
std::cout << "The single unit method must convert large\n"
"or small values in other units to the base unit.\n"
"This is both inefficient and inaccurate\n\n";
const length<metre, float> L1B = L1A;
const length<metre, float> L2B = L2A;
const length<metre, float> LrB = L1B + L2B;
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n + {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1B, L2B, LrB);
std::cout << "In multiplication and division:\n\n";
const area<square_femtometre, float> ArA = L1A * L2A;
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n * {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, ArA);
std::cout << "similar problems arise\n\n";
const area<square_metre, float> ArB = L1B * L2B;
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n * {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1B, L2B, ArB);
}
} // namespace
int main()
{
std::cout << "This demo was originally posted on com.lang.c++.moderated in 2006\n";
std::cout << "http://compgroups.net/comp.lang.c++.moderated/dimensional-analysis-units/51712\n";
std::cout << "Here converted to use mp-units library.\n\n";
simple_quantities();
quantities_with_typed_units();
calcs_comparison();
}

View File

@@ -1,44 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/generic/angle.h>
#include <units/isq/si/force.h>
#include <units/isq/si/length.h>
#include <units/isq/si/torque.h>
#include <units/math.h>
#include <units/quantity_io.h>
#include <iostream>
int main()
{
using namespace units;
using namespace units::isq;
using namespace units::isq::si::literals;
const Length auto lever = 20_q_cm;
const Force auto force = 500_q_N;
const Angle auto angle = 90._q_deg;
const Torque auto torque = lever * force * sin(angle) / cotes_angle<>;
std::cout << "Applying a perpendicular force of " << force << " to a " << lever << " long lever results in "
<< quantity_cast<si::newton_metre_per_radian>(torque) << " of torque.\n";
}

View File

@@ -1,137 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/format.h>
#include <units/isq/si/fps/density.h>
#include <units/isq/si/fps/length.h>
#include <units/isq/si/fps/mass.h>
#include <units/isq/si/fps/power.h>
#include <units/isq/si/fps/speed.h>
#include <units/isq/si/international/speed.h>
#include <units/isq/si/length.h>
#include <units/isq/si/mass.h>
#include <units/isq/si/power.h>
#include <units/isq/si/speed.h>
#include <units/isq/si/volume.h>
#include <iostream>
#include <string_view>
using namespace units::isq;
// Some basic specs for the warship
struct Ship {
si::fps::length<si::fps::foot> length;
si::fps::length<si::fps::foot> draft;
si::fps::length<si::fps::foot> beam;
si::fps::speed<si::fps::foot_per_second> speed;
si::fps::mass<si::fps::pound> mass;
si::fps::length<si::fps::inch> mainGuns;
si::fps::mass<si::fps::pound> shellMass;
si::fps::speed<si::fps::foot_per_second> shellSpeed;
si::fps::power<si::fps::foot_poundal_per_second> power;
};
// Print 'a' in its current units and print its value cast to the units in each of Args
template<class... Args, units::Quantity Q>
auto fmt_line(const Q a)
{
return UNITS_STD_FMT::format("{:22}", a) + (UNITS_STD_FMT::format(",{:20}", units::quantity_cast<Args>(a)) + ...);
}
// Print the ship details in the units as defined in the Ship struct, in other si::imperial units, and in SI
void print_details(std::string_view description, const Ship& ship)
{
using namespace units::isq::si::fps::literals;
const auto waterDensity = 62.4_q_lb_per_ft3;
std::cout << UNITS_STD_FMT::format("{}\n", description);
std::cout
<< UNITS_STD_FMT::format("{:20} : {}\n", "length",
fmt_line<si::fps::length<si::fps::yard>, si::length<si::metre>>(ship.length))
<< UNITS_STD_FMT::format("{:20} : {}\n", "draft",
fmt_line<si::fps::length<si::fps::yard>, si::length<si::metre>>(ship.draft))
<< UNITS_STD_FMT::format("{:20} : {}\n", "beam",
fmt_line<si::fps::length<si::fps::yard>, si::length<si::metre>>(ship.beam))
<< UNITS_STD_FMT::format("{:20} : {}\n", "mass",
fmt_line<si::fps::mass<si::fps::long_ton>, si::mass<si::tonne>>(ship.mass))
<< UNITS_STD_FMT::format(
"{:20} : {}\n", "speed",
fmt_line<si::speed<si::international::knot>, si::speed<si::kilometre_per_hour>>(ship.speed))
<< UNITS_STD_FMT::format("{:20} : {}\n", "power",
fmt_line<si::fps::power<si::fps::horse_power>, si::power<si::kilowatt>>(ship.power))
<< UNITS_STD_FMT::format("{:20} : {}\n", "main guns",
fmt_line<si::fps::length<si::fps::inch>, si::length<si::millimetre>>(ship.mainGuns))
<< UNITS_STD_FMT::format("{:20} : {}\n", "fire shells weighing",
fmt_line<si::fps::mass<si::fps::long_ton>, si::mass<si::kilogram>>(ship.shellMass))
<< UNITS_STD_FMT::format(
"{:20} : {}\n", "fire shells at",
fmt_line<si::fps::speed<si::fps::mile_per_hour>, si::speed<si::kilometre_per_hour>>(ship.shellSpeed))
<< UNITS_STD_FMT::format("{:20} : {}\n", "volume underwater",
fmt_line<si::volume<si::cubic_metre>, si::volume<si::litre>>(ship.mass / waterDensity));
}
int main()
{
using namespace units::isq::si::literals;
using namespace units::isq::si::fps::literals;
// KMS Bismark, using the units the Germans would use, taken from Wiki
auto bismark = Ship{.length{251._q_m},
.draft{9.3_q_m},
.beam{36_q_m},
.speed{56_q_km_per_h},
.mass{50'300_q_t},
.mainGuns{380_q_mm},
.shellMass{800_q_kg},
.shellSpeed{820._q_m_per_s},
.power{110.45_q_kW}};
// USS Iowa, using units from the foot-pound-second system
auto iowa = Ship{.length{860._q_ft},
.draft{37._q_ft + 2._q_in},
.beam{108._q_ft + 2._q_in},
.speed{si::speed<si::international::knot>{33}},
.mass{57'540_q_lton},
.mainGuns{16_q_in},
.shellMass{2700_q_lb},
.shellSpeed{2690._q_ft_per_s},
.power{212'000_q_hp}};
// HMS King George V, using units from the foot-pound-second system
auto kgv = Ship{.length{745.1_q_ft},
.draft{33._q_ft + 7.5_q_in},
.beam{103.2_q_ft + 2.5_q_in},
.speed{si::speed<si::international::knot>{28.3}},
.mass{42'245_q_lton},
.mainGuns{14_q_in},
.shellMass{1'590_q_lb},
.shellSpeed{2483._q_ft_per_s},
.power{110'000_q_hp}};
print_details("KMS Bismark, defined in appropriate units from the SI system", bismark);
std::cout << "\n\n";
print_details("USS Iowa, defined in appropriate units foot-pound-second system", iowa);
std::cout << "\n\n";
print_details("HMS King George V, defined in appropriate units foot-pound-second system", kgv);
}

View File

@@ -1,201 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "glide_computer.h"
#include <units/bits/fmt_hacks.h>
#include <units/chrono.h>
#include <units/generic/angle.h>
#include <units/generic/dimensionless.h>
#include <units/isq/si/international/length.h>
#include <array>
#include <exception>
#include <iostream>
#include <iterator>
#include <string>
#include <utility>
#include <vector>
namespace {
using namespace glide_computer;
using namespace units::isq;
auto get_gliders()
{
using namespace si::literals;
UNITS_DIAGNOSTIC_PUSH
UNITS_DIAGNOSTIC_IGNORE_MISSING_BRACES
static const std::array gliders = {
glider{"SZD-30 Pirat", {velocity(83_q_km_per_h), rate_of_climb(-0.7389_q_m_per_s)}},
glider{"SZD-51 Junior", {velocity(80_q_km_per_h), rate_of_climb(-0.6349_q_m_per_s)}},
glider{"SZD-48 Jantar Std 3", {velocity(110_q_km_per_h), rate_of_climb(-0.77355_q_m_per_s)}},
glider{"SZD-56 Diana", {velocity(110_q_km_per_h), rate_of_climb(-0.63657_q_m_per_s)}}};
UNITS_DIAGNOSTIC_POP
return gliders;
}
auto get_weather_conditions()
{
using namespace si::literals;
static const std::array weather_conditions = {
std::pair("Good", weather{height(1900_q_m), rate_of_climb(4.3_q_m_per_s)}),
std::pair("Medium", weather{height(1550_q_m), rate_of_climb(2.8_q_m_per_s)}),
std::pair("Bad", weather{height(850_q_m), rate_of_climb(1.8_q_m_per_s)})};
return weather_conditions;
}
auto get_waypoints()
{
using namespace geographic::literals;
using namespace units::isq::si::international::literals;
static const std::array waypoints = {
waypoint{"EPPR", {54.24772_N, 18.6745_E}, altitude(16_q_ft)}, // N54°14'51.8" E18°40'28.2"
waypoint{"EPGI", {53.52442_N, 18.84947_E}, altitude(115_q_ft)} // N53°31'27.9" E18°50'58.1"
};
return waypoints;
}
template<std::ranges::input_range R>
requires(std::same_as<std::ranges::range_value_t<R>, glider>)
void print(const R& gliders)
{
std::cout << "Gliders:\n";
std::cout << "========\n";
for (const auto& g : gliders) {
std::cout << "- Name: " << g.name << "\n";
std::cout << "- Polar:\n";
for (const auto& p : g.polar) {
const auto ratio = units::quantity_cast<units::one>(glide_ratio(g.polar[0]));
std::cout << UNITS_STD_FMT::format(" * {:%.4Q %q} @ {:%.1Q %q} -> {:%.1Q %q} ({:%.1Q %q})\n", p.climb, p.v,
ratio, units::quantity_cast<units::degree>(asin(1 / ratio)));
}
std::cout << "\n";
}
}
template<std::ranges::input_range R>
requires(std::same_as<std::ranges::range_value_t<R>, std::pair<const char*, weather>>)
void print(const R& conditions)
{
std::cout << "Weather:\n";
std::cout << "========\n";
for (const auto& c : conditions) {
std::cout << "- " << c.first << "\n";
const auto& w = c.second;
std::cout << " * Cloud base: " << UNITS_STD_FMT::format("{:%.0Q %q}", w.cloud_base) << " AGL\n";
std::cout << " * Thermals strength: " << UNITS_STD_FMT::format("{:%.1Q %q}", w.thermal_strength) << "\n";
std::cout << "\n";
}
}
template<std::ranges::input_range R>
requires(std::same_as<std::ranges::range_value_t<R>, waypoint>)
void print(const R& waypoints)
{
std::cout << "Waypoints:\n";
std::cout << "==========\n";
for (const auto& w : waypoints)
std::cout << UNITS_STD_FMT::format("- {}: {} {}, {:%.1Q %q}\n", w.name, w.pos.lat, w.pos.lon, w.alt);
std::cout << "\n";
}
void print(const task& t)
{
std::cout << "Task:\n";
std::cout << "=====\n";
std::cout << "- Start: " << t.get_start().name << "\n";
std::cout << "- Finish: " << t.get_finish().name << "\n";
std::cout << "- Length: " << UNITS_STD_FMT::format("{:%.1Q %q}", t.get_length()) << "\n";
std::cout << "- Legs: "
<< "\n";
for (const auto& l : t.get_legs())
std::cout << UNITS_STD_FMT::format(" * {} -> {} ({:%.1Q %q})\n", l.begin().name, l.end().name, l.get_length());
std::cout << "\n";
}
void print(const safety& s)
{
std::cout << "Safety:\n";
std::cout << "=======\n";
std::cout << "- Min AGL separation: " << UNITS_STD_FMT::format("{:%.0Q %q}", s.min_agl_height) << "\n";
std::cout << "\n";
}
void print(const aircraft_tow& tow)
{
std::cout << "Tow:\n";
std::cout << "====\n";
std::cout << "- Type: aircraft\n";
std::cout << "- Height: " << UNITS_STD_FMT::format("{:%.0Q %q}", tow.height_agl) << "\n";
std::cout << "- Performance: " << UNITS_STD_FMT::format("{:%.1Q %q}", tow.performance) << "\n";
std::cout << "\n";
}
void example()
{
using namespace si::literals;
const safety sfty = {height(300_q_m)};
const auto gliders = get_gliders();
const auto waypoints = get_waypoints();
const auto weather_conditions = get_weather_conditions();
const task t = {waypoints[0], waypoints[1], waypoints[0]};
const aircraft_tow tow = {height(400_q_m), rate_of_climb(1.6_q_m_per_s)};
// TODO use C++20 date library when available
// set `start_time` to 11:00 am today
const timestamp start_time(std::chrono::system_clock::now());
print(sfty);
print(gliders);
print(waypoints);
print(weather_conditions);
print(t);
print(tow);
for (const auto& g : gliders) {
for (const auto& c : weather_conditions) {
std::string txt = "Scenario: Glider = " + g.name + ", Weather = " + c.first;
std::cout << txt << "\n";
std::cout << UNITS_STD_FMT::format("{0:=^{1}}\n\n", "", txt.size());
estimate(start_time, g, c.second, t, sfty, tow);
std::cout << "\n\n";
}
}
}
} // namespace
int main()
{
try {
example();
} catch (const std::exception& ex) {
std::cerr << "Unhandled std exception caught: " << ex.what() << '\n';
} catch (...) {
std::cerr << "Unhandled unknown exception caught\n";
}
}

View File

@@ -1,231 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/format.h>
#include <units/isq/si/energy.h> // IWYU pragma: keep
#include <units/isq/si/force.h>
#include <units/isq/si/length.h>
#include <units/isq/si/speed.h> // IWYU pragma: keep
#include <units/quantity_io.h>
#include <iostream>
#include <matrix>
template<typename Rep = double>
using vector = STD_LA::fixed_size_column_vector<Rep, 3>;
template<typename Rep = double>
using matrix = STD_LA::fixed_size_matrix<Rep, 3, 3>;
namespace STD_LA {
template<typename Rep>
std::ostream& operator<<(std::ostream& os, const ::vector<Rep>& v)
{
os << "|";
for (auto i = 0U; i < v.size(); ++i) {
os << UNITS_STD_FMT::format(" {:>9}", v(i));
}
os << " |";
return os;
}
template<typename Rep>
std::ostream& operator<<(std::ostream& os, const ::matrix<Rep>& v)
{
for (auto i = 0U; i < v.rows(); ++i) {
os << "|";
for (auto j = 0U; j < v.columns(); ++j) {
os << UNITS_STD_FMT::format(" {:>9}", v(i, j));
}
os << (i != v.rows() - 1U ? " |\n" : " |");
}
return os;
}
} // namespace STD_LA
namespace {
using namespace units::isq;
using namespace units::isq::si::literals;
void vector_of_quantity_add()
{
std::cout << "\nvector_of_quantity_add:\n";
vector<si::length<si::metre>> v = {4_q_m, 8_q_m, 12_q_m};
vector<si::length<si::metre>> u = {3_q_m, 2_q_m, 1_q_m};
vector<si::length<si::kilometre>> t = {3_q_km, 2_q_km, 1_q_km};
std::cout << "v = " << v << "\n";
std::cout << "u = " << u << "\n";
std::cout << "t = " << t << "\n";
std::cout << "v + u = " << v + u << "\n";
std::cout << "v + t = " << v + t << "\n";
std::cout << "t[m] = " << vector<si::length<si::metre>>(t) << "\n";
}
void vector_of_quantity_divide_by_scalar()
{
std::cout << "\nvector_of_quantity_divide_by_scalar:\n";
vector<si::length<si::metre>> v = {4_q_m, 8_q_m, 12_q_m};
std::cout << "v = " << v << "\n";
std::cout << "v / 2_q_s = " << v / quantity_cast<double>(2_q_s) << "\n";
std::cout << "v / 2 = " << v / 2 << "\n";
}
void vector_of_quantity_tests()
{
vector_of_quantity_add();
vector_of_quantity_divide_by_scalar();
}
void matrix_of_quantity_add()
{
std::cout << "\nmatrix_of_quantity_add:\n";
matrix<si::length<si::metre>> v = {{1_q_m, 2_q_m, 3_q_m}, {4_q_m, 5_q_m, 6_q_m}, {7_q_m, 8_q_m, 9_q_m}};
matrix<si::length<si::metre>> u = {{3_q_m, 2_q_m, 1_q_m}, {3_q_m, 2_q_m, 1_q_m}, {3_q_m, 2_q_m, 1_q_m}};
matrix<si::length<si::millimetre>> t = {{3_q_mm, 2_q_mm, 1_q_mm}, {3_q_mm, 2_q_mm, 1_q_mm}, {3_q_mm, 2_q_mm, 1_q_mm}};
std::cout << "v =\n" << v << "\n";
std::cout << "u =\n" << u << "\n";
std::cout << "t =\n" << t << "\n";
std::cout << "v + u =\n" << v + u << "\n";
std::cout << "v + t =\n" << v + t << "\n";
std::cout << "v[mm] =\n" << matrix<si::length<si::millimetre>>(v) << "\n";
}
void matrix_of_quantity_divide_by_scalar()
{
std::cout << "\nmatrix_of_quantity_divide_by_scalar:\n";
matrix<si::length<si::metre>> v = {{2_q_m, 4_q_m, 6_q_m}, {4_q_m, 6_q_m, 8_q_m}, {8_q_m, 4_q_m, 2_q_m}};
std::cout << "v =\n" << v << "\n";
std::cout << "v / 2_q_s =\n" << v / quantity_cast<double>(2_q_s) << "\n";
std::cout << "v / 2 =\n" << v / 2 << "\n";
}
void matrix_of_quantity_tests()
{
matrix_of_quantity_add();
matrix_of_quantity_divide_by_scalar();
}
template<units::Unit U = si::metre, units::Representation Rep = double>
using length_v = si::length<U, vector<Rep>>;
template<units::Unit U = si::newton, units::Representation Rep = double>
using force_v = si::force<U, vector<Rep>>;
void quantity_of_vector_add()
{
std::cout << "\nquantity_of_vector_add:\n";
length_v<> v(vector<>{4, 8, 12});
length_v<> u(vector<>{3, 2, 1});
length_v<si::kilometre> t(vector<>{3, 2, 1});
std::cout << "v = " << v << "\n";
std::cout << "u = " << u << "\n";
std::cout << "t = " << t << "\n";
std::cout << "v + u = " << v + u << "\n";
std::cout << "v + t = " << v + t << "\n";
std::cout << "t[m] = " << quantity_cast<si::metre>(t) << "\n";
}
void quantity_of_vector_divide_by_scalar()
{
std::cout << "\nquantity_of_vector_divide_by_scalar:\n";
length_v<> v(vector<>{4, 8, 12});
std::cout << "v = " << v << "\n";
std::cout << "v / 2_q_s = " << v / 2_q_s << "\n";
std::cout << "v / 2 = " << v / 2 << "\n";
}
void quantity_of_vector_tests()
{
quantity_of_vector_add();
quantity_of_vector_divide_by_scalar();
}
template<units::Unit U = si::metre, units::Representation Rep = double>
using length_m = si::length<U, matrix<Rep>>;
void quantity_of_matrix_add()
{
std::cout << "\nquantity_of_matrix_add:\n";
length_m<> v(matrix<>{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
length_m<> u(matrix<>{{3, 2, 1}, {3, 2, 1}, {3, 2, 1}});
length_m<si::kilometre> t(matrix<>{{3, 2, 1}, {3, 2, 1}, {3, 2, 1}});
std::cout << "v =\n" << v << "\n";
std::cout << "u =\n" << u << "\n";
std::cout << "t =\n" << t << "\n";
std::cout << "v + u =\n" << v + u << "\n";
std::cout << "v + t =\n" << v + t << "\n";
// TODO Fix it
// std::cout << "v[mm] =\n" << matrix<si::length<si::millimetre>>(v) << "\n";
}
void quantity_of_matrix_divide_by_scalar()
{
std::cout << "\nquantity_of_matrix_divide_by_scalar:\n";
length_m<> v(matrix<>{{2, 4, 6}, {4, 6, 8}, {8, 4, 2}});
std::cout << "v =\n" << v << "\n";
std::cout << "v / 2_q_s =\n" << v / 2_q_s << "\n";
std::cout << "v / 2 =\n" << v / 2 << "\n";
}
void quantity_of_matrix_tests()
{
quantity_of_matrix_add();
quantity_of_matrix_divide_by_scalar();
}
} // namespace
int main()
{
vector_of_quantity_tests();
matrix_of_quantity_tests();
quantity_of_vector_tests();
quantity_of_matrix_tests();
}

View File

@@ -1,103 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/isq/natural/natural.h>
#include <units/isq/si/constants.h>
#include <units/isq/si/energy.h>
#include <units/isq/si/mass.h>
#include <units/isq/si/momentum.h>
#include <units/isq/si/speed.h> // IWYU pragma: keep
#include <units/math.h>
#include <units/quantity_io.h>
#include <exception>
#include <iostream>
namespace {
using namespace units::isq;
Energy auto total_energy(Momentum auto p, Mass auto m, Speed auto c)
{
return sqrt(pow<2>(p * c) + pow<2>(m * pow<2>(c)));
}
void si_example()
{
using namespace units::isq::si;
constexpr Speed auto c = si2019::speed_of_light<>;
std::cout << "\n*** SI units (c = " << c << ") ***\n";
const Momentum auto p = 4._q_GeV / c;
const Mass auto m = 3._q_GeV / pow<2>(c);
const Energy auto E = total_energy(p, m, c);
std::cout << "[in GeV]\n"
<< "p = " << p << "\n"
<< "m = " << m << "\n"
<< "E = " << E << "\n";
const momentum<kilogram_metre_per_second> p_si = p;
const mass<kilogram> m_si = m;
const energy<joule> E_si = total_energy(p_si, m_si, c);
std::cout << "\n[in SI units]\n"
<< "p = " << p_si << "\n"
<< "m = " << m_si << "\n"
<< "E = " << E_si << "\n";
std::cout << "\n[converted from SI units back to GeV]\n"
<< "E = " << quantity_cast<gigaelectronvolt>(E_si) << "\n";
}
void natural_example()
{
using namespace units::isq::natural;
// TODO Typical UDLs will not work here as the same units are reused by many quantities.
// Should we define some strange ones (i.e. _q_mass_GeV)?
constexpr Speed auto c = speed_of_light<>;
const momentum<gigaelectronvolt> p(4);
const mass<gigaelectronvolt> m(3);
const Energy auto E = total_energy(p, m, c);
std::cout << "\n*** Natural units (c = " << c << ") ***\n"
<< "p = " << p << "\n"
<< "m = " << m << "\n"
<< "E = " << E << "\n";
}
} // namespace
int main()
{
try {
si_example();
natural_example();
} catch (const std::exception& ex) {
std::cerr << "Unhandled std exception caught: " << ex.what() << '\n';
} catch (...) {
std::cerr << "Unhandled unknown exception caught\n";
}
}

View File

@@ -1,71 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/isq/si/length.h>
#include <units/isq/si/speed.h> // IWYU pragma: keep
#include <units/isq/si/time.h>
#include <units/quantity_io.h>
#include <exception>
#include <iostream>
namespace {
template<units::isq::Length D, units::isq::Time T>
constexpr units::isq::Speed auto avg_speed(D d, T t)
{
return d / t;
}
void example()
{
using namespace units::isq;
using namespace units::isq::si::literals;
Length auto d1 = 123_q_m;
Time auto t1 = 10_q_s;
Speed auto v1 = avg_speed(d1, t1);
auto temp1 =
v1 * 50_q_m; // produces intermediate unknown dimension with 'unknown_coherent_unit' as its 'coherent_unit'
Speed auto v2 = temp1 / 100_q_m; // back to known dimensions again
Length auto d2 = v2 * 60_q_s;
std::cout << "d1 = " << d1 << '\n';
std::cout << "t1 = " << t1 << '\n';
std::cout << "v1 = " << v1 << '\n';
std::cout << "temp1 = " << temp1 << '\n';
std::cout << "v2 = " << v2 << '\n';
std::cout << "d2 = " << d2 << '\n';
}
} // namespace
int main()
{
try {
example();
} catch (const std::exception& ex) {
std::cerr << "Unhandled std exception caught: " << ex.what() << '\n';
} catch (...) {
std::cerr << "Unhandled unknown exception caught\n";
}
}

View File

@@ -20,37 +20,36 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
#include <units/isq/si/acceleration.h> #include <mp-units/iostream.h>
#include <units/isq/si/length.h> #include <mp-units/systems/isq/space_and_time.h>
#include <units/isq/si/speed.h> #include <mp-units/systems/si/unit_symbols.h>
#include <units/isq/si/time.h> #include <mp-units/systems/si/units.h>
#include <units/quantity_io.h>
#include <cmath> #include <cmath>
#include <exception> #include <exception>
#include <iostream> #include <iostream>
namespace { namespace {
template<class T> template<typename T>
class measurement { class measurement {
public: public:
using value_type = T; using value_type = T;
measurement() = default; measurement() = default;
constexpr explicit measurement(const value_type& val, const value_type& err = {}) : value_(val) constexpr explicit measurement(value_type val, const value_type& err = {}) : value_(std::move(val))
{ {
// it sucks that using declaration cannot be provided for a constructor initializer list // it sucks that using declaration cannot be provided for a constructor initializer list
using namespace std; using namespace std;
uncertainty_ = abs(err); uncertainty_ = abs(err);
} }
constexpr const value_type& value() const { return value_; } [[nodiscard]] constexpr const value_type& value() const { return value_; }
constexpr const value_type& uncertainty() const { return uncertainty_; } [[nodiscard]] constexpr const value_type& uncertainty() const { return uncertainty_; }
constexpr value_type relative_uncertainty() const { return uncertainty() / value(); } [[nodiscard]] constexpr value_type relative_uncertainty() const { return uncertainty() / value(); }
constexpr value_type lower_bound() const { return value() - uncertainty(); } [[nodiscard]] constexpr value_type lower_bound() const { return value() - uncertainty(); }
constexpr value_type upper_bound() const { return value() + uncertainty(); } [[nodiscard]] constexpr value_type upper_bound() const { return value() + uncertainty(); }
[[nodiscard]] constexpr measurement operator-() const { return measurement(-value(), uncertainty()); } [[nodiscard]] constexpr measurement operator-() const { return measurement(-value(), uncertainty()); }
@@ -118,27 +117,27 @@ private:
} // namespace } // namespace
template<class T>
inline constexpr bool mp_units::is_scalar<measurement<T>> = true;
template<class T>
inline constexpr bool mp_units::is_vector<measurement<T>> = true;
static_assert(mp_units::RepresentationOf<measurement<double>, mp_units::quantity_character::scalar>);
namespace { namespace {
static_assert(units::Representation<measurement<double>>);
void example() void example()
{ {
using namespace units::isq; using namespace mp_units;
using namespace mp_units::si::unit_symbols;
const auto a = si::acceleration<si::metre_per_second_sq, measurement<double>>(measurement(9.8, 0.1)); const auto a = isq::acceleration(measurement{9.8, 0.1} * (m / s2));
const auto t = si::time<si::second, measurement<double>>(measurement(1.2, 0.1)); const auto t = measurement{1.2, 0.1} * s;
const Speed auto v1 = a * t; const QuantityOf<isq::velocity> auto v = a * t;
#if UNITS_DOWNCAST_MODE == 0 std::cout << a << " * " << t << " = " << v << " = " << v[km / h] << '\n';
std::cout << a << " * " << t << " = " << v1 << " = " << quantity_cast<si::dim_speed, si::kilometre_per_hour>(v1)
<< '\n';
#else
std::cout << a << " * " << t << " = " << v1 << " = " << quantity_cast<si::kilometre_per_hour>(v1) << '\n';
#endif
si::length<si::metre, measurement<double>> length(measurement(123., 1.)); const auto length = measurement{123., 1.} * m;
std::cout << "10 * " << length << " = " << 10 * length << '\n'; std::cout << "10 * " << length << " = " << 10 * length << '\n';
} }

View File

@@ -1,64 +0,0 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Mateusz Pusz
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
cmake_minimum_required(VERSION 3.2)
#
# add_example(target <depependencies>...)
#
function(add_example target)
add_executable(${target}-references ${target}.cpp)
target_link_libraries(${target}-references PRIVATE ${ARGN})
target_compile_definitions(${target}-references PRIVATE ${projectPrefix}NO_LITERALS ${projectPrefix}NO_ALIASES)
endfunction()
add_example(avg_speed mp-units::core-io mp-units::si mp-units::si-cgs mp-units::si-international)
add_example(box_example mp-units::core-fmt mp-units::si)
add_example(capacitor_time_curve mp-units::core-io mp-units::si)
add_example(
clcpp_response
mp-units::core-fmt
mp-units::core-io
mp-units::si
mp-units::si-iau
mp-units::si-imperial
mp-units::si-international
mp-units::si-typographic
mp-units::si-uscs
)
add_example(experimental_angle mp-units::core-fmt mp-units::core-io mp-units::si)
add_example(foot_pound_second mp-units::core-fmt mp-units::si-fps)
add_example(total_energy mp-units::core-io mp-units::si mp-units::isq-natural)
add_example(unknown_dimension mp-units::core-io mp-units::si)
if(NOT ${projectPrefix}LIBCXX)
add_example(glide_computer_example mp-units::core-fmt mp-units::si-international glide_computer)
target_compile_definitions(
glide_computer_example-references PRIVATE ${projectPrefix}NO_LITERALS ${projectPrefix}NO_ALIASES
)
if(${projectPrefix}BUILD_LA)
find_package(wg21_linear_algebra CONFIG REQUIRED)
add_example(linear_algebra mp-units::core-fmt mp-units::core-io mp-units::si)
target_link_libraries(linear_algebra-references PRIVATE wg21_linear_algebra::wg21_linear_algebra)
endif()
endif()

View File

@@ -1,112 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/format.h>
#include <units/generic/dimensionless.h>
#include <units/isq/si/amount_of_substance.h>
#include <units/isq/si/area.h>
#include <units/isq/si/constants.h>
#include <units/isq/si/density.h>
#include <units/isq/si/force.h>
#include <units/isq/si/length.h>
#include <units/isq/si/mass.h>
#include <units/isq/si/speed.h> // IWYU pragma: keep
#include <units/isq/si/time.h>
#include <units/isq/si/volume.h>
#include <cassert>
#include <iostream>
#include <utility>
namespace {
using namespace units::isq;
using namespace si::mass_references;
using namespace si::volume_references;
inline constexpr Acceleration auto g = si::standard_gravity<>;
inline constexpr Density auto air_density = 1.225 * (kg / m3);
class Box {
si::area<si::square_metre> base_;
si::length<si::metre> height_;
si::density<si::kilogram_per_metre_cub> density_ = air_density;
public:
constexpr Box(const si::length<si::metre>& length, const si::length<si::metre>& width, si::length<si::metre> height) :
base_(length * width), height_(std::move(height))
{
}
[[nodiscard]] constexpr si::force<si::newton> filled_weight() const
{
const Volume auto volume = base_ * height_;
const Mass auto mass = density_ * volume;
return mass * g;
}
[[nodiscard]] constexpr si::length<si::metre> fill_level(const si::mass<si::kilogram>& measured_mass) const
{
return height_ * measured_mass * g / filled_weight();
}
[[nodiscard]] constexpr si::volume<si::cubic_metre> spare_capacity(const si::mass<si::kilogram>& measured_mass) const
{
return (height_ - fill_level(measured_mass)) * base_;
}
constexpr void set_contents_density(const si::density<si::kilogram_per_metre_cub>& density_in)
{
assert(density_in > air_density);
density_ = density_in;
}
};
} // namespace
int main()
{
using namespace units;
using namespace units::isq::si;
using namespace length_references;
using namespace time_references;
const auto height = quantity_cast<metre>(200.0 * mm);
auto box = Box(1000.0 * mm, 500.0 * mm, height);
box.set_contents_density(1000.0 * (kg / m3));
const auto fill_time = 200.0 * s; // time since starting fill
const auto measured_mass = 20.0 * kg; // measured mass at fill_time
const Length auto fill_level = box.fill_level(measured_mass);
const Dimensionless auto fill_percent = quantity_cast<percent>(fill_level / height);
const Volume auto spare_capacity = box.spare_capacity(measured_mass);
const auto input_flow_rate = measured_mass / fill_time; // unknown dimension
const Speed auto float_rise_rate = fill_level / fill_time;
const Time auto fill_time_left = (height / fill_level - 1) * fill_time;
std::cout << "mp-units box example...\n";
std::cout << UNITS_STD_FMT::format("fill height at {} = {} ({} full)\n", fill_time, fill_level, fill_percent);
std::cout << UNITS_STD_FMT::format("spare_capacity at {} = {}\n", fill_time, spare_capacity);
std::cout << UNITS_STD_FMT::format("input flow rate after {} = {}\n", fill_time, input_flow_rate);
std::cout << UNITS_STD_FMT::format("float rise rate = {}\n", float_rise_rate);
std::cout << UNITS_STD_FMT::format("box full E.T.A. at current flow rate = {}\n", fill_time_left);
}

View File

@@ -1,159 +0,0 @@
/*
Copyright (c) 2003-2019 Andy Little.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses./
*/
#include <units/format.h>
#include <units/isq/si/area.h>
#include <units/isq/si/iau/length.h>
#include <units/isq/si/imperial/length.h>
#include <units/isq/si/international/length.h>
#include <units/isq/si/length.h>
#include <units/isq/si/time.h>
#include <units/isq/si/typographic/length.h>
#include <units/isq/si/uscs/length.h>
#include <units/quantity_io.h>
#include <iostream>
namespace {
void simple_quantities()
{
using namespace units::isq;
using namespace units::isq::si;
using namespace units::isq::si::references;
using namespace units::isq::si::international::references;
using distance = si::length<si::metre>;
using duration = si::time<si::second>;
UNITS_DIAGNOSTIC_PUSH
UNITS_DIAGNOSTIC_IGNORE_SHADOW
constexpr distance km = 1.0 * references::km;
constexpr distance miles = 1.0 * mi;
constexpr duration sec = 1 * s;
constexpr duration min = 1 * references::min;
constexpr duration hr = 1 * h;
UNITS_DIAGNOSTIC_POP
std::cout << "A physical quantities library can choose the simple\n";
std::cout << "option to provide output using a single type for each base unit:\n\n";
std::cout << km << '\n';
std::cout << miles << '\n';
std::cout << sec << '\n';
std::cout << min << '\n';
std::cout << hr << "\n\n";
}
void quantities_with_typed_units()
{
using namespace units::isq;
using namespace units::isq::si;
using namespace units::isq::si::references;
using namespace units::isq::si::international;
using namespace units::isq::si::international::references;
UNITS_DIAGNOSTIC_PUSH
UNITS_DIAGNOSTIC_IGNORE_SHADOW
constexpr length<kilometre> km = 1.0 * si::references::km;
constexpr length<mile> miles = 1.0 * mi;
std::cout.precision(6);
constexpr si::time<second> sec = 1 * s;
constexpr si::time<minute> min = 1 * si::references::min;
constexpr si::time<hour> hr = 1 * h;
UNITS_DIAGNOSTIC_POP
std::cout << "A more flexible option is to provide separate types for each unit,\n\n";
std::cout << km << '\n';
std::cout << miles << '\n';
std::cout << sec << '\n';
std::cout << min << '\n';
std::cout << hr << "\n\n";
constexpr auto meter = 1 * m;
std::cout << "then a wide range of pre-defined units can be defined and converted,\n"
" for consistency and repeatability across applications:\n\n";
std::cout << meter << '\n';
std::cout << " = " << quantity_cast<si::astronomical_unit>(meter) << '\n';
std::cout << " = " << quantity_cast<si::iau::angstrom>(meter) << '\n';
std::cout << " = " << quantity_cast<si::imperial::chain>(meter) << '\n';
std::cout << " = " << quantity_cast<si::international::fathom>(meter) << '\n';
std::cout << " = " << quantity_cast<si::uscs::fathom>(meter) << '\n';
std::cout << " = " << quantity_cast<si::international::foot>(meter) << '\n';
std::cout << " = " << quantity_cast<si::uscs::foot>(meter) << '\n';
std::cout << " = " << quantity_cast<si::international::inch>(meter) << '\n';
std::cout << " = " << quantity_cast<si::iau::light_year>(meter) << '\n';
std::cout << " = " << quantity_cast<si::international::mile>(meter) << '\n';
std::cout << " = " << quantity_cast<si::international::nautical_mile>(meter) << '\n';
std::cout << " = " << quantity_cast<si::iau::parsec>(meter) << '\n';
std::cout << " = " << quantity_cast<si::typographic::pica_comp>(meter) << '\n';
std::cout << " = " << quantity_cast<si::typographic::pica_prn>(meter) << '\n';
std::cout << " = " << quantity_cast<si::typographic::point_comp>(meter) << '\n';
std::cout << " = " << quantity_cast<si::typographic::point_prn>(meter) << '\n';
std::cout << " = " << quantity_cast<si::imperial::rod>(meter) << '\n';
std::cout << " = " << quantity_cast<si::international::yard>(meter) << '\n';
}
void calcs_comparison()
{
using namespace units::isq::si;
using namespace units::isq::si::references;
std::cout << "\nA distinct unit for each type is efficient and accurate\n"
"when adding two values of the same very big\n"
"or very small type:\n\n";
const length<femtometre, float> L1A = 2.f * fm;
const length<femtometre, float> L2A = 3.f * fm;
const length<femtometre, float> LrA = L1A + L2A;
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n + {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, LrA);
std::cout << "The single unit method must convert large\n"
"or small values in other units to the base unit.\n"
"This is both inefficient and inaccurate\n\n";
const length<metre, float> L1B = L1A;
const length<metre, float> L2B = L2A;
const length<metre, float> LrB = L1B + L2B;
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n + {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1B, L2B, LrB);
std::cout << "In multiplication and division:\n\n";
const area<square_femtometre, float> ArA = L1A * L2A;
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n * {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, ArA);
std::cout << "similar problems arise\n\n";
const area<square_metre, float> ArB = L1B * L2B;
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n * {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1B, L2B, ArB);
}
} // namespace
int main()
{
std::cout << "This demo was originally posted on com.lang.c++.moderated in 2006\n";
std::cout << "http://compgroups.net/comp.lang.c++.moderated/dimensional-analysis-units/51712\n";
std::cout << "Here converted to use mp-units library.\n\n";
simple_quantities();
quantities_with_typed_units();
calcs_comparison();
}

View File

@@ -1,141 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/format.h>
#include <units/isq/si/fps/density.h> // IWYU pragma: keep
#include <units/isq/si/fps/length.h>
#include <units/isq/si/fps/mass.h>
#include <units/isq/si/fps/power.h>
#include <units/isq/si/fps/speed.h>
#include <units/isq/si/fps/time.h>
#include <units/isq/si/fps/volume.h>
#include <units/isq/si/international/speed.h>
#include <units/isq/si/length.h>
#include <units/isq/si/mass.h>
#include <units/isq/si/power.h>
#include <units/isq/si/speed.h>
#include <units/isq/si/time.h>
#include <units/isq/si/volume.h>
#include <iostream>
#include <string_view>
using namespace units::isq;
// Some basic specs for the warship
struct Ship {
si::fps::length<si::fps::foot> length;
si::fps::length<si::fps::foot> draft;
si::fps::length<si::fps::foot> beam;
si::fps::speed<si::fps::foot_per_second> speed;
si::fps::mass<si::fps::pound> mass;
si::fps::length<si::fps::inch> mainGuns;
si::fps::mass<si::fps::pound> shellMass;
si::fps::speed<si::fps::foot_per_second> shellSpeed;
si::fps::power<si::fps::foot_poundal_per_second> power;
};
// Print 'a' in its current units and print its value cast to the units in each of Args
template<class... Args, units::Quantity Q>
auto fmt_line(const Q a)
{
return UNITS_STD_FMT::format("{:22}", a) + (UNITS_STD_FMT::format(",{:20}", units::quantity_cast<Args>(a)) + ...);
}
// Print the ship details in the units as defined in the Ship struct, in other si::imperial units, and in SI
void print_details(std::string_view description, const Ship& ship)
{
using namespace units::isq::si::fps::references;
const auto waterDensity = 62.4 * (lb / ft3);
std::cout << UNITS_STD_FMT::format("{}\n", description);
std::cout
<< UNITS_STD_FMT::format("{:20} : {}\n", "length",
fmt_line<si::fps::length<si::fps::yard>, si::length<si::metre>>(ship.length))
<< UNITS_STD_FMT::format("{:20} : {}\n", "draft",
fmt_line<si::fps::length<si::fps::yard>, si::length<si::metre>>(ship.draft))
<< UNITS_STD_FMT::format("{:20} : {}\n", "beam",
fmt_line<si::fps::length<si::fps::yard>, si::length<si::metre>>(ship.beam))
<< UNITS_STD_FMT::format("{:20} : {}\n", "mass",
fmt_line<si::fps::mass<si::fps::long_ton>, si::mass<si::tonne>>(ship.mass))
<< UNITS_STD_FMT::format(
"{:20} : {}\n", "speed",
fmt_line<si::speed<si::international::knot>, si::speed<si::kilometre_per_hour>>(ship.speed))
<< UNITS_STD_FMT::format("{:20} : {}\n", "power",
fmt_line<si::fps::power<si::fps::horse_power>, si::power<si::kilowatt>>(ship.power))
<< UNITS_STD_FMT::format("{:20} : {}\n", "main guns",
fmt_line<si::fps::length<si::fps::inch>, si::length<si::millimetre>>(ship.mainGuns))
<< UNITS_STD_FMT::format("{:20} : {}\n", "fire shells weighing",
fmt_line<si::fps::mass<si::fps::long_ton>, si::mass<si::kilogram>>(ship.shellMass))
<< UNITS_STD_FMT::format(
"{:20} : {}\n", "fire shells at",
fmt_line<si::fps::speed<si::fps::mile_per_hour>, si::speed<si::kilometre_per_hour>>(ship.shellSpeed))
<< UNITS_STD_FMT::format("{:20} : {}\n", "volume underwater",
fmt_line<si::volume<si::cubic_metre>, si::volume<si::litre>>(ship.mass / waterDensity));
}
int main()
{
using namespace units::isq::si::references;
using namespace units::isq::si::fps::references;
using units::isq::si::fps::references::ft; // collides with si::femtotonne (alias unit of mass)
// KMS Bismark, using the units the Germans would use, taken from Wiki
auto bismark = Ship{.length{251. * m},
.draft{9.3 * m},
.beam{36 * m},
.speed{56 * (km / h)},
.mass{50'300 * t},
.mainGuns{380 * mm},
.shellMass{800 * kg},
.shellSpeed{820. * (m / s)},
.power{110.45 * kW}};
// USS Iowa, using units from the foot-pound-second system
auto iowa = Ship{.length{860. * ft},
.draft{37. * ft + 2. * in},
.beam{108. * ft + 2. * in},
.speed{33 * units::isq::si::international::references::kn},
.mass{57'540 * lton},
.mainGuns{16 * in},
.shellMass{2700 * lb},
.shellSpeed{2690. * (ft / s)},
.power{212'000 * hp}};
// HMS King George V, using units from the foot-pound-second system
auto kgv = Ship{.length{745.1 * ft},
.draft{33. * ft + 7.5 * in},
.beam{103.2 * ft + 2.5 * in},
.speed{28.3 * units::isq::si::international::references::kn},
.mass{42'245 * lton},
.mainGuns{14 * in},
.shellMass{1'590 * lb},
.shellSpeed{2483. * (ft / s)},
.power{110'000 * hp}};
print_details("KMS Bismark, defined in appropriate units from the SI system", bismark);
std::cout << "\n\n";
print_details("USS Iowa, defined in appropriate units foot-pound-second system", iowa);
std::cout << "\n\n";
print_details("HMS King George V, defined in appropriate units foot-pound-second system", kgv);
}

View File

@@ -1,201 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "glide_computer.h"
#include <units/bits/fmt_hacks.h>
#include <units/chrono.h>
#include <units/generic/angle.h>
#include <units/generic/dimensionless.h>
#include <units/isq/si/international/length.h>
#include <array>
#include <exception>
#include <iostream>
#include <iterator>
#include <string>
#include <utility>
#include <vector>
namespace {
using namespace glide_computer;
using namespace units::isq;
auto get_gliders()
{
using namespace si::references;
UNITS_DIAGNOSTIC_PUSH
UNITS_DIAGNOSTIC_IGNORE_MISSING_BRACES
static const std::array gliders = {
glider{"SZD-30 Pirat", {velocity(83 * (km / h)), rate_of_climb(-0.7389 * (m / s))}},
glider{"SZD-51 Junior", {velocity(80 * (km / h)), rate_of_climb(-0.6349 * (m / s))}},
glider{"SZD-48 Jantar Std 3", {velocity(110 * (km / h)), rate_of_climb(-0.77355 * (m / s))}},
glider{"SZD-56 Diana", {velocity(110 * (km / h)), rate_of_climb(-0.63657 * (m / s))}}};
UNITS_DIAGNOSTIC_POP
return gliders;
}
auto get_weather_conditions()
{
using namespace si::references;
static const std::array weather_conditions = {
std::pair("Good", weather{height(1900 * m), rate_of_climb(4.3 * (m / s))}),
std::pair("Medium", weather{height(1550 * m), rate_of_climb(2.8 * (m / s))}),
std::pair("Bad", weather{height(850 * m), rate_of_climb(1.8 * (m / s))})};
return weather_conditions;
}
auto get_waypoints()
{
using namespace geographic::literals;
using namespace units::isq::si::international::references;
static const std::array waypoints = {
waypoint{"EPPR", {54.24772_N, 18.6745_E}, altitude(16 * ft)}, // N54°14'51.8" E18°40'28.2"
waypoint{"EPGI", {53.52442_N, 18.84947_E}, altitude(115 * ft)} // N53°31'27.9" E18°50'58.1"
};
return waypoints;
}
template<std::ranges::input_range R>
requires(std::same_as<std::ranges::range_value_t<R>, glider>)
void print(const R& gliders)
{
std::cout << "Gliders:\n";
std::cout << "========\n";
for (const auto& g : gliders) {
std::cout << "- Name: " << g.name << "\n";
std::cout << "- Polar:\n";
for (const auto& p : g.polar) {
const auto ratio = units::quantity_cast<units::one>(glide_ratio(g.polar[0]));
std::cout << UNITS_STD_FMT::format(" * {:%.4Q %q} @ {:%.1Q %q} -> {:%.1Q %q} ({:%.1Q %q})\n", p.climb, p.v,
ratio, units::quantity_cast<units::degree>(asin(1 / ratio)));
}
std::cout << "\n";
}
}
template<std::ranges::input_range R>
requires(std::same_as<std::ranges::range_value_t<R>, std::pair<const char*, weather>>)
void print(const R& conditions)
{
std::cout << "Weather:\n";
std::cout << "========\n";
for (const auto& c : conditions) {
std::cout << "- " << c.first << "\n";
const auto& w = c.second;
std::cout << " * Cloud base: " << UNITS_STD_FMT::format("{:%.0Q %q}", w.cloud_base) << " AGL\n";
std::cout << " * Thermals strength: " << UNITS_STD_FMT::format("{:%.1Q %q}", w.thermal_strength) << "\n";
std::cout << "\n";
}
}
template<std::ranges::input_range R>
requires(std::same_as<std::ranges::range_value_t<R>, waypoint>)
void print(const R& waypoints)
{
std::cout << "Waypoints:\n";
std::cout << "==========\n";
for (const auto& w : waypoints)
std::cout << UNITS_STD_FMT::format("- {}: {} {}, {:%.1Q %q}\n", w.name, w.pos.lat, w.pos.lon, w.alt);
std::cout << "\n";
}
void print(const task& t)
{
std::cout << "Task:\n";
std::cout << "=====\n";
std::cout << "- Start: " << t.get_start().name << "\n";
std::cout << "- Finish: " << t.get_finish().name << "\n";
std::cout << "- Length: " << UNITS_STD_FMT::format("{:%.1Q %q}", t.get_length()) << "\n";
std::cout << "- Legs: "
<< "\n";
for (const auto& l : t.get_legs())
std::cout << UNITS_STD_FMT::format(" * {} -> {} ({:%.1Q %q})\n", l.begin().name, l.end().name, l.get_length());
std::cout << "\n";
}
void print(const safety& s)
{
std::cout << "Safety:\n";
std::cout << "=======\n";
std::cout << "- Min AGL separation: " << UNITS_STD_FMT::format("{:%.0Q %q}", s.min_agl_height) << "\n";
std::cout << "\n";
}
void print(const aircraft_tow& tow)
{
std::cout << "Tow:\n";
std::cout << "====\n";
std::cout << "- Type: aircraft\n";
std::cout << "- Height: " << UNITS_STD_FMT::format("{:%.0Q %q}", tow.height_agl) << "\n";
std::cout << "- Performance: " << UNITS_STD_FMT::format("{:%.1Q %q}", tow.performance) << "\n";
std::cout << "\n";
}
void example()
{
using namespace si::references;
const safety sfty = {height(300 * m)};
const auto gliders = get_gliders();
const auto waypoints = get_waypoints();
const auto weather_conditions = get_weather_conditions();
const task t = {waypoints[0], waypoints[1], waypoints[0]};
const aircraft_tow tow = {height(400 * m), rate_of_climb(1.6 * (m / s))};
// TODO use C++20 date library when available
// set `start_time` to 11:00 am today
const timestamp start_time(std::chrono::system_clock::now());
print(sfty);
print(gliders);
print(waypoints);
print(weather_conditions);
print(t);
print(tow);
for (const auto& g : gliders) {
for (const auto& c : weather_conditions) {
std::string txt = "Scenario: Glider = " + g.name + ", Weather = " + c.first;
std::cout << txt << "\n";
std::cout << UNITS_STD_FMT::format("{0:=^{1}}\n\n", "", txt.size());
estimate(start_time, g, c.second, t, sfty, tow);
std::cout << "\n\n";
}
}
}
} // namespace
int main()
{
try {
example();
} catch (const std::exception& ex) {
std::cerr << "Unhandled std exception caught: " << ex.what() << '\n';
} catch (...) {
std::cerr << "Unhandled unknown exception caught\n";
}
}

View File

@@ -1,232 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/format.h>
#include <units/isq/si/energy.h> // IWYU pragma: keep
#include <units/isq/si/force.h>
#include <units/isq/si/length.h>
#include <units/isq/si/speed.h> // IWYU pragma: keep
#include <units/quantity_io.h>
#include <iostream>
#include <matrix>
template<typename Rep = double>
using vector = STD_LA::fixed_size_column_vector<Rep, 3>;
template<typename Rep = double>
using matrix = STD_LA::fixed_size_matrix<Rep, 3, 3>;
namespace STD_LA {
template<typename Rep>
std::ostream& operator<<(std::ostream& os, const ::vector<Rep>& v)
{
os << "|";
for (auto i = 0U; i < v.size(); ++i) {
os << UNITS_STD_FMT::format(" {:>9}", v(i));
}
os << " |";
return os;
}
template<typename Rep>
std::ostream& operator<<(std::ostream& os, const ::matrix<Rep>& v)
{
for (auto i = 0U; i < v.rows(); ++i) {
os << "|";
for (auto j = 0U; j < v.columns(); ++j) {
os << UNITS_STD_FMT::format(" {:>9}", v(i, j));
}
os << (i != v.rows() - 1U ? " |\n" : " |");
}
return os;
}
} // namespace STD_LA
namespace {
using namespace units::isq;
using namespace units::isq::si::length_references;
using namespace units::isq::si::time_references;
void vector_of_quantity_add()
{
std::cout << "\nvector_of_quantity_add:\n";
vector<si::length<si::metre>> v = {4 * m, 8 * m, 12 * m};
vector<si::length<si::metre>> u = {3 * m, 2 * m, 1 * m};
vector<si::length<si::kilometre>> t = {3 * km, 2 * km, 1 * km};
std::cout << "v = " << v << "\n";
std::cout << "u = " << u << "\n";
std::cout << "t = " << t << "\n";
std::cout << "v + u = " << v + u << "\n";
std::cout << "v + t = " << v + t << "\n";
std::cout << "t[m] = " << vector<si::length<si::metre>>(t) << "\n";
}
void vector_of_quantity_divide_by_scalar()
{
std::cout << "\nvector_of_quantity_divide_by_scalar:\n";
vector<si::length<si::metre>> v = {4 * m, 8 * m, 12 * m};
std::cout << "v = " << v << "\n";
std::cout << "v / (2 * s) = " << v / (2 * s) << "\n";
std::cout << "v / 2 = " << v / 2 << "\n";
}
void vector_of_quantity_tests()
{
vector_of_quantity_add();
vector_of_quantity_divide_by_scalar();
}
void matrix_of_quantity_add()
{
std::cout << "\nmatrix_of_quantity_add:\n";
matrix<si::length<si::metre>> v = {{1 * m, 2 * m, 3 * m}, {4 * m, 5 * m, 6 * m}, {7 * m, 8 * m, 9 * m}};
matrix<si::length<si::metre>> u = {{3 * m, 2 * m, 1 * m}, {3 * m, 2 * m, 1 * m}, {3 * m, 2 * m, 1 * m}};
matrix<si::length<si::millimetre>> t = {{3 * mm, 2 * mm, 1 * mm}, {3 * mm, 2 * mm, 1 * mm}, {3 * mm, 2 * mm, 1 * mm}};
std::cout << "v =\n" << v << "\n";
std::cout << "u =\n" << u << "\n";
std::cout << "t =\n" << t << "\n";
std::cout << "v + u =\n" << v + u << "\n";
std::cout << "v + t =\n" << v + t << "\n";
std::cout << "v[mm] =\n" << matrix<si::length<si::millimetre>>(v) << "\n";
}
void matrix_of_quantity_divide_by_scalar()
{
std::cout << "\nmatrix_of_quantity_divide_by_scalar:\n";
matrix<si::length<si::metre>> v = {{2 * m, 4 * m, 6 * m}, {4 * m, 6 * m, 8 * m}, {8 * m, 4 * m, 2 * m}};
std::cout << "v =\n" << v << "\n";
std::cout << "v / (2 * s) =\n" << v / (2 * s) << "\n";
std::cout << "v / 2 =\n" << v / 2 << "\n";
}
void matrix_of_quantity_tests()
{
matrix_of_quantity_add();
matrix_of_quantity_divide_by_scalar();
}
template<units::Unit U = si::metre, units::Representation Rep = double>
using length_v = si::length<U, vector<Rep>>;
template<units::Unit U = si::newton, units::Representation Rep = double>
using force_v = si::force<U, vector<Rep>>;
void quantity_of_vector_add()
{
std::cout << "\nquantity_of_vector_add:\n";
length_v<> v(vector<>{4, 8, 12});
length_v<> u(vector<>{3, 2, 1});
length_v<si::kilometre> t(vector<>{3, 2, 1});
std::cout << "v = " << v << "\n";
std::cout << "u = " << u << "\n";
std::cout << "t = " << t << "\n";
std::cout << "v + u = " << v + u << "\n";
std::cout << "v + t = " << v + t << "\n";
std::cout << "t[m] = " << quantity_cast<si::metre>(t) << "\n";
}
void quantity_of_vector_divide_by_scalar()
{
std::cout << "\nquantity_of_vector_divide_by_scalar:\n";
length_v<> v(vector<>{4, 8, 12});
std::cout << "v = " << v << "\n";
std::cout << "v / (2 * s) = " << v / (2 * s) << "\n";
std::cout << "v / 2 = " << v / 2 << "\n";
}
void quantity_of_vector_tests()
{
quantity_of_vector_add();
quantity_of_vector_divide_by_scalar();
}
template<units::Unit U = si::metre, units::Representation Rep = double>
using length_m = si::length<U, matrix<Rep>>;
void quantity_of_matrix_add()
{
std::cout << "\nquantity_of_matrix_add:\n";
length_m<> v(matrix<>{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
length_m<> u(matrix<>{{3, 2, 1}, {3, 2, 1}, {3, 2, 1}});
length_m<si::kilometre> t(matrix<>{{3, 2, 1}, {3, 2, 1}, {3, 2, 1}});
std::cout << "v =\n" << v << "\n";
std::cout << "u =\n" << u << "\n";
std::cout << "t =\n" << t << "\n";
std::cout << "v + u =\n" << v + u << "\n";
std::cout << "v + t =\n" << v + t << "\n";
// TODO Fix it
// std::cout << "v[mm] =\n" << matrix<si::length<si::millimetre>>(v) << "\n";
}
void quantity_of_matrix_divide_by_scalar()
{
std::cout << "\nquantity_of_matrix_divide_by_scalar:\n";
length_m<> v(matrix<>{{2, 4, 6}, {4, 6, 8}, {8, 4, 2}});
std::cout << "v =\n" << v << "\n";
std::cout << "v / (2 * s) =\n" << v / (2 * s) << "\n";
std::cout << "v / 2 =\n" << v / 2 << "\n";
}
void quantity_of_matrix_tests()
{
quantity_of_matrix_add();
quantity_of_matrix_divide_by_scalar();
}
} // namespace
int main()
{
vector_of_quantity_tests();
matrix_of_quantity_tests();
quantity_of_vector_tests();
quantity_of_matrix_tests();
}

View File

@@ -1,102 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/isq/natural/natural.h>
#include <units/isq/si/constants.h>
#include <units/isq/si/energy.h>
#include <units/isq/si/mass.h>
#include <units/isq/si/momentum.h>
#include <units/isq/si/speed.h> // IWYU pragma: keep
#include <units/math.h>
#include <units/quantity_io.h>
#include <exception>
#include <iostream>
namespace {
using namespace units::isq;
Energy auto total_energy(Momentum auto p, Mass auto m, Speed auto c)
{
return sqrt(pow<2>(p * c) + pow<2>(m * pow<2>(c)));
}
void si_example()
{
using namespace units::isq::si;
using namespace units::isq::si::energy_references;
constexpr Speed auto c = si2019::speed_of_light<>;
std::cout << "\n*** SI units (c = " << c << ") ***\n";
const Momentum auto p = 4. * GeV / c;
const Mass auto m = 3. * GeV / pow<2>(c);
const Energy auto E = total_energy(p, m, c);
std::cout << "[in GeV]\n"
<< "p = " << p << "\n"
<< "m = " << m << "\n"
<< "E = " << E << "\n";
const momentum<kilogram_metre_per_second> p_si = p;
const mass<kilogram> m_si = m;
const energy<joule> E_si = total_energy(p_si, m_si, c);
std::cout << "\n[in SI units]\n"
<< "p = " << p_si << "\n"
<< "m = " << m_si << "\n"
<< "E = " << E_si << "\n";
std::cout << "\n[converted from SI units back to GeV]\n"
<< "E = " << quantity_cast<gigaelectronvolt>(E_si) << "\n";
}
void natural_example()
{
using namespace units::isq::natural;
using namespace units::isq::natural::references;
constexpr Speed auto c = speed_of_light<>;
const auto p = 4. * momentum_references::GeV;
const auto m = 3. * mass_references::GeV;
const Energy auto E = total_energy(p, m, c);
std::cout << "\n*** Natural units (c = " << c << ") ***\n"
<< "p = " << p << "\n"
<< "m = " << m << "\n"
<< "E = " << E << "\n";
}
} // namespace
int main()
{
try {
si_example();
natural_example();
} catch (const std::exception& ex) {
std::cerr << "Unhandled std exception caught: " << ex.what() << '\n';
} catch (...) {
std::cerr << "Unhandled unknown exception caught\n";
}
}

View File

@@ -1,71 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <units/isq/si/length.h>
#include <units/isq/si/speed.h> // IWYU pragma: keep
#include <units/isq/si/time.h>
#include <units/quantity_io.h>
#include <exception>
#include <iostream>
namespace {
template<units::isq::Length D, units::isq::Time T>
constexpr units::isq::Speed auto avg_speed(D d, T t)
{
return d / t;
}
void example()
{
using namespace units::isq;
using namespace units::isq::si::references;
Length auto d1 = 123 * m;
Time auto t1 = 10 * s;
Speed auto v1 = avg_speed(d1, t1);
auto temp1 =
v1 * (50 * m); // produces intermediate unknown dimension with 'unknown_coherent_unit' as its 'coherent_unit'
Speed auto v2 = temp1 / (100 * m); // back to known dimensions again
Length auto d2 = v2 * (60 * s);
std::cout << "d1 = " << d1 << '\n';
std::cout << "t1 = " << t1 << '\n';
std::cout << "v1 = " << v1 << '\n';
std::cout << "temp1 = " << temp1 << '\n';
std::cout << "v2 = " << v2 << '\n';
std::cout << "d2 = " << d2 << '\n';
}
} // namespace
int main()
{
try {
example();
} catch (const std::exception& ex) {
std::cerr << "Unhandled std exception caught: " << ex.what() << '\n';
} catch (...) {
std::cerr << "Unhandled unknown exception caught\n";
}
}

View File

@@ -20,21 +20,37 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
#include <units/format.h> #include <mp-units/format.h>
#include <units/isq/si/constants.h> #include <mp-units/systems/si/constants.h>
#include <mp-units/systems/si/unit_symbols.h>
#include <iostream> #include <iostream>
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
int main() int main()
{ {
using namespace units::isq::si::si2019; using namespace mp_units::si;
using namespace mp_units::si::unit_symbols;
std::cout << "The seven defining constants of the SI and the seven corresponding units they define:\n"; std::cout << "The seven defining constants of the SI and the seven corresponding units they define:\n";
std::cout << UNITS_STD_FMT::format("- hyperfine transition frequency of Cs: {:%.0Q %q}\n", std::cout << UNITS_STD_FMT::format("- hyperfine transition frequency of Cs: {} = {:%.0Q %q}\n",
hyperfine_structure_transition_frequency<>); 1. * si2019::hyperfine_structure_transition_frequency_of_cs,
std::cout << UNITS_STD_FMT::format("- speed of light in vacuum: {:%.0Q %q}\n", speed_of_light<>); (1. * si2019::hyperfine_structure_transition_frequency_of_cs)[Hz]);
std::cout << UNITS_STD_FMT::format("- Planck constant: {}\n", planck_constant<>); std::cout << UNITS_STD_FMT::format("- speed of light in vacuum: {} = {:%.0Q %q}\n",
std::cout << UNITS_STD_FMT::format("- elementary charge: {}\n", elementary_charge<>); 1. * si2019::speed_of_light_in_vacuum,
std::cout << UNITS_STD_FMT::format("- Boltzmann constant: {}\n", boltzmann_constant<>); (1. * si2019::speed_of_light_in_vacuum)[m / s]);
std::cout << UNITS_STD_FMT::format("- Avogadro constant: {}\n", avogadro_constant<>); std::cout << UNITS_STD_FMT::format("- Planck constant: {} = {:%.8eQ %q}\n",
std::cout << UNITS_STD_FMT::format("- luminous efficacy: {}\n", luminous_efficacy<>); 1. * si2019::planck_constant, (1. * si2019::planck_constant)[J * s]);
std::cout << UNITS_STD_FMT::format("- elementary charge: {} = {:%.9eQ %q}\n",
1. * si2019::elementary_charge, (1. * si2019::elementary_charge)[C]);
std::cout << UNITS_STD_FMT::format("- Boltzmann constant: {} = {:%.6eQ %q}\n",
1. * si2019::boltzmann_constant, (1. * si2019::boltzmann_constant)[J / K]);
std::cout << UNITS_STD_FMT::format("- Avogadro constant: {} = {:%.8eQ %q}\n",
1. * si2019::avogadro_constant, (1. * si2019::avogadro_constant)[1 / mol]);
// TODO uncomment the below when ISQ is done
// std::cout << UNITS_STD_FMT::format("- luminous efficacy: {} = {}\n",
// si2019::luminous_efficacy(1.),
// si2019::luminous_efficacy(1.)[lm / W]);
} }

128
example/storage_tank.cpp Normal file
View File

@@ -0,0 +1,128 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <mp-units/format.h>
#include <mp-units/systems/isq/mechanics.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si/constants.h>
#include <mp-units/systems/si/unit_symbols.h>
#include <mp-units/systems/si/units.h>
#include <cassert>
#include <iostream>
#include <utility>
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
namespace {
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
inline constexpr auto g = 1 * si::standard_gravity;
inline constexpr auto air_density = isq::mass_density(1.225 * (kg / m3));
class StorageTank {
quantity<isq::area[m2]> base_;
quantity<isq::height[m]> height_;
quantity<isq::mass_density[kg / m3]> density_ = air_density;
public:
constexpr StorageTank(const quantity<isq::area[m2]>& base, const quantity<isq::height[m]>& height) :
base_(base), height_(height)
{
}
constexpr void set_contents_density(const quantity<isq::mass_density[kg / m3]>& density)
{
assert(density > air_density);
density_ = density;
}
[[nodiscard]] constexpr QuantityOf<isq::weight> auto filled_weight() const
{
const auto volume = isq::volume(base_ * height_); // TODO check if we can remove that cast
const QuantityOf<isq::mass> auto mass = density_ * volume;
return isq::weight(mass * g);
}
[[nodiscard]] constexpr quantity<isq::height[m]> fill_level(const quantity<isq::mass[kg]>& measured_mass) const
{
return height_ * measured_mass * g / filled_weight();
}
[[nodiscard]] constexpr quantity<isq::volume[m3]> spare_capacity(const quantity<isq::mass[kg]>& measured_mass) const
{
return (height_ - fill_level(measured_mass)) * base_;
}
};
class CylindricalStorageTank : public StorageTank {
public:
constexpr CylindricalStorageTank(const quantity<isq::radius[m]>& radius, const quantity<isq::height[m]>& height) :
StorageTank(std::numbers::pi * radius * radius, height)
{
}
};
class RectangularStorageTank : public StorageTank {
public:
constexpr RectangularStorageTank(const quantity<isq::length[m]>& length, const quantity<isq::width[m]>& width,
const quantity<isq::height[m]>& height) :
StorageTank(length * width, height)
{
}
};
} // namespace
int main()
{
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
const auto height = isq::height(200 * mm);
auto tank = RectangularStorageTank(isq::length(1'000 * mm), isq::width(500 * mm), height);
tank.set_contents_density(1'000 * isq::mass_density[kg / m3]);
const auto fill_time = 200 * s; // time since starting fill
const auto measured_mass = 20. * kg; // measured mass at fill_time
const auto fill_level = tank.fill_level(measured_mass);
const auto spare_capacity = tank.spare_capacity(measured_mass);
const auto filled_weight = tank.filled_weight();
const QuantityOf<isq::mass_change_rate> auto input_flow_rate = measured_mass / fill_time;
const QuantityOf<isq::speed> auto float_rise_rate = fill_level / fill_time;
const QuantityOf<isq::time> auto fill_time_left = (height / fill_level - 1) * fill_time;
const auto fill_ratio = fill_level / height;
std::cout << UNITS_STD_FMT::format("fill height at {} = {} ({} full)\n", fill_time, fill_level, fill_ratio[percent]);
std::cout << UNITS_STD_FMT::format("fill weight at {} = {} ({})\n", fill_time, filled_weight, filled_weight[N]);
std::cout << UNITS_STD_FMT::format("spare capacity at {} = {}\n", fill_time, spare_capacity);
std::cout << UNITS_STD_FMT::format("input flow rate = {}\n", input_flow_rate);
std::cout << UNITS_STD_FMT::format("float rise rate = {}\n", float_rise_rate);
std::cout << UNITS_STD_FMT::format("tank full E.T.A. at current flow rate = {}\n", fill_time_left[s]);
}

View File

@@ -20,31 +20,29 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
#include <units/generic/angle.h> #include <mp-units/iostream.h>
#include <mp-units/math.h>
UNITS_DIAGNOSTIC_PUSH #include <mp-units/systems/isq_angle/isq_angle.h>
UNITS_DIAGNOSTIC_IGNORE_SHADOW #include <mp-units/systems/si/unit_symbols.h>
#include <units/isq/si/force.h> // 'N' (Newton) shadows a template parameter traditionally used as a size of the array
UNITS_DIAGNOSTIC_POP
#include <units/isq/si/length.h>
#include <units/isq/si/torque.h>
#include <units/math.h>
#include <units/quantity_io.h>
#include <iostream> #include <iostream>
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
int main() int main()
{ {
using namespace units; using namespace mp_units;
using namespace units::isq; using namespace mp_units::si::unit_symbols;
using namespace units::isq::si::references; using namespace mp_units::angular::unit_symbols;
using namespace units::references; using mp_units::angular::unit_symbols::deg;
using mp_units::angular::unit_symbols::rad;
const Length auto lever = 20 * cm; const auto lever = isq_angle::position_vector(20 * cm);
const Force auto force = 500 * N; const auto force = isq_angle::force(500 * N);
const Angle auto angle = 90. * deg; const auto angle = isq_angle::angular_measure(90. * deg);
const Torque auto torque = lever * force * sin(angle) / cotes_angle<>; const auto torque = isq_angle::torque(lever * force * angular::sin(angle) / (1 * isq_angle::cotes_angle));
std::cout << "Applying a perpendicular force of " << force << " to a " << lever << " long lever results in " std::cout << "Applying a perpendicular force of " << force << " to a " << lever << " long lever results in "
<< quantity_cast<si::newton_metre_per_radian>(torque) << " of torque.\n"; << torque[N * m / rad] << " of torque.\n";
} }

114
example/total_energy.cpp Normal file
View File

@@ -0,0 +1,114 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <mp-units/iostream.h>
#include <mp-units/math.h>
#include <mp-units/systems/isq/mechanics.h>
#include <mp-units/systems/natural/natural.h>
#include <mp-units/systems/si/constants.h>
#include <mp-units/systems/si/unit_symbols.h>
#include <exception>
#include <iostream>
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
namespace {
using namespace mp_units;
QuantityOf<isq::mechanical_energy> auto total_energy(QuantityOf<isq::momentum> auto p, QuantityOf<isq::mass> auto m,
QuantityOf<isq::speed> auto c)
{
return isq::mechanical_energy(sqrt(pow<2>(p * c) + pow<2>(m * pow<2>(c))));
}
void si_example()
{
using namespace mp_units::si::unit_symbols;
constexpr auto GeV = si::giga<si::electronvolt>;
constexpr QuantityOf<isq::speed> auto c = 1. * si::si2019::speed_of_light_in_vacuum;
auto c2 = pow<2>(c);
const auto p1 = isq::momentum(4. * GeV / c);
const QuantityOf<isq::mass> auto m1 = 3. * GeV / c2;
const auto E = total_energy(p1, m1, c);
std::cout << "\n*** SI units (c = " << c << " = " << c[si::metre / s] << ") ***\n";
std::cout << "\n[in `GeV` and `c`]\n"
<< "p = " << p1 << "\n"
<< "m = " << m1 << "\n"
<< "E = " << E << "\n";
const auto p2 = p1[GeV / (m / s)];
const auto m2 = m1[GeV / pow<2>(m / s)];
const auto E2 = total_energy(p2, m2, c)[GeV];
std::cout << "\n[in `GeV`]\n"
<< "p = " << p2 << "\n"
<< "m = " << m2 << "\n"
<< "E = " << E2 << "\n";
const auto p3 = p1[kg * m / s];
const auto m3 = m1[kg];
const auto E3 = total_energy(p3, m3, c)[J];
std::cout << "\n[in SI base units]\n"
<< "p = " << p3 << "\n"
<< "m = " << m3 << "\n"
<< "E = " << E3 << "\n";
std::cout << "\n[converted from SI units back to GeV]\n"
<< "E = " << value_cast<GeV>(E3) << "\n";
}
void natural_example()
{
using namespace mp_units::natural;
using namespace mp_units::natural::unit_symbols;
constexpr auto c = 1. * speed_of_light;
const auto p = 4. * momentum[GeV];
const auto m = 3. * mass[GeV];
const auto E = total_energy(p, m, c);
std::cout << "\n*** Natural units (c = " << c << ") ***\n"
<< "p = " << p << "\n"
<< "m = " << m << "\n"
<< "E = " << E << "\n";
}
} // namespace
int main()
{
try {
si_example();
natural_example();
} catch (const std::exception& ex) {
std::cerr << "Unhandled std exception caught: " << ex.what() << '\n';
} catch (...) {
std::cerr << "Unhandled unknown exception caught\n";
}
}

View File

@@ -0,0 +1,167 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "geographic.h"
#include <mp-units/iostream.h>
#include <mp-units/quantity_point.h>
#include <mp-units/systems/international/international.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si/unit_symbols.h>
#include <iostream>
using namespace mp_units;
using namespace geographic;
// **** HAE ****
enum class earth_gravity_model { egm84_15, egm95_5, egm2008_1 };
template<earth_gravity_model M>
struct height_above_ellipsoid_t : absolute_point_origin<isq::altitude> {
static constexpr earth_gravity_model egm = M;
using absolute_point_origin<isq::altitude>::absolute_point_origin;
};
template<earth_gravity_model M>
inline constexpr height_above_ellipsoid_t<M> height_above_ellipsoid;
template<earth_gravity_model M>
using hae_altitude = quantity_point<isq::altitude[si::metre], height_above_ellipsoid<M>>;
constexpr const char* to_text(earth_gravity_model m)
{
switch (m) {
using enum earth_gravity_model;
case egm84_15:
return "EGM84-15";
case egm95_5:
return "EGM95-5";
case egm2008_1:
return "EGM2008-1";
}
}
template<earth_gravity_model M>
[[nodiscard]] consteval bool is_hae(height_above_ellipsoid_t<M>)
{
return true;
}
[[nodiscard]] consteval bool is_hae(...) { return false; }
template<class CharT, class Traits, QuantityPoint QP>
requires(is_hae(QP::absolute_point_origin))
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const QP& a)
{
return os << a.absolute() << " HAE(" << to_text(a.absolute_point_origin.egm) << ")";
}
template<QuantityPoint QP>
requires(is_hae(QP::absolute_point_origin))
struct UNITS_STD_FMT::formatter<QP> : formatter<typename QP::quantity_type> {
template<typename FormatContext>
auto format(const QP& a, FormatContext& ctx)
{
formatter<typename QP::quantity_type>::format(a.absolute(), ctx);
return UNITS_STD_FMT::format_to(ctx.out(), " HAE({})", to_text(QP::absolute_point_origin.egm));
}
};
double GeographicLibWhatsMyOffset(long double /* lat */, long double /* lon */)
{
// for example use GeographicLib for that:
// - https://geographiclib.sourceforge.io/C++/doc/geoid.html
// - https://conan.io/center/geographiclib
return 29.49;
}
template<earth_gravity_model M>
hae_altitude<M> to_hae(msl_altitude msl, position<long double> pos)
{
const auto geoid_undulation =
isq::height(GeographicLibWhatsMyOffset(pos.lat.number_in(si::degree), pos.lon.number_in(si::degree)) * si::metre);
return hae_altitude<M>{msl.absolute() - geoid_undulation};
}
// **** HAL ****
// clang-format off
inline constexpr struct height_above_launch : absolute_point_origin<isq::altitude> {} height_above_launch;
// clang-format on
using hal_altitude = quantity_point<isq::altitude[si::metre], height_above_launch>;
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const hal_altitude& a)
{
return os << a.absolute() << " HAL";
}
template<>
struct UNITS_STD_FMT::formatter<hal_altitude> : formatter<hal_altitude::quantity_type> {
template<typename FormatContext>
auto format(const hal_altitude& a, FormatContext& ctx)
{
formatter<hal_altitude::quantity_type>::format(a.absolute(), ctx);
return UNITS_STD_FMT::format_to(ctx.out(), " HAL");
}
};
// **** UAV ****
class unmanned_aerial_vehicle {
msl_altitude current_{0 * si::metre};
msl_altitude launch_ = current_;
public:
void take_off(msl_altitude alt) { launch_ = alt; }
msl_altitude take_off() const { return launch_; }
void current(msl_altitude alt) { current_ = alt; }
msl_altitude current() const { return current_; }
hal_altitude hal() const { return hal_altitude{current_ - launch_}; }
};
int main()
{
using namespace mp_units::si::unit_symbols;
using namespace mp_units::international::unit_symbols;
unmanned_aerial_vehicle uav;
uav.take_off(msl_altitude{6'000 * ft});
uav.current(msl_altitude{10'000 * ft});
std::cout << UNITS_STD_FMT::format("hal = {}\n", uav.hal());
msl_altitude ground_level{123 * m};
std::cout << UNITS_STD_FMT::format("agl = {}\n", uav.current() - ground_level);
struct waypoint {
std::string name;
geographic::position<long double> pos;
msl_altitude msl_alt;
};
waypoint wpt = {"EPPR", {54.24772_N, 18.6745_E}, msl_altitude{16. * ft}};
std::cout << UNITS_STD_FMT::format("{}: {} {}, {:%.2Q %q}, {:%.2Q %q}\n", wpt.name, wpt.pos.lat, wpt.pos.lon,
wpt.msl_alt, to_hae<earth_gravity_model::egm2008_1>(wpt.msl_alt, wpt.pos));
}

View File

@@ -21,7 +21,7 @@
# SOFTWARE. # SOFTWARE.
cmake_minimum_required(VERSION 3.19) cmake_minimum_required(VERSION 3.19)
project(mp-units VERSION 0.8.0 LANGUAGES CXX) project(mp-units VERSION 2.0.0 LANGUAGES CXX)
set(projectPrefix UNITS_) set(projectPrefix UNITS_)
@@ -44,10 +44,13 @@ add_subdirectory(core)
add_subdirectory(core-fmt) add_subdirectory(core-fmt)
add_subdirectory(core-io) add_subdirectory(core-io)
add_subdirectory(systems) add_subdirectory(systems)
add_subdirectory(utility)
# project-wide wrapper # project-wide wrapper
add_library(mp-units INTERFACE) add_library(mp-units INTERFACE)
target_link_libraries(mp-units INTERFACE mp-units::core mp-units::core-io mp-units::core-fmt mp-units::systems) target_link_libraries(
mp-units INTERFACE mp-units::core mp-units::core-io mp-units::core-fmt mp-units::systems mp-units::utility
)
add_library(mp-units::mp-units ALIAS mp-units) add_library(mp-units::mp-units ALIAS mp-units)
install(TARGETS mp-units EXPORT mp-unitsTargets) install(TARGETS mp-units EXPORT mp-unitsTargets)

View File

@@ -65,5 +65,5 @@ function(add_units_module name)
add_library(mp-units::${name} ALIAS mp-units-${name}) add_library(mp-units::${name} ALIAS mp-units-${name})
install(TARGETS mp-units-${name} EXPORT mp-unitsTargets) install(TARGETS mp-units-${name} EXPORT mp-unitsTargets)
install(DIRECTORY include/units TYPE INCLUDE) install(DIRECTORY include/mp-units TYPE INCLUDE)
endfunction() endfunction()

View File

@@ -25,7 +25,7 @@ cmake_minimum_required(VERSION 3.19)
option(${projectPrefix}USE_LIBFMT "Enables usage of libfmt instead of the one from 'std'" ON) option(${projectPrefix}USE_LIBFMT "Enables usage of libfmt instead of the one from 'std'" ON)
message(STATUS "${projectPrefix}USE_LIBFMT: ${${projectPrefix}USE_LIBFMT}") message(STATUS "${projectPrefix}USE_LIBFMT: ${${projectPrefix}USE_LIBFMT}")
add_units_module(core-fmt DEPENDENCIES mp-units::core HEADERS include/units/format.h) add_units_module(core-fmt DEPENDENCIES mp-units::core HEADERS include/mp-units/format.h)
target_compile_definitions(mp-units-core-fmt INTERFACE ${projectPrefix}USE_LIBFMT=$<BOOL:${${projectPrefix}USE_LIBFMT}>) target_compile_definitions(mp-units-core-fmt INTERFACE ${projectPrefix}USE_LIBFMT=$<BOOL:${${projectPrefix}USE_LIBFMT}>)
if(${projectPrefix}USE_LIBFMT) if(${projectPrefix}USE_LIBFMT)

View File

@@ -28,14 +28,14 @@
// For the license information refer to format.h. // For the license information refer to format.h.
#include <gsl/gsl-lite.hpp> #include <gsl/gsl-lite.hpp>
#include <units/bits/fmt_hacks.h> #include <mp-units/bits/fmt_hacks.h>
#include <concepts> #include <concepts>
#include <limits> #include <limits>
#include <string_view> #include <string_view>
// most of the below code is based on/copied from libfmt // most of the below code is based on/copied from libfmt
namespace units::detail { namespace mp_units::detail {
struct auto_id {}; struct auto_id {};
@@ -382,11 +382,11 @@ template<std::input_iterator It, std::sentinel_for<It> S, typename SpecHandler>
if (begin == end) return begin; if (begin == end) return begin;
begin = ::units::detail::parse_align(begin, end, handler); begin = ::mp_units::detail::parse_align(begin, end, handler);
if (begin == end) return begin; if (begin == end) return begin;
// Parse sign. // Parse sign.
begin = ::units::detail::parse_sign(begin, end, handler); begin = ::mp_units::detail::parse_sign(begin, end, handler);
if (begin == end) return begin; if (begin == end) return begin;
if (*begin == '#') { if (*begin == '#') {
@@ -400,12 +400,12 @@ template<std::input_iterator It, std::sentinel_for<It> S, typename SpecHandler>
if (++begin == end) return begin; if (++begin == end) return begin;
} }
begin = ::units::detail::parse_width(begin, end, handler); begin = ::mp_units::detail::parse_width(begin, end, handler);
if (begin == end) return begin; if (begin == end) return begin;
// Parse precision. // Parse precision.
if (*begin == '.') { if (*begin == '.') {
begin = ::units::detail::parse_precision(begin, end, handler); begin = ::mp_units::detail::parse_precision(begin, end, handler);
if (begin == end) return begin; if (begin == end) return begin;
} }
@@ -465,4 +465,4 @@ private:
ParseContext& context_; ParseContext& context_;
}; };
} // namespace units::detail } // namespace mp_units::detail

View File

@@ -27,7 +27,7 @@
// //
// For the license information refer to format.h. // For the license information refer to format.h.
#include <units/bits/external/hacks.h> #include <mp-units/bits/external/hacks.h>
#ifndef UNITS_USE_LIBFMT #ifndef UNITS_USE_LIBFMT
#define UNITS_USE_LIBFMT 1 #define UNITS_USE_LIBFMT 1

View File

@@ -22,16 +22,13 @@
#pragma once #pragma once
#include <units/bits/fmt.h> #include <mp-units/bits/algorithm.h>
#include <units/customization_points.h> #include <mp-units/bits/fmt.h>
#include <units/quantity.h> #include <mp-units/customization_points.h>
#include <algorithm> #include <mp-units/quantity.h>
#include <mp-units/unit.h>
#include <cstdint> #include <cstdint>
// IWYU pragma: begin_exports
#include <units/bits/unit_text.h>
// IWYU pragma: end_exports
// Grammar // Grammar
// //
// units-format-spec ::= [fill-and-align] [width] [units-specs] // units-format-spec ::= [fill-and-align] [width] [units-specs]
@@ -42,39 +39,14 @@
// conversion-spec ::= '%' units-type // conversion-spec ::= '%' units-type
// units-type ::= [units-rep-modifier] 'Q' // units-type ::= [units-rep-modifier] 'Q'
// [units-unit-modifier] 'q' // [units-unit-modifier] 'q'
// one of "nt%"
// units-rep-modifier ::= [sign] [#] [precision] [L] [units-rep-type] // units-rep-modifier ::= [sign] [#] [precision] [L] [units-rep-type]
// units-rep-type ::= one of "aAbBdeEfFgGoxX" // units-rep-type ::= one of "aAbBdeEfFgGoxX"
// units-unit-modifier ::= 'A' // units-unit-modifier ::= [units-text-encoding, units-unit-symbol-denominator, units-unit-symbol-separator]
// units-text-encoding ::= one of "UA"
// units-unit-symbol-solidus ::= one of "oan"
// units-unit-symbol-separator ::= one of "sd"
// Guide for editing namespace mp_units::detail {
//
// If you want to add a new `units-type` terminal character (e.g. 'Q', 'q'):
// - If needed, write a new `specs` class (e.g. `global_format_specs`)
// - Add the new symbol in the `units_types` variable in the `parse_units_format` function
// - Add a new case in the `if` following the format_error in `parse_units_format` function;
// this should invoke `handler.on_[...]`
// - Edit `UNITS_STD_FMT::formatter`:
// - Add a new field for the flag/specs
// - Add to the `UNITS_STD_FMT::formatter::spec_handler` a `on_[...]` function that set the flag/specs if needed
// - Edit `quantity_formatter`:
// - Add a new field for the flag/specs
// - write a `on_[...]` function that writes to the `out` iterator the correct output
//
// If you want to add a new `units-rep-type`:
// - Add the new symbol in the `valid_rep_types` variable (which is in the
// UNITS_STD_FMT::formatter::spec_handler::on_type member function)
// NB: currently this function forward the modifier to the value that must be formatted;
// if the symbol has no meaning for UNITS_STD_FMT::formatter<Rep>, this behavior should be disabled manually
// (as is done for '\0')
// - Implement the effect of the new flag in `format_units_quantity_value`
//
// If you want to add a new `units-unit-modifier`:
// - Add the new symbol in the `valid_modifiers` variable (which is in the
// UNITS_STD_FMT::formatter::spec_handler::on_unit_modifier member function)
// - Implement the effect of the new flag in the `quantity_formatter::on_quantity_unit` member function
namespace units::detail {
// Holds specs about the whole object // Holds specs about the whole object
template<typename CharT> template<typename CharT>
@@ -96,9 +68,7 @@ struct quantity_rep_format_specs {
}; };
// Holds specs about the unit (%[specs]q) // Holds specs about the unit (%[specs]q)
struct quantity_unit_format_specs { struct quantity_unit_format_specs : unit_symbol_formatting {};
bool ascii_only = false;
};
template<typename CharT> template<typename CharT>
struct quantity_format_specs { struct quantity_format_specs {
@@ -109,7 +79,7 @@ struct quantity_format_specs {
// Parse a `units-rep-modifier` // Parse a `units-rep-modifier`
template<std::input_iterator It, std::sentinel_for<It> S, typename Handler> template<std::input_iterator It, std::sentinel_for<It> S, typename Handler>
constexpr const It parse_units_rep(It begin, S end, Handler&& handler, bool treat_as_floating_point) constexpr It parse_units_rep(It begin, S end, Handler&& handler, bool treat_as_floating_point)
{ {
// parse sign // parse sign
begin = parse_sign(begin, end, handler); begin = parse_sign(begin, end, handler);
@@ -126,7 +96,7 @@ constexpr const It parse_units_rep(It begin, S end, Handler&& handler, bool trea
if (treat_as_floating_point) { if (treat_as_floating_point) {
begin = parse_precision(begin, end, handler); begin = parse_precision(begin, end, handler);
} else } else
UNITS_THROW(UNITS_STD_FMT::format_error("precision not allowed for integral quantity representation")); throw UNITS_STD_FMT::format_error("precision not allowed for integral quantity representation");
if (begin == end) return begin; if (begin == end) return begin;
} }
@@ -156,35 +126,18 @@ constexpr It parse_units_format(It begin, S end, Handler&& handler)
} }
if (begin != ptr) handler.on_text(begin, ptr); if (begin != ptr) handler.on_text(begin, ptr);
begin = ++ptr; // consume '%' begin = ++ptr; // consume '%'
if (ptr == end) UNITS_THROW(UNITS_STD_FMT::format_error("invalid format")); if (ptr == end) throw UNITS_STD_FMT::format_error("invalid format");
c = *ptr++; c = *ptr++;
switch (c) { constexpr auto units_types = std::string_view{"Qq"};
// units-type const auto new_end = find_first_of(begin, end, units_types.begin(), units_types.end());
case '%': if (new_end == end) throw UNITS_STD_FMT::format_error("invalid format");
handler.on_text(ptr - 1, ptr); if (*new_end == 'Q') {
break; handler.on_quantity_value(begin, new_end); // Edit `on_quantity_value` to add rep modifiers
case 'n': { } else {
const char newline[] = "\n"; handler.on_quantity_unit(begin, new_end); // Edit `on_quantity_unit` to add an unit modifier
handler.on_text(newline, newline + 1);
break;
}
case 't': {
const char tab[] = "\t";
handler.on_text(tab, tab + 1);
break;
}
default:
constexpr auto units_types = std::string_view{"Qq"};
const auto new_end = std::find_first_of(begin, end, units_types.begin(), units_types.end());
if (new_end == end) UNITS_THROW(UNITS_STD_FMT::format_error("invalid format"));
if (*new_end == 'Q') {
handler.on_quantity_value(begin, new_end); // Edit `on_quantity_value` to add rep modifiers
} else {
handler.on_quantity_unit(*begin); // Edit `on_quantity_unit` to add an unit modifier
}
ptr = new_end + 1;
} }
ptr = new_end + 1;
begin = ptr; begin = ptr;
} }
if (begin != ptr) handler.on_text(begin, ptr); if (begin != ptr) handler.on_text(begin, ptr);
@@ -264,14 +217,14 @@ OutputIt format_global_buffer(OutputIt out, const quantity_global_format_specs<C
return UNITS_STD_FMT::format_to(out, "}}"); return UNITS_STD_FMT::format_to(out, "}}");
} }
template<typename Dimension, typename Unit, typename Rep, typename Locale, typename CharT, typename OutputIt> template<auto Reference, typename Rep, typename Locale, typename CharT, typename OutputIt>
struct quantity_formatter { struct quantity_formatter {
OutputIt out; OutputIt out;
Rep val; Rep val;
const quantity_format_specs<CharT>& specs; const quantity_format_specs<CharT>& specs;
Locale loc; Locale loc;
explicit quantity_formatter(OutputIt o, quantity<Dimension, Unit, Rep> q, const quantity_format_specs<CharT>& fspecs, explicit quantity_formatter(OutputIt o, quantity<Reference, Rep> q, const quantity_format_specs<CharT>& fspecs,
Locale lc) : Locale lc) :
out(o), val(std::move(q).number()), specs(fspecs), loc(std::move(lc)) out(o), val(std::move(q).number()), specs(fspecs), loc(std::move(lc))
{ {
@@ -289,28 +242,34 @@ struct quantity_formatter {
out = format_units_quantity_value<CharT>(out, val, specs.rep, loc); out = format_units_quantity_value<CharT>(out, val, specs.rep, loc);
} }
void on_quantity_unit([[maybe_unused]] CharT) template<std::input_iterator It, std::sentinel_for<It> S>
void on_quantity_unit(It, S)
{ {
auto txt = unit_text<Dimension, Unit>(); out = unit_symbol_to<CharT>(out, get_unit(Reference), specs.unit);
if (specs.unit.ascii_only) {
UNITS_STD_FMT::format_to(out, "{}", txt.ascii().c_str());
} else {
UNITS_STD_FMT::format_to(out, "{}", txt.standard().c_str());
}
} }
}; };
} // namespace units::detail template<std::input_iterator It, std::sentinel_for<It> S>
[[nodiscard]] constexpr It at_most_one_of(It begin, S end, std::string_view modifiers)
{
auto it = find_first_of(begin, end, modifiers.begin(), modifiers.end());
if (it != end && find_first_of(it + 1, end, modifiers.begin(), modifiers.end()) != end)
throw UNITS_STD_FMT::format_error("only one of '" + std::string(modifiers) +
"' unit modifiers may be used in the format spec");
return it;
}
template<typename Dimension, typename Unit, typename Rep, typename CharT> } // namespace mp_units::detail
struct UNITS_STD_FMT::formatter<units::quantity<Dimension, Unit, Rep>, CharT> {
template<auto Reference, typename Rep, typename CharT>
struct UNITS_STD_FMT::formatter<mp_units::quantity<Reference, Rep>, CharT> {
private: private:
using quantity = units::quantity<Dimension, Unit, Rep>; using quantity = mp_units::quantity<Reference, Rep>;
using iterator = TYPENAME UNITS_STD_FMT::basic_format_parse_context<CharT>::iterator; using iterator = TYPENAME UNITS_STD_FMT::basic_format_parse_context<CharT>::iterator;
bool quantity_value = false; bool quantity_value = false;
bool quantity_unit = false; bool quantity_unit = false;
units::detail::quantity_format_specs<CharT> specs; mp_units::detail::quantity_format_specs<CharT> specs;
std::basic_string_view<CharT> format_str; std::basic_string_view<CharT> format_str;
struct spec_handler { struct spec_handler {
@@ -318,9 +277,9 @@ private:
UNITS_STD_FMT::basic_format_parse_context<CharT>& context; UNITS_STD_FMT::basic_format_parse_context<CharT>& context;
constexpr void on_fill(std::basic_string_view<CharT> fill) { f.specs.global.fill = fill; } constexpr void on_fill(std::basic_string_view<CharT> fill) { f.specs.global.fill = fill; }
constexpr void on_align(units::detail::fmt_align align) { f.specs.global.align = align; } constexpr void on_align(mp_units::detail::fmt_align align) { f.specs.global.align = align; }
constexpr void on_width(int width) { f.specs.global.width = width; } constexpr void on_width(int width) { f.specs.global.width = width; }
constexpr void on_sign(units::detail::fmt_sign sign) { f.specs.rep.sign = sign; } constexpr void on_sign(mp_units::detail::fmt_sign sign) { f.specs.rep.sign = sign; }
constexpr void on_hash() { f.specs.rep.alt = true; } constexpr void on_hash() { f.specs.rep.alt = true; }
constexpr void on_precision(int precision) { f.specs.rep.precision = precision; } constexpr void on_precision(int precision) { f.specs.rep.precision = precision; }
constexpr void on_localized() { f.specs.rep.localized = true; } constexpr void on_localized() { f.specs.rep.localized = true; }
@@ -331,30 +290,20 @@ private:
if (valid_rep_types.find(type) != std::string_view::npos) { if (valid_rep_types.find(type) != std::string_view::npos) {
f.specs.rep.type = type; f.specs.rep.type = type;
} else { } else {
UNITS_THROW(UNITS_STD_FMT::format_error("invalid quantity type specifier")); throw UNITS_STD_FMT::format_error("invalid quantity type specifier");
}
}
constexpr void on_unit_modifier(char mod)
{
constexpr auto valid_modifiers = std::string_view{"A"};
if (valid_modifiers.find(mod) != std::string_view::npos) {
f.specs.unit.ascii_only = true;
} else {
UNITS_THROW(UNITS_STD_FMT::format_error("invalid unit modifier specified"));
} }
} }
template<typename T> template<typename T>
constexpr void on_dynamic_width(T t) constexpr void on_dynamic_width(T t)
{ {
f.specs.global.dynamic_width_index = units::detail::on_dynamic_arg(t, context); f.specs.global.dynamic_width_index = mp_units::detail::on_dynamic_arg(t, context);
} }
template<typename T> template<typename T>
constexpr void on_dynamic_precision(T t) constexpr void on_dynamic_precision(T t)
{ {
f.specs.rep.dynamic_precision_index = units::detail::on_dynamic_arg(t, context); f.specs.rep.dynamic_precision_index = mp_units::detail::on_dynamic_arg(t, context);
} }
template<std::input_iterator It, std::sentinel_for<It> S> template<std::input_iterator It, std::sentinel_for<It> S>
@@ -365,13 +314,47 @@ private:
template<std::input_iterator It, std::sentinel_for<It> S> template<std::input_iterator It, std::sentinel_for<It> S>
constexpr void on_quantity_value(It begin, S end) constexpr void on_quantity_value(It begin, S end)
{ {
if (begin != end) units::detail::parse_units_rep(begin, end, *this, units::treat_as_floating_point<Rep>); if (begin != end) mp_units::detail::parse_units_rep(begin, end, *this, mp_units::treat_as_floating_point<Rep>);
f.quantity_value = true; f.quantity_value = true;
} }
constexpr void on_quantity_unit(CharT mod) template<std::input_iterator It, std::sentinel_for<It> S>
constexpr void on_quantity_unit(It begin, S end)
{ {
if (mod != 'q') on_unit_modifier(mod); if (begin == end) return;
constexpr auto valid_modifiers = std::string_view{"UAoansd"};
for (auto it = begin; it != end; ++it) {
if (valid_modifiers.find(*it) == std::string_view::npos)
throw UNITS_STD_FMT::format_error("invalid unit modifier specified");
}
if (auto it = mp_units::detail::at_most_one_of(begin, end, "UA"); it != end) {
if (*it == 'U')
f.specs.unit.encoding = mp_units::text_encoding::unicode;
else
f.specs.unit.encoding = mp_units::text_encoding::ascii;
}
if (auto it = mp_units::detail::at_most_one_of(begin, end, "oan"); it != end) {
if (*it == 'o')
f.specs.unit.solidus = mp_units::unit_symbol_solidus::one_denominator;
else if (*it == 'a')
f.specs.unit.solidus = mp_units::unit_symbol_solidus::always;
else
f.specs.unit.solidus = mp_units::unit_symbol_solidus::never;
}
if (auto it = mp_units::detail::at_most_one_of(begin, end, "sd"); it != end) {
if (*it == 's')
f.specs.unit.separator = mp_units::unit_symbol_separator::space;
else {
if (f.specs.unit.encoding == mp_units::text_encoding::ascii)
throw UNITS_STD_FMT::format_error("dot unit separator allowed only for Unicode encoding");
f.specs.unit.separator = mp_units::unit_symbol_separator::dot;
}
}
f.quantity_unit = true; f.quantity_unit = true;
} }
}; };
@@ -387,19 +370,19 @@ private:
spec_handler handler{*this, ctx}; spec_handler handler{*this, ctx};
// parse alignment // parse alignment
begin = units::detail::parse_align(begin, end, handler); begin = mp_units::detail::parse_align(begin, end, handler);
if (begin == end) return {begin, begin}; if (begin == end) return {begin, begin};
// parse width // parse width
begin = units::detail::parse_width(begin, end, handler); begin = mp_units::detail::parse_width(begin, end, handler);
if (begin == end) return {begin, begin}; if (begin == end) return {begin, begin};
// parse units-specific specification // parse units-specific specification
end = units::detail::parse_units_format(begin, end, handler); end = mp_units::detail::parse_units_format(begin, end, handler);
if (specs.global.align == units::detail::fmt_align::none && (!quantity_unit || quantity_value)) if (specs.global.align == mp_units::detail::fmt_align::none && (!quantity_unit || quantity_value))
// quantity values should behave like numbers (by default aligned to right) // quantity values should behave like numbers (by default aligned to right)
specs.global.align = units::detail::fmt_align::right; specs.global.align = mp_units::detail::fmt_align::right;
return {begin, end}; return {begin, end};
} }
@@ -412,16 +395,15 @@ private:
if (begin == end || *begin == '}') { if (begin == end || *begin == '}') {
// default format should print value followed by the unit separated with 1 space // default format should print value followed by the unit separated with 1 space
out = units::detail::format_units_quantity_value<CharT>(out, q.number(), specs.rep, ctx.locale()); out = mp_units::detail::format_units_quantity_value<CharT>(out, q.number(), specs.rep, ctx.locale());
constexpr auto symbol = units::detail::unit_text<Dimension, Unit>(); if constexpr (mp_units::detail::has_unit_symbol(get_unit(Reference))) {
if constexpr (symbol.standard().size() > 0) {
*out++ = CharT(' '); *out++ = CharT(' ');
UNITS_STD_FMT::format_to(out, "{}", symbol.standard().c_str()); out = unit_symbol_to<CharT>(out, get_unit(Reference));
} }
} else { } else {
// user provided format // user provided format
units::detail::quantity_formatter f(out, q, specs, ctx.locale()); mp_units::detail::quantity_formatter f(out, q, specs, ctx.locale());
units::detail::parse_units_format(begin, end, f); mp_units::detail::parse_units_format(begin, end, f);
} }
return out; return out;
} }
@@ -441,10 +423,10 @@ public:
// process dynamic width and precision // process dynamic width and precision
if (specs.global.dynamic_width_index >= 0) if (specs.global.dynamic_width_index >= 0)
specs.global.width = specs.global.width =
units::detail::get_dynamic_spec<units::detail::width_checker>(specs.global.dynamic_width_index, ctx); mp_units::detail::get_dynamic_spec<mp_units::detail::width_checker>(specs.global.dynamic_width_index, ctx);
if (specs.rep.dynamic_precision_index >= 0) if (specs.rep.dynamic_precision_index >= 0)
specs.rep.precision = specs.rep.precision =
units::detail::get_dynamic_spec<units::detail::precision_checker>(specs.rep.dynamic_precision_index, ctx); mp_units::detail::get_dynamic_spec<mp_units::detail::precision_checker>(specs.rep.dynamic_precision_index, ctx);
if (specs.global.width == 0) { if (specs.global.width == 0) {
// Avoid extra copying if width is not specified // Avoid extra copying if width is not specified
@@ -461,7 +443,7 @@ public:
// In `global_format_buffer` we will create a global format string // In `global_format_buffer` we will create a global format string
// e.g. "{:*^10%.1Q_%q}, 1.23_q_m" => "{:*^10}" // e.g. "{:*^10%.1Q_%q}, 1.23_q_m" => "{:*^10}"
std::basic_string<CharT> global_format_buffer; std::basic_string<CharT> global_format_buffer;
units::detail::format_global_buffer<CharT>(std::back_inserter(global_format_buffer), specs.global); mp_units::detail::format_global_buffer<CharT>(std::back_inserter(global_format_buffer), specs.global);
// Format the `quantity buffer` using specs from `global_format_buffer` // Format the `quantity buffer` using specs from `global_format_buffer`
// In the example, equivalent to UNITS_STD_FMT::format("{:*^10}", "1.2_m") // In the example, equivalent to UNITS_STD_FMT::format("{:*^10}", "1.2_m")

View File

@@ -22,4 +22,4 @@
cmake_minimum_required(VERSION 3.19) cmake_minimum_required(VERSION 3.19)
add_units_module(core-io DEPENDENCIES mp-units::core HEADERS include/units/quantity_io.h) add_units_module(core-io DEPENDENCIES mp-units::core HEADERS include/mp-units/iostream.h)

View File

@@ -23,32 +23,28 @@
#pragma once #pragma once
#include <units/quantity.h> #include <mp-units/quantity.h>
#include <mp-units/unit.h>
// IWYU pragma: begin_exports
#include <units/bits/external/fixed_string_io.h>
#include <units/bits/unit_text.h>
#include <sstream> #include <sstream>
// IWYU pragma: end_exports
namespace units { namespace mp_units {
namespace detail { namespace detail {
template<typename CharT, class Traits, typename D, typename U, typename Rep> template<typename CharT, class Traits, auto R, typename Rep>
void to_stream(std::basic_ostream<CharT, Traits>& os, const quantity<D, U, Rep>& q) void to_stream(std::basic_ostream<CharT, Traits>& os, const quantity<R, Rep>& q)
{ {
os << q.number(); os << q.number();
constexpr auto symbol = detail::unit_text<D, U>(); if constexpr (has_unit_symbol(get_unit(R))) {
if constexpr (!symbol.standard().empty()) { os << " ";
os << " " << symbol.standard(); unit_symbol_to<CharT>(std::ostream_iterator<CharT>(os), get_unit(R));
} }
} }
} // namespace detail } // namespace detail
template<typename CharT, typename Traits, typename D, typename U, typename Rep> template<typename CharT, typename Traits, auto R, typename Rep>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const quantity<D, U, Rep>& q) std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const quantity<R, Rep>& q)
requires requires { os << q.number(); } requires requires { os << q.number(); }
{ {
if (os.width()) { if (os.width()) {
@@ -65,4 +61,4 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
return os; return os;
} }
} // namespace units } // namespace mp_units

View File

@@ -1,37 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <units/bits/external/fixed_string.h>
#include <ostream>
namespace units {
template<typename CharT, class Traits, std::size_t N>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
const basic_fixed_string<CharT, N>& txt)
{
return os << txt.c_str();
}
} // namespace units

View File

@@ -22,10 +22,6 @@
cmake_minimum_required(VERSION 3.19) cmake_minimum_required(VERSION 3.19)
# core library options
set(${projectPrefix}DOWNCAST_MODE ON CACHE STRING "Select downcasting mode")
set_property(CACHE ${projectPrefix}DOWNCAST_MODE PROPERTY STRINGS AUTO ON OFF)
# find dependencies # find dependencies
if(NOT TARGET gsl::gsl-lite) if(NOT TARGET gsl::gsl-lite)
find_package(gsl-lite CONFIG REQUIRED) find_package(gsl-lite CONFIG REQUIRED)
@@ -39,30 +35,41 @@ check_libcxx_in_use(${projectPrefix}LIBCXX)
add_library( add_library(
mp-units-core mp-units-core
INTERFACE INTERFACE
include/units/base_dimension.h include/mp-units/bits/external/fixed_string.h
include/units/chrono.h include/mp-units/bits/external/hacks.h
include/units/concepts.h include/mp-units/bits/external/type_list.h
include/units/customization_points.h include/mp-units/bits/external/type_name.h
include/units/derived_dimension.h include/mp-units/bits/external/type_traits.h
include/units/exponent.h include/mp-units/bits/algorithm.h
include/units/generic/angle.h include/mp-units/bits/dimension_concepts.h
include/units/generic/dimensionless.h include/mp-units/bits/expression_template.h
include/units/generic/solid_angle.h include/mp-units/bits/get_associated_quantity.h
include/units/kind.h include/mp-units/bits/get_common_base.h
include/units/magnitude.h include/mp-units/bits/magnitude.h
include/units/math.h include/mp-units/bits/math_concepts.h
include/units/point_origin.h include/mp-units/bits/prime.h
include/units/prefix.h include/mp-units/bits/quantity_cast.h
include/units/quantity.h include/mp-units/bits/quantity_concepts.h
include/units/quantity_cast.h include/mp-units/bits/quantity_point_concepts.h
include/units/quantity_kind.h include/mp-units/bits/quantity_spec_concepts.h
include/units/quantity_point.h include/mp-units/bits/ratio.h
include/units/quantity_point_kind.h include/mp-units/bits/reference_concepts.h
include/units/random.h include/mp-units/bits/representation_concepts.h
include/units/ratio.h include/mp-units/bits/sudo_cast.h
include/units/reference.h include/mp-units/bits/symbol_text.h
include/units/symbol_text.h include/mp-units/bits/text_tools.h
include/units/unit.h include/mp-units/bits/unit_concepts.h
include/mp-units/bits/value_cast.h
include/mp-units/concepts.h
include/mp-units/core.h
include/mp-units/customization_points.h
include/mp-units/dimension.h
include/mp-units/quantity.h
include/mp-units/quantity_point.h
include/mp-units/quantity_spec.h
include/mp-units/reference.h
include/mp-units/system_reference.h
include/mp-units/unit.h
) )
target_compile_features(mp-units-core INTERFACE cxx_std_20) target_compile_features(mp-units-core INTERFACE cxx_std_20)
target_link_libraries(mp-units-core INTERFACE gsl::gsl-lite) target_link_libraries(mp-units-core INTERFACE gsl::gsl-lite)
@@ -85,23 +92,9 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
) )
endif() endif()
if(DEFINED ${projectPrefix}DOWNCAST_MODE)
set(downcast_mode_options OFF ON AUTO)
list(FIND downcast_mode_options "${${projectPrefix}DOWNCAST_MODE}" downcast_mode)
if(downcast_mode EQUAL -1)
message(FATAL_ERROR
"'${projectPrefix}DOWNCAST_MODE' should be one of ${downcast_mode_options} ('${${projectPrefix}DOWNCAST_MODE}' received)"
)
else()
message(STATUS "${projectPrefix}DOWNCAST_MODE: ${${projectPrefix}DOWNCAST_MODE}")
target_compile_definitions(mp-units-core INTERFACE ${projectPrefix}DOWNCAST_MODE=${downcast_mode})
endif()
endif()
set_target_properties(mp-units-core PROPERTIES EXPORT_NAME core) set_target_properties(mp-units-core PROPERTIES EXPORT_NAME core)
add_library(mp-units::core ALIAS mp-units-core) add_library(mp-units::core ALIAS mp-units-core)
# installation # installation
install(TARGETS mp-units-core EXPORT mp-unitsTargets) install(TARGETS mp-units-core EXPORT mp-unitsTargets)
install(DIRECTORY include/units TYPE INCLUDE) install(DIRECTORY include/mp-units TYPE INCLUDE)

View File

@@ -22,11 +22,13 @@
#pragma once #pragma once
#include <units/bits/external/hacks.h> // IWYU pragma: keep #include <mp-units/bits/external/hacks.h> // IWYU pragma: keep
#include <compare> #include <compare>
#include <initializer_list>
#include <iterator> #include <iterator>
#include <ranges>
namespace units::detail { namespace mp_units::detail {
// TODO refactor two below functions with std::ranges when moved to modules // TODO refactor two below functions with std::ranges when moved to modules
@@ -59,6 +61,19 @@ constexpr auto get_first_of(const Rng& rng, UnaryFunction f)
} }
// TODO remove all the below and use std when moved to modules // TODO remove all the below and use std when moved to modules
template<class InputIt, class ForwardIt>
constexpr InputIt find_first_of(InputIt first, InputIt last, ForwardIt s_first, ForwardIt s_last)
{
for (; first != last; ++first) {
for (ForwardIt it = s_first; it != s_last; ++it) {
if (*first == *it) {
return first;
}
}
}
return last;
}
template<class InputIt1, class InputIt2> template<class InputIt1, class InputIt2>
constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2)
{ {
@@ -94,7 +109,68 @@ constexpr auto lexicographical_compare_three_way(I1 f1, I1 l1, I2 f2, I2 l2, Cmp
template<class I1, class I2> template<class I1, class I2>
constexpr auto lexicographical_compare_three_way(I1 f1, I1 l1, I2 f2, I2 l2) constexpr auto lexicographical_compare_three_way(I1 f1, I1 l1, I2 f2, I2 l2)
{ {
return ::units::detail::lexicographical_compare_three_way(f1, l1, f2, l2, std::compare_three_way()); return ::mp_units::detail::lexicographical_compare_three_way(f1, l1, f2, l2, std::compare_three_way());
} }
} // namespace units::detail template<class ForwardIt>
constexpr ForwardIt max_element(ForwardIt first, ForwardIt last)
{
if (first == last) return last;
ForwardIt largest = first;
++first;
for (; first != last; ++first)
if (*largest < *first) largest = first;
return largest;
}
template<class T>
constexpr T max(std::initializer_list<T> ilist)
{
return *max_element(ilist.begin(), ilist.end());
}
template<class I, class O>
struct in_out_result {
[[no_unique_address]] I in;
[[no_unique_address]] O out;
template<class I2, class O2>
requires std::convertible_to<const I&, I2> && std::convertible_to<const O&, O2>
constexpr operator in_out_result<I2, O2>() const&
{
return {in, out};
}
template<class I2, class O2>
requires std::convertible_to<I, I2> && std::convertible_to<O, O2>
constexpr operator in_out_result<I2, O2>() &&
{
return {std::move(in), std::move(out)};
}
};
template<class I, class O>
using copy_result = in_out_result<I, O>;
template<std::input_iterator I, std::sentinel_for<I> S, std::weakly_incrementable O>
requires std::indirectly_copyable<I, O>
constexpr copy_result<I, O> copy(I first, S last, O result)
{
for (; first != last; ++first, (void)++result) {
*result = *first;
}
return {std::move(first), std::move(result)};
}
template<std::ranges::input_range R, std::weakly_incrementable O>
requires std::indirectly_copyable<std::ranges::iterator_t<R>, O>
constexpr copy_result<std::ranges::borrowed_iterator_t<R>, O> copy(R&& r, O result)
{
return ::mp_units::detail::copy(std::ranges::begin(r), std::ranges::end(r), std::move(result));
}
} // namespace mp_units::detail

View File

@@ -0,0 +1,104 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp-units/bits/expression_template.h>
#include <mp-units/bits/external/type_traits.h>
#include <mp-units/bits/symbol_text.h>
namespace mp_units {
template<basic_symbol_text Symbol>
struct base_dimension;
namespace detail {
template<basic_symbol_text Symbol>
void to_base_specialization_of_base_dimension(const volatile base_dimension<Symbol>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_base_dimension =
requires(T* t) { to_base_specialization_of_base_dimension(t); };
template<typename T>
inline constexpr bool is_specialization_of_base_dimension = false;
template<basic_symbol_text Symbol>
inline constexpr bool is_specialization_of_base_dimension<base_dimension<Symbol>> = true;
/**
* @brief A concept matching all named base dimensions in the library.
*
* Satisfied by all dimension types derived from a specialization of `base_dimension`.
*/
template<typename T>
concept BaseDimension =
is_derived_from_specialization_of_base_dimension<T> && (!is_specialization_of_base_dimension<T>);
template<typename T>
struct is_dimension_one : std::false_type {};
template<typename T>
inline constexpr bool is_power_of_dim = requires {
requires is_specialization_of_power<T> &&
(BaseDimension<typename T::factor> || is_dimension_one<typename T::factor>::value);
};
template<typename T>
inline constexpr bool is_per_of_dims = false;
template<typename... Ts>
inline constexpr bool is_per_of_dims<per<Ts...>> =
(... && (BaseDimension<Ts> || is_dimension_one<Ts>::value || is_power_of_dim<Ts>));
template<typename T>
concept DerivedDimensionExpr =
BaseDimension<T> || is_dimension_one<T>::value || is_power_of_dim<T> || is_per_of_dims<T>;
} // namespace detail
template<detail::DerivedDimensionExpr... Expr>
struct derived_dimension;
namespace detail {
/**
* @brief A concept matching all derived dimensions in the library.
*
* Satisfied by all dimension types either being a specialization of `derived_dimension`
* or derived from it (inheritance needed to properly handle `dimension_one`).
*/
template<typename T>
concept DerivedDimension = is_derived_from_specialization_of<T, derived_dimension>;
} // namespace detail
/**
* @brief A concept matching all dimensions in the library.
*
* Satisfied by all dimension types for which either `BaseDimension<T>` or `DerivedDimension<T>` is `true`.
*/
template<typename T>
concept Dimension = detail::BaseDimension<T> || detail::DerivedDimension<T>;
} // namespace mp_units

View File

@@ -0,0 +1,557 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp-units/bits/external/type_list.h>
#include <mp-units/bits/external/type_traits.h>
#include <mp-units/bits/math_concepts.h>
#include <mp-units/bits/ratio.h>
namespace mp_units {
/**
* @brief Type list type used by the expression template framework
*
* @tparam Ts The list of types
*/
template<typename... Ts>
struct type_list {};
/**
* @brief Type list type storing the list of components with negative exponents
*
* @note Can't be empty
*/
template<typename T, typename... Ts>
struct per {};
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_per = false;
template<typename... Ts>
inline constexpr bool is_specialization_of_per<per<Ts...>> = true;
template<int Num, int... Den>
inline constexpr bool valid_ratio = true;
template<int... Den>
inline constexpr bool valid_ratio<0, Den...> = false;
template<int Num>
inline constexpr bool valid_ratio<Num, 0> = false;
template<>
inline constexpr bool valid_ratio<0, 0> = false;
template<int Num, int... Den>
inline constexpr bool positive_ratio = gt_zero<Num>;
template<int Num, int Den>
inline constexpr bool positive_ratio<Num, Den> = gt_zero<Num * Den>;
template<int Num, int... Den>
inline constexpr bool ratio_one = false;
template<>
inline constexpr bool ratio_one<1> = true;
template<int N>
inline constexpr bool ratio_one<N, N> = true;
} // namespace detail
/**
* @brief Type container for exponents with ratio different than `1`
*
* Ratio must be mathematically valid and non-negative. Negative exponents are passed to
* `per<...>` with inverted sign (as positive exponents).
*
* @tparam F factor to be raised to specified power
* @tparam Num Power ratio numerator
* @tparam Den [optional] Power ration denominator
*
* @note @p Den is an optional parameter to shorten the types presented to the user in the case when @p Den equals `1`.
*/
template<typename F, int Num, int... Den>
requires(detail::valid_ratio<Num, Den...> && detail::positive_ratio<Num, Den...> && !detail::ratio_one<Num, Den...>)
struct power {
using factor = F;
static constexpr ratio exponent{Num, Den...};
};
namespace detail {
template<typename T>
struct expr_type_impl : std::type_identity<T> {};
template<typename T, int... Ints>
struct expr_type_impl<power<T, Ints...>> : std::type_identity<T> {};
} // namespace detail
template<typename T>
using expr_type = TYPENAME detail::expr_type_impl<T>::type;
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_power = false;
template<typename F, int... Ints>
inline constexpr bool is_specialization_of_power<power<F, Ints...>> = true;
template<typename T, ratio R>
consteval auto power_or_T_impl()
{
if constexpr (is_specialization_of_power<T>) {
return power_or_T_impl<typename T::factor, T::exponent * R>();
} else {
if constexpr (R.den == 1) {
if constexpr (R.num == 1)
return T{};
else
return power<T, R.num>{};
} else {
return power<T, R.num, R.den>{};
}
}
}
template<typename T, auto R>
// template<typename T, ratio R> // TODO ICE gcc 12
using power_or_T = decltype(power_or_T_impl<T, R>());
/**
* @brief Consolidates contiguous ranges of exponents of the same type
*
* If there is more than one exponent with the same type they are aggregated into one type by adding
* their powers.
*/
template<typename List>
struct expr_consolidate_impl;
template<>
struct expr_consolidate_impl<type_list<>> {
using type = type_list<>;
};
template<typename T>
struct expr_consolidate_impl<type_list<T>> {
using type = type_list<T>;
};
template<typename T, typename... Rest>
struct expr_consolidate_impl<type_list<T, Rest...>> {
using type = type_list_push_front<typename expr_consolidate_impl<type_list<Rest...>>::type, T>;
};
// replaces two instances of a type with one having the power of `2`
template<typename T, typename... Rest>
requires(!is_specialization_of_power<T>)
struct expr_consolidate_impl<type_list<T, T, Rest...>> {
using type = TYPENAME expr_consolidate_impl<type_list<power<T, 2>, Rest...>>::type;
};
// replaces the instance of a type and a power of it with one with incremented power
template<typename T, int... Ints, typename... Rest>
struct expr_consolidate_impl<type_list<T, power<T, Ints...>, Rest...>> {
using type = TYPENAME expr_consolidate_impl<type_list<power_or_T<T, power<T, Ints...>::exponent + 1>, Rest...>>::type;
};
// accumulates the powers of instances of the same type (removes the element in case the accumulation result is `0`)
template<typename T, int... Ints1, int... Ints2, typename... Rest>
struct expr_consolidate_impl<type_list<power<T, Ints1...>, power<T, Ints2...>, Rest...>> {
static constexpr ratio r = power<T, Ints1...>::exponent + power<T, Ints2...>::exponent;
using type = TYPENAME expr_consolidate_impl<type_list<power_or_T<T, r>, Rest...>>::type;
};
template<typename List>
using expr_consolidate = TYPENAME expr_consolidate_impl<List>::type;
/**
* @brief Simplifies the expression template
*
* Analyzes provided numerator and denominator type lists and simplifies elements with the same type.
* If the same power exists in both list, this elements gets omitted. Otherwise, the power of its
* exponent gets subtracted according to numerator and denominator elements powers.
*
* @tparam NumList type list for expression numerator
* @tparam DenList type list for expression denominator
* @tparam Pred predicate to be used for elements comparisons
*/
template<typename NumList, typename DenList, template<typename, typename> typename Pred>
struct expr_simplify;
// when one of the lists is empty there is nothing to do
template<typename NumList, typename DenList, template<typename, typename> typename Pred>
requires(type_list_size<NumList> == 0) || (type_list_size<DenList> == 0)
struct expr_simplify<NumList, DenList, Pred> {
using num = NumList;
using den = DenList;
};
// in case when front elements are different progress to the next element
template<typename Num, typename... NRest, typename Den, typename... DRest, template<typename, typename> typename Pred>
struct expr_simplify<type_list<Num, NRest...>, type_list<Den, DRest...>, Pred> {
using impl = conditional<Pred<Num, Den>::value, expr_simplify<type_list<NRest...>, type_list<Den, DRest...>, Pred>,
expr_simplify<type_list<Num, NRest...>, type_list<DRest...>, Pred>>;
using num = conditional<Pred<Num, Den>::value, type_list_push_front<typename impl::num, Num>, typename impl::num>;
using den = conditional<Pred<Num, Den>::value, typename impl::den, type_list_push_front<typename impl::den, Den>>;
};
// in case two elements are of the same power such element gets omitted
template<typename T, typename... NRest, typename... DRest, template<typename, typename> typename Pred>
struct expr_simplify<type_list<T, NRest...>, type_list<T, DRest...>, Pred> :
expr_simplify<type_list<NRest...>, type_list<DRest...>, Pred> {};
template<typename T, ratio Num, ratio Den>
struct expr_simplify_power {
static constexpr ratio r = Num - Den;
using type = power_or_T<T, ratio{abs(r.num), r.den}>;
using num = conditional<(r > 0), type_list<type>, type_list<>>;
using den = conditional<(r < 0), type_list<type>, type_list<>>;
};
// in case there are different powers for the same element simplify the power
template<typename T, typename... NRest, int... Ints, typename... DRest, template<typename, typename> typename Pred>
struct expr_simplify<type_list<power<T, Ints...>, NRest...>, type_list<T, DRest...>, Pred> {
using impl = expr_simplify<type_list<NRest...>, type_list<DRest...>, Pred>;
using type = expr_simplify_power<T, power<T, Ints...>::exponent, ratio{1}>;
using num = type_list_join<typename type::num, typename impl::num>;
using den = type_list_join<typename type::den, typename impl::den>;
};
// in case there are different powers for the same element simplify the power
template<typename T, typename... NRest, typename... DRest, int... Ints, template<typename, typename> typename Pred>
struct expr_simplify<type_list<T, NRest...>, type_list<power<T, Ints...>, DRest...>, Pred> {
using impl = expr_simplify<type_list<NRest...>, type_list<DRest...>, Pred>;
using type = expr_simplify_power<T, ratio{1}, power<T, Ints...>::exponent>;
using num = type_list_join<typename type::num, typename impl::num>;
using den = type_list_join<typename type::den, typename impl::den>;
};
// in case there are different powers for the same element simplify the power
template<typename T, typename... NRest, int... Ints1, typename... DRest, int... Ints2,
template<typename, typename> typename Pred>
requires(!std::same_as<power<T, Ints1...>, power<T, Ints2...>>)
struct expr_simplify<type_list<power<T, Ints1...>, NRest...>, type_list<power<T, Ints2...>, DRest...>, Pred> {
using impl = expr_simplify<type_list<NRest...>, type_list<DRest...>, Pred>;
using type = expr_simplify_power<T, power<T, Ints1...>::exponent, power<T, Ints2...>::exponent>;
using num = type_list_join<typename type::num, typename impl::num>;
using den = type_list_join<typename type::den, typename impl::den>;
};
// expr_less
template<typename Lhs, typename Rhs, template<typename, typename> typename Pred>
struct expr_less_impl : Pred<expr_type<Lhs>, expr_type<Rhs>> {};
template<typename T, int... Ints, template<typename, typename> typename Pred>
struct expr_less_impl<T, power<T, Ints...>, Pred> : std::true_type {};
/**
* @brief Compares two types with a given predicate
*
* Algorithm accounts not only for explicit types but also for the case when they
* are wrapped within `power<T, Num, Den>`.
*/
template<typename Lhs, typename Rhs, template<typename, typename> typename Pred>
using expr_less = expr_less_impl<Lhs, Rhs, Pred>;
// expr_fractions
template<typename Num = type_list<>, typename Den = type_list<>>
struct expr_fractions_result {
using _num_ = Num; // exposition only
using _den_ = Den; // exposition only
};
template<template<typename> typename OneType, typename List>
[[nodiscard]] consteval auto expr_fractions_impl()
{
constexpr std::size_t size = type_list_size<List>;
if constexpr (size == 0)
return expr_fractions_result<>{};
else if constexpr (size == 1)
return expr_fractions_result<List>{};
else {
using last_element = type_list_back<List>;
if constexpr (is_specialization_of_per<last_element>) {
if constexpr (size == 2 && OneType<type_list_front<List>>::value)
return expr_fractions_result<type_list<>, type_list_map<last_element, type_list>>{};
else {
using split = type_list_split<List, size - 1>;
return expr_fractions_result<typename split::first_list, type_list_map<last_element, type_list>>{};
}
} else {
return expr_fractions_result<List>{};
}
}
}
/**
* @brief Divides expression template spec to numerator and denominator parts
*/
template<template<typename> typename OneType, typename... Ts>
struct expr_fractions : decltype(expr_fractions_impl<OneType, type_list<Ts...>>()) {};
// expr_make_spec
template<typename NumList, typename DenList, typename OneType, template<typename...> typename To>
[[nodiscard]] consteval auto expr_make_spec_impl()
{
constexpr std::size_t num = type_list_size<NumList>;
constexpr std::size_t den = type_list_size<DenList>;
if constexpr (num == 0 && den == 0) {
return OneType{};
} else if constexpr (num > 0 && den > 0) {
return type_list_map<type_list_push_back<NumList, type_list_map<DenList, per>>, To>{};
} else if constexpr (den > 0) {
return To<OneType, type_list_map<DenList, per>>{};
} else {
if constexpr (num == 1 && !is_specialization_of_power<type_list_front<NumList>>)
// temporary derived type not needed -> just return the original one
return type_list_front<NumList>{};
else
return type_list_map<NumList, To>{};
}
}
/**
* @brief Creates an expression template spec based on provided numerator and denominator parts
*/
template<typename NumList, typename DenList, typename OneType, template<typename...> typename To>
using expr_make_spec = decltype(expr_make_spec_impl<NumList, DenList, OneType, To>());
template<typename NumList, typename DenList, typename OneType, template<typename, typename> typename Pred,
template<typename...> typename To>
[[nodiscard]] consteval auto get_optimized_expression()
{
using num_list = expr_consolidate<NumList>;
using den_list = expr_consolidate<DenList>;
using simple = expr_simplify<num_list, den_list, Pred>;
using expr = expr_make_spec<typename simple::num, typename simple::den, OneType, To>;
return expr{};
}
/**
* @brief Multiplies two sorted expression template specs
*
* @tparam To destination type list to put the result to
* @tparam OneType type that represents the value `1`
* @tparam Pred binary less then predicate
* @tparam Lhs lhs of the operation
* @tparam Rhs rhs of the operation
*/
template<template<typename...> typename To, typename OneType, template<typename, typename> typename Pred, typename Lhs,
typename Rhs>
[[nodiscard]] consteval auto expr_multiply(Lhs, Rhs)
{
if constexpr (is_same_v<Lhs, OneType>) {
return Rhs{};
} else if constexpr (is_same_v<Rhs, OneType>) {
return Lhs{};
} else if constexpr (is_specialization_of<Lhs, To> && is_specialization_of<Rhs, To>) {
// two derived dimensions
return get_optimized_expression<type_list_merge_sorted<typename Lhs::_num_, typename Rhs::_num_, Pred>,
type_list_merge_sorted<typename Lhs::_den_, typename Rhs::_den_, Pred>, OneType,
Pred, To>();
} else if constexpr (is_specialization_of<Lhs, To>) {
return get_optimized_expression<type_list_merge_sorted<typename Lhs::_num_, type_list<Rhs>, Pred>,
typename Lhs::_den_, OneType, Pred, To>();
} else if constexpr (is_specialization_of<Rhs, To>) {
return get_optimized_expression<type_list_merge_sorted<typename Rhs::_num_, type_list<Lhs>, Pred>,
typename Rhs::_den_, OneType, Pred, To>();
} else {
// two base dimensions
return get_optimized_expression<type_list_merge_sorted<type_list<Lhs>, type_list<Rhs>, Pred>, type_list<>, OneType,
Pred, To>();
}
}
/**
* @brief Divides two sorted expression template specs
*
* @tparam To destination type list to put the result to
* @tparam OneType type that represents the value `1`
* @tparam Pred binary less then predicate
* @tparam Lhs lhs of the operation
* @tparam Rhs rhs of the operation
*/
template<template<typename...> typename To, typename OneType, template<typename, typename> typename Pred, typename Lhs,
typename Rhs>
[[nodiscard]] consteval auto expr_divide(Lhs lhs, Rhs rhs)
{
if constexpr (is_same_v<Lhs, Rhs>) {
return OneType{};
} else if constexpr (is_same_v<Rhs, OneType>) {
return lhs;
} else if constexpr (is_same_v<Lhs, OneType>) {
return expr_divide<To, OneType, Pred>(To<>{}, rhs);
} else if constexpr (is_specialization_of<Lhs, To> && is_specialization_of<Rhs, To>) {
// two derived entities
return get_optimized_expression<type_list_merge_sorted<typename Lhs::_num_, typename Rhs::_den_, Pred>,
type_list_merge_sorted<typename Lhs::_den_, typename Rhs::_num_, Pred>, OneType,
Pred, To>();
} else if constexpr (is_specialization_of<Lhs, To>) {
return get_optimized_expression<
typename Lhs::_num_, type_list_merge_sorted<typename Lhs::_den_, type_list<Rhs>, Pred>, OneType, Pred, To>();
} else if constexpr (is_specialization_of<Rhs, To>) {
return get_optimized_expression<type_list_merge_sorted<typename Rhs::_den_, type_list<Lhs>, Pred>,
typename Rhs::_num_, OneType, Pred, To>();
} else {
// two named entities
return To<Lhs, per<Rhs>>{};
}
}
/**
* @brief Inverts the expression template spec
*
* @tparam T expression template spec to invert
* @tparam OneType type that represents the value `1`
* @tparam To destination type list to put the result to
*/
template<template<typename...> typename To, typename OneType, typename T>
[[nodiscard]] consteval auto expr_invert(T)
{
if constexpr (is_specialization_of<T, To>)
return expr_make_spec<typename T::_den_, typename T::_num_, OneType, To>{};
else
return To<OneType, per<T>>{};
}
template<std::intmax_t Num, std::intmax_t Den, template<typename...> typename To, typename OneType,
template<typename, typename> typename Pred, typename... Nums, typename... Dens>
requires detail::non_zero<Den>
[[nodiscard]] consteval auto expr_pow_impl(type_list<Nums...>, type_list<Dens...>)
{
return detail::get_optimized_expression<type_list<power_or_T<Nums, ratio{Num, Den}>...>,
type_list<power_or_T<Dens, ratio{Num, Den}>...>, OneType, Pred, To>();
}
/**
* @brief Computes the value of an expression raised to the `Num/Den` power
*
* @tparam Num Exponent numerator
* @tparam Den Exponent denominator
* @tparam To destination type list to put the result to
* @tparam OneType type that represents the value `1`
* @tparam Pred binary less then predicate
* @tparam T Expression being the base of the operation
*/
template<std::intmax_t Num, std::intmax_t Den, template<typename...> typename To, typename OneType,
template<typename, typename> typename Pred, typename T>
requires detail::non_zero<Den>
[[nodiscard]] consteval auto expr_pow(T)
{
return expr_pow_impl<Num, Den, To, OneType, Pred>(typename T::_num_{}, typename T::_den_{});
}
// expr_map
template<typename T, template<typename> typename Proj>
struct expr_type_map;
template<typename T, template<typename> typename Proj>
requires requires { typename Proj<T>; }
struct expr_type_map<T, Proj> {
using type = Proj<T>;
};
template<typename F, int... Ints, template<typename> typename Proj>
requires requires { typename Proj<F>; }
struct expr_type_map<power<F, Ints...>, Proj> {
using type = power<Proj<F>, Ints...>;
};
template<typename T, template<typename> typename Proj>
concept expr_type_projectable = (requires { typename Proj<T>; } ||
(is_specialization_of_power<T> && requires { typename Proj<typename T::factor>; }));
template<typename T, template<typename> typename Proj>
inline constexpr bool expr_projectable_impl = false;
template<typename... Ts, template<typename> typename Proj>
inline constexpr bool expr_projectable_impl<type_list<Ts...>, Proj> = (... && expr_type_projectable<Ts, Proj>);
template<typename T, template<typename> typename Proj>
concept expr_projectable = requires {
typename T::_num_;
typename T::_den_;
requires expr_projectable_impl<typename T::_num_, Proj>;
requires expr_projectable_impl<typename T::_den_, Proj>;
};
template<typename T>
[[nodiscard]] consteval auto map_power(T t)
{
return t;
}
template<typename T, auto... Ints>
[[nodiscard]] consteval auto map_power(power<T, Ints...>)
{
return pow<Ints...>(T{});
}
template<template<typename> typename Proj, template<typename...> typename To, typename OneType,
template<typename, typename> typename Pred, expr_type_projectable<Proj>... Nums,
expr_type_projectable<Proj>... Dens>
[[nodiscard]] consteval auto expr_map_impl(type_list<Nums...>, type_list<Dens...>)
{
return (OneType{} * ... * map_power(typename expr_type_map<std::remove_const_t<Nums>, Proj>::type{})) /
(OneType{} * ... * map_power(typename expr_type_map<std::remove_const_t<Dens>, Proj>::type{}));
}
/**
* @brief Maps contents of one expression template to another resulting in a different type list
*
* @tparam Proj Projection to be used for mapping
* @tparam To destination type list to put the result to
* @tparam OneType type that represents the value `1`
* @tparam Pred binary less then predicate
* @tparam T expression template to map from
*/
template<template<typename> typename Proj, template<typename...> typename To, typename OneType,
template<typename, typename> typename Pred, expr_projectable<Proj> T>
[[nodiscard]] consteval auto expr_map(T)
{
if constexpr (type_list_size<typename T::_num_> + type_list_size<typename T::_den_> == 0)
return OneType{};
else
return expr_map_impl<Proj, To, OneType, Pred>(typename T::_num_{}, typename T::_den_{});
}
} // namespace detail
} // namespace mp_units

View File

@@ -22,9 +22,9 @@
#pragma once #pragma once
#include <units/bits/external/hacks.h> // IWYU pragma: keep #include <mp-units/bits/external/hacks.h> // IWYU pragma: keep
// TODO use <algorithm> when moved to C++20 modules (parsing takes too long for each translation unit) // TODO use <algorithm> when moved to C++20 modules (parsing takes too long for each translation unit)
#include <units/bits/algorithm.h> #include <mp-units/bits/algorithm.h>
// IWYU pragma: begin_exports // IWYU pragma: begin_exports
#include <compare> #include <compare>
@@ -34,7 +34,7 @@
#include <cstddef> #include <cstddef>
namespace units { namespace mp_units {
/** /**
* @brief A compile-time fixed string * @brief A compile-time fixed string
@@ -111,4 +111,4 @@ basic_fixed_string(CharT) -> basic_fixed_string<CharT, 1>;
template<std::size_t N> template<std::size_t N>
using fixed_string = basic_fixed_string<char, N>; using fixed_string = basic_fixed_string<char, N>;
} // namespace units } // namespace mp_units

View File

@@ -52,6 +52,7 @@
#define UNITS_DIAGNOSTIC_IGNORE_SHADOW UNITS_DIAGNOSTIC_IGNORE("-Wshadow") #define UNITS_DIAGNOSTIC_IGNORE_SHADOW UNITS_DIAGNOSTIC_IGNORE("-Wshadow")
#define UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE #define UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE
#define UNITS_DIAGNOSTIC_IGNORE_ZERO_AS_NULLPOINTER_CONSTANT UNITS_DIAGNOSTIC_IGNORE("-Wzero-as-nullpointer-constant") #define UNITS_DIAGNOSTIC_IGNORE_ZERO_AS_NULLPOINTER_CONSTANT UNITS_DIAGNOSTIC_IGNORE("-Wzero-as-nullpointer-constant")
#define UNITS_DIAGNOSTIC_IGNORE_DEPRECATED UNITS_DIAGNOSTIC_IGNORE("-Wdeprecated-declarations")
#else #else
#define UNITS_DIAGNOSTIC_PUSH UNITS_PRAGMA(warning(push)) #define UNITS_DIAGNOSTIC_PUSH UNITS_PRAGMA(warning(push))
#define UNITS_DIAGNOSTIC_POP UNITS_PRAGMA(warning(pop)) #define UNITS_DIAGNOSTIC_POP UNITS_PRAGMA(warning(pop))
@@ -65,6 +66,7 @@
#define UNITS_DIAGNOSTIC_IGNORE_SHADOW UNITS_DIAGNOSTIC_IGNORE(4459) #define UNITS_DIAGNOSTIC_IGNORE_SHADOW UNITS_DIAGNOSTIC_IGNORE(4459)
#define UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE UNITS_DIAGNOSTIC_IGNORE(4702) #define UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE UNITS_DIAGNOSTIC_IGNORE(4702)
#define UNITS_DIAGNOSTIC_IGNORE_ZERO_AS_NULLPOINTER_CONSTANT #define UNITS_DIAGNOSTIC_IGNORE_ZERO_AS_NULLPOINTER_CONSTANT
#define UNITS_DIAGNOSTIC_IGNORE_DEPRECATED
#endif #endif
#if _LIBCPP_VERSION #if _LIBCPP_VERSION

View File

@@ -22,12 +22,14 @@
#pragma once #pragma once
#include <mp-units/bits/external/hacks.h> // IWYU pragma: keep
#include <cstddef> #include <cstddef>
#include <utility>
UNITS_DIAGNOSTIC_PUSH UNITS_DIAGNOSTIC_PUSH
UNITS_DIAGNOSTIC_IGNORE_EXPR_ALWAYS_TF UNITS_DIAGNOSTIC_IGNORE_EXPR_ALWAYS_TF
namespace units { namespace mp_units {
namespace detail { namespace detail {
@@ -42,6 +44,94 @@ inline constexpr bool is_type_list<T<Types...>> = true;
template<typename T> template<typename T>
concept TypeList = detail::is_type_list<T>; concept TypeList = detail::is_type_list<T>;
// size
namespace detail {
template<typename List>
struct type_list_size_impl;
template<template<typename...> typename List, typename... Types>
struct type_list_size_impl<List<Types...>> : std::integral_constant<std::size_t, sizeof...(Types)> {};
} // namespace detail
template<TypeList List>
inline constexpr std::size_t type_list_size = detail::type_list_size_impl<List>::value;
// map
namespace detail {
template<typename T, template<typename...> typename To>
struct type_list_map_impl;
template<template<typename...> typename From, template<typename...> typename To, typename... Args>
struct type_list_map_impl<From<Args...>, To> {
using type = To<Args...>;
};
} // namespace detail
template<TypeList From, template<typename...> typename To>
using type_list_map = TYPENAME detail::type_list_map_impl<From, To>::type;
// element
namespace detail {
template<std::size_t I, typename T>
struct type_list_leaf {
using type = T;
};
template<typename Seq, typename...>
struct indexed_type_list_impl;
template<std::size_t... Is, typename... Ts>
struct indexed_type_list_impl<std::index_sequence<Is...>, Ts...> : type_list_leaf<Is, Ts>... {};
template<std::size_t I, typename T>
std::type_identity<T> type_list_element_func(const type_list_leaf<I, T>&);
} // namespace detail
template<typename... Ts>
struct indexed_type_list : detail::indexed_type_list_impl<std::index_sequence_for<Ts...>, Ts...> {};
template<TypeList List, std::size_t I>
using type_list_element_indexed = typename decltype(detail::type_list_element_func<I>(std::declval<List>()))::type;
template<TypeList List, std::size_t I>
using type_list_element = type_list_element_indexed<type_list_map<List, indexed_type_list>, I>;
// front
namespace detail {
template<typename List>
struct type_list_front_impl;
template<template<typename...> typename List, typename T, typename... Ts>
struct type_list_front_impl<List<T, Ts...>> {
using type = T;
};
} // namespace detail
template<TypeList List>
using type_list_front = TYPENAME detail::type_list_front_impl<List>::type;
// back
template<TypeList List>
using type_list_back = type_list_element<List, type_list_size<List> - 1>;
// push_front // push_front
namespace detail { namespace detail {
@@ -59,6 +149,7 @@ struct type_list_push_front_impl<List<OldTypes...>, NewTypes...> {
template<TypeList List, typename... Types> template<TypeList List, typename... Types>
using type_list_push_front = TYPENAME detail::type_list_push_front_impl<List, Types...>::type; using type_list_push_front = TYPENAME detail::type_list_push_front_impl<List, Types...>::type;
// push_back // push_back
namespace detail { namespace detail {
@@ -76,6 +167,7 @@ struct type_list_push_back_impl<List<OldTypes...>, NewTypes...> {
template<TypeList List, typename... Types> template<TypeList List, typename... Types>
using type_list_push_back = TYPENAME detail::type_list_push_back_impl<List, Types...>::type; using type_list_push_back = TYPENAME detail::type_list_push_back_impl<List, Types...>::type;
// join // join
namespace detail { namespace detail {
@@ -95,46 +187,29 @@ struct type_list_join_impl<List<First...>, List<Second...>, Rest...> {
template<TypeList... Lists> template<TypeList... Lists>
using type_list_join = TYPENAME detail::type_list_join_impl<Lists...>::type; using type_list_join = TYPENAME detail::type_list_join_impl<Lists...>::type;
// split // split
namespace detail { namespace detail {
template<template<typename...> typename List, std::size_t Idx, std::size_t N, typename... Types> template<typename List, typename First, typename Second>
struct split_impl; struct type_list_split_impl;
template<template<typename...> typename List, std::size_t Idx, std::size_t N> template<template<typename...> typename List, typename... Args, std::size_t... First, std::size_t... Second>
struct split_impl<List, Idx, N> { struct type_list_split_impl<List<Args...>, std::index_sequence<First...>, std::index_sequence<Second...>> {
using first_list = List<>; using indexed_list = indexed_type_list<Args...>;
using second_list = List<>; using first_list = List<type_list_element_indexed<indexed_list, First>...>;
}; using second_list = List<type_list_element_indexed<indexed_list, sizeof...(First) + Second>...>;
template<template<typename...> typename List, std::size_t Idx, std::size_t N, typename T, typename... Rest>
requires(Idx < N)
struct split_impl<List, Idx, N, T, Rest...> : split_impl<List, Idx + 1, N, Rest...> {
using base = split_impl<List, Idx + 1, N, Rest...>;
using first_list = TYPENAME type_list_push_front_impl<typename base::first_list, T>::type;
using second_list = TYPENAME base::second_list;
};
template<template<typename...> typename List, std::size_t Idx, std::size_t N, typename T, typename... Rest>
struct split_impl<List, Idx, N, T, Rest...> : split_impl<List, Idx + 1, N, Rest...> {
using base = split_impl<List, Idx + 1, N, Rest...>;
using first_list = TYPENAME base::first_list;
using second_list = TYPENAME type_list_push_front_impl<typename base::second_list, T>::type;
}; };
} // namespace detail } // namespace detail
template<TypeList List, std::size_t N> template<TypeList List, std::size_t N>
struct type_list_split; requires(N <= type_list_size<List>)
struct type_list_split :
detail::type_list_split_impl<List, std::make_index_sequence<N>,
std::make_index_sequence<type_list_size<List> - N>> {};
template<template<typename...> typename List, std::size_t N, typename... Types>
struct type_list_split<List<Types...>, N> {
static_assert(N <= sizeof...(Types), "Invalid index provided");
using split = detail::split_impl<List, 0, N, Types...>;
using first_list = TYPENAME split::first_list;
using second_list = TYPENAME split::second_list;
};
// split_half // split_half
@@ -144,6 +219,7 @@ struct type_list_split_half;
template<template<typename...> typename List, typename... Types> template<template<typename...> typename List, typename... Types>
struct type_list_split_half<List<Types...>> : type_list_split<List<Types...>, (sizeof...(Types) + 1) / 2> {}; struct type_list_split_half<List<Types...>> : type_list_split<List<Types...>, (sizeof...(Types) + 1) / 2> {};
// merge_sorted // merge_sorted
namespace detail { namespace detail {
@@ -166,7 +242,6 @@ struct type_list_merge_sorted_impl<List<>, List<Rhs...>, Pred> {
using type = List<Rhs...>; using type = List<Rhs...>;
}; };
template<template<typename...> typename List, typename Lhs1, typename... LhsRest, typename Rhs1, typename... RhsRest, template<template<typename...> typename List, typename Lhs1, typename... LhsRest, typename Rhs1, typename... RhsRest,
template<typename, typename> typename Pred> template<typename, typename> typename Pred>
requires Pred<Lhs1, Rhs1>::value requires Pred<Lhs1, Rhs1>::value
@@ -187,6 +262,7 @@ struct type_list_merge_sorted_impl<List<Lhs1, LhsRest...>, List<Rhs1, RhsRest...
template<TypeList SortedList1, TypeList SortedList2, template<typename, typename> typename Pred> template<TypeList SortedList1, TypeList SortedList2, template<typename, typename> typename Pred>
using type_list_merge_sorted = TYPENAME detail::type_list_merge_sorted_impl<SortedList1, SortedList2, Pred>::type; using type_list_merge_sorted = TYPENAME detail::type_list_merge_sorted_impl<SortedList1, SortedList2, Pred>::type;
// sort // sort
namespace detail { namespace detail {
@@ -218,6 +294,6 @@ struct type_list_sort_impl<List<Types...>, Pred> {
template<TypeList List, template<typename, typename> typename Pred> template<TypeList List, template<typename, typename> typename Pred>
using type_list_sort = TYPENAME detail::type_list_sort_impl<List, Pred>::type; using type_list_sort = TYPENAME detail::type_list_sort_impl<List, Pred>::type;
} // namespace units } // namespace mp_units
UNITS_DIAGNOSTIC_POP UNITS_DIAGNOSTIC_POP

View File

@@ -0,0 +1,40 @@
// https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c/56766138#56766138
#pragma once
#include <string_view>
template<typename T>
[[nodiscard]] consteval std::string_view type_name()
{
std::string_view name, prefix, suffix;
#ifdef __clang__
name = __PRETTY_FUNCTION__;
prefix = "auto type_name() [T = ";
suffix = "]";
#elif defined(__GNUC__)
name = __PRETTY_FUNCTION__;
prefix = "constexpr auto type_name() [with T = ";
suffix = "]";
#elif defined(_MSC_VER)
name = __FUNCSIG__;
prefix = "auto __cdecl type_name<";
suffix = ">(void)";
#endif
name.remove_prefix(prefix.size());
name.remove_suffix(suffix.size());
return name;
}
template<typename T1, typename T2>
[[nodiscard]] consteval auto better_type_name(T1 v1, T2 v2)
{
if constexpr (type_name<T1>().size() < type_name<T2>().size())
return v1;
else if constexpr (type_name<T2>().size() < type_name<T1>().size())
return v2;
else if constexpr (type_name<T1>() < type_name<T2>())
return v1;
else
return v2;
}

View File

@@ -22,11 +22,11 @@
#pragma once #pragma once
#include <units/bits/external/hacks.h> #include <mp-units/bits/external/hacks.h>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
namespace units { namespace mp_units {
// conditional // conditional
namespace detail { namespace detail {
@@ -65,6 +65,12 @@ inline constexpr bool is_specialization_of = false;
template<typename... Params, template<typename...> typename Type> template<typename... Params, template<typename...> typename Type>
inline constexpr bool is_specialization_of<Type<Params...>, Type> = true; inline constexpr bool is_specialization_of<Type<Params...>, Type> = true;
template<typename T, template<auto...> typename Type>
inline constexpr bool is_specialization_of_v = false;
template<auto... Params, template<auto...> typename Type>
inline constexpr bool is_specialization_of_v<Type<Params...>, Type> = true;
// is_derived_from_specialization_of // is_derived_from_specialization_of
namespace detail { namespace detail {
@@ -77,4 +83,72 @@ template<typename T, template<typename...> typename Type>
// inline constexpr bool // TODO: Replace with concept when it works with MSVC // inline constexpr bool // TODO: Replace with concept when it works with MSVC
concept is_derived_from_specialization_of = requires(T* t) { detail::to_base_specialization_of<Type>(t); }; concept is_derived_from_specialization_of = requires(T* t) { detail::to_base_specialization_of<Type>(t); };
} // namespace units template<typename T, typename... Ts>
concept one_of = (false || ... || std::same_as<T, Ts>);
template<typename T, auto... Vs>
[[nodiscard]] consteval bool contains()
{
return (false || ... || is_same_v<std::remove_const_t<decltype(Vs)>, T>);
}
template<template<typename...> typename T, typename... Ts>
[[nodiscard]] consteval bool contains()
{
return (false || ... || is_specialization_of<Ts, T>);
}
template<template<auto...> typename T, typename... Ts>
[[nodiscard]] consteval bool contains()
{
return (false || ... || is_specialization_of_v<Ts, T>);
}
template<typename T, std::same_as<T> auto V>
[[nodiscard]] consteval auto get()
{
return V;
}
template<typename T, auto V1, auto V2, auto... Vs>
[[nodiscard]] consteval auto get()
{
if constexpr (is_same_v<T, std::remove_const_t<decltype(V1)>>)
return V1;
else
return get<T, V2, Vs...>();
}
template<template<typename...> typename T, typename T1>
requires is_specialization_of<T1, T>
[[nodiscard]] consteval auto get()
{
return T1{};
}
template<template<typename...> typename T, typename T1, typename T2, typename... Ts>
[[nodiscard]] consteval auto get()
{
if constexpr (is_specialization_of<T1, T>)
return T1{};
else
return get<T, T2, Ts...>();
}
template<template<auto...> typename T, typename T1>
requires is_specialization_of_v<T1, T>
[[nodiscard]] consteval auto get()
{
return T1{};
}
template<template<auto...> typename T, typename T1, typename T2, typename... Ts>
[[nodiscard]] consteval auto get()
{
if constexpr (is_specialization_of_v<T1, T>)
return T1{};
else
return get<T, T2, Ts...>();
}
} // namespace mp_units

View File

@@ -0,0 +1,86 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp-units/bits/expression_template.h>
#include <mp-units/bits/unit_concepts.h>
#include <mp-units/quantity_spec.h>
namespace mp_units::detail {
template<AssociatedUnit U>
[[nodiscard]] consteval auto all_are_kinds(U);
template<typename U, auto... Vs>
[[nodiscard]] consteval auto all_are_kinds(power<U, Vs...>)
{
return all_are_kinds(U{});
}
template<typename... Nums, typename... Dens>
[[nodiscard]] consteval bool all_are_kinds(type_list<Nums...>, type_list<Dens...>)
{
return (... && all_are_kinds(Nums{})) && (... && all_are_kinds(Dens{}));
}
template<AssociatedUnit U>
[[nodiscard]] consteval auto all_are_kinds(U)
{
if constexpr (requires { U::quantity_spec; })
return QuantityKindSpec<std::remove_const_t<decltype(U::quantity_spec)>>;
else if constexpr (requires { U::reference_unit; })
return all_are_kinds(U::reference_unit);
else if constexpr (requires { typename U::_num_; }) {
return all_are_kinds(typename U::_num_{}, typename U::_den_{});
}
}
template<AssociatedUnit U>
[[nodiscard]] consteval auto get_associated_quantity_impl(U u);
template<AssociatedUnit U>
using to_quantity_spec = std::remove_const_t<decltype(get_associated_quantity_impl(U{}))>;
template<AssociatedUnit U>
[[nodiscard]] consteval auto get_associated_quantity_impl(U u)
{
if constexpr (requires { U::quantity_spec; })
return remove_kind(U::quantity_spec);
else if constexpr (requires { U::reference_unit; })
return get_associated_quantity_impl(U::reference_unit);
else if constexpr (requires { typename U::_num_; }) {
return expr_map<to_quantity_spec, derived_quantity_spec, struct dimensionless, type_list_of_quantity_spec_less>(u);
}
}
template<AssociatedUnit U>
[[nodiscard]] consteval auto get_associated_quantity(U u)
{
constexpr bool all_kinds = all_are_kinds(u);
if constexpr (all_kinds)
return kind_of<get_associated_quantity_impl(u)>;
else
return get_associated_quantity_impl(u);
}
} // namespace mp_units::detail

View File

@@ -0,0 +1,91 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp-units/bits/quantity_spec_concepts.h>
namespace mp_units::detail {
[[nodiscard]] consteval std::size_t hierarchy_path_length(QuantitySpec auto q)
{
if constexpr (requires { q._parent_; })
return hierarchy_path_length(q._parent_) + 1;
else
return 1;
}
template<std::size_t Offset>
requires(Offset >= 0)
[[nodiscard]] consteval QuantitySpec auto hierarchy_path_advance(QuantitySpec auto q)
{
if constexpr (Offset == 0)
return q;
else if constexpr (requires { q._parent_; })
return hierarchy_path_advance<Offset - 1>(q._parent_);
}
template<QuantitySpec A, QuantitySpec B>
[[nodiscard]] consteval bool have_common_base_in_hierarchy_of_equal_length(A a, B b)
{
if constexpr (is_same_v<A, B>)
return true;
else if constexpr (requires { a._parent_; })
return have_common_base_in_hierarchy_of_equal_length(a._parent_, b._parent_);
else
return false;
}
template<QuantitySpec A, QuantitySpec B>
[[nodiscard]] consteval auto have_common_base(A a, B b)
{
constexpr int a_length = hierarchy_path_length(A{});
constexpr int b_length = hierarchy_path_length(B{});
if constexpr (a_length > b_length)
return have_common_base_in_hierarchy_of_equal_length(hierarchy_path_advance<a_length - b_length>(a), b);
else
return have_common_base_in_hierarchy_of_equal_length(a, hierarchy_path_advance<b_length - a_length>(b));
}
template<QuantitySpec A, QuantitySpec B>
requires(have_common_base_in_hierarchy_of_equal_length(A{}, B{}))
[[nodiscard]] consteval auto get_common_base_for_hierarchy_of_equal_length(A a, B b)
{
if constexpr (is_same_v<A, B>)
return a;
else
return get_common_base_for_hierarchy_of_equal_length(a._parent_, b._parent_);
}
template<QuantitySpec A, QuantitySpec B>
requires(have_common_base(A{}, B{}))
[[nodiscard]] consteval auto get_common_base(A a, B b)
{
constexpr int a_length = hierarchy_path_length(A{});
constexpr int b_length = hierarchy_path_length(B{});
if constexpr (a_length > b_length)
return get_common_base_for_hierarchy_of_equal_length(hierarchy_path_advance<a_length - b_length>(a), b);
else
return get_common_base_for_hierarchy_of_equal_length(a, hierarchy_path_advance<b_length - a_length>(b));
}
} // namespace mp_units::detail

View File

@@ -22,23 +22,74 @@
#pragma once #pragma once
#include <units/bits/external/hacks.h> #include <mp-units/bits/expression_template.h>
#include <units/bits/prime.h> #include <mp-units/bits/external/hacks.h>
#include <units/ratio.h> #include <mp-units/bits/external/type_name.h>
#include <mp-units/bits/external/type_traits.h>
#include <mp-units/bits/math_concepts.h>
#include <mp-units/bits/prime.h>
#include <mp-units/bits/ratio.h>
#include <mp-units/bits/symbol_text.h>
#include <mp-units/bits/text_tools.h>
#include <mp-units/customization_points.h>
#include <concepts> #include <concepts>
#include <cstdint> #include <cstdint>
#include <exception>
#include <numbers> #include <numbers>
#include <optional> #include <optional>
namespace units { namespace mp_units {
namespace detail { namespace detail {
// Higher numbers use fewer trial divisions, at the price of more storage space. // Higher numbers use fewer trial divisions, at the price of more storage space.
using factorizer = wheel_factorizer<4>; using factorizer = wheel_factorizer<4>;
} // namespace detail
namespace detail {
template<typename T>
inline constexpr bool is_magnitude = false;
template<typename T>
inline constexpr bool is_specialization_of_magnitude = false;
} // namespace detail } // namespace detail
/** /**
* @brief Any type which can be used as a basis vector in a BasePower. * @brief Concept to detect whether T is a valid Magnitude.
*/
template<typename T>
concept Magnitude = detail::is_magnitude<T>;
/**
* @brief A type to represent a standalone constant value.
*/
// template<auto V>
// struct constant {
// static constexpr auto value = V;
// };
// // is_derived_from_specialization_of_constant
// namespace detail {
// template<auto V>
// void to_base_specialization_of_constant(const volatile constant<V>*);
// template<typename T>
// inline constexpr bool is_derived_from_specialization_of_constant =
// requires(T * t) { to_base_specialization_of_constant(t); };
// } // namespace detail
// template<typename T>
// concept Constant = detail::is_derived_from_specialization_of_constant<T>;
// struct pi_v : constant<std::numbers::pi_v<long double>> {};
/**
* @brief Any type which can be used as a basis vector in a PowerV.
* *
* We have two categories. * We have two categories.
* *
@@ -52,9 +103,6 @@ using factorizer = wheel_factorizer<4>;
* The reason we can't hold the value directly for floating point bases is so that we can support some compilers (e.g., * The reason we can't hold the value directly for floating point bases is so that we can support some compilers (e.g.,
* GCC 10) which don't yet permit floating point NTTPs. * GCC 10) which don't yet permit floating point NTTPs.
*/ */
template<typename T>
concept BaseRep =
std::is_same_v<T, std::intmax_t> || std::is_same_v<std::remove_cvref_t<decltype(T::value)>, long double>;
/** /**
* @brief A basis vector in our magnitude representation, raised to some rational power. * @brief A basis vector in our magnitude representation, raised to some rational power.
@@ -80,72 +128,119 @@ concept BaseRep =
* _existing_ bases, including both prime numbers and any other irrational bases. For example, even though `sqrt(2)` is * _existing_ bases, including both prime numbers and any other irrational bases. For example, even though `sqrt(2)` is
* irrational, we must not ever use it as a base; instead, we would use `base_power{2, ratio{1, 2}}`. * irrational, we must not ever use it as a base; instead, we would use `base_power{2, ratio{1, 2}}`.
*/ */
template<BaseRep T>
struct base_power {
// The rational power to which the base is raised.
ratio power{1};
constexpr long double get_base() const { return T::value; }
};
/**
* @brief Specialization for prime number bases.
*/
template<>
struct base_power<std::intmax_t> {
// The value of the basis "vector". Must be prime to be used with `magnitude` (below).
std::intmax_t base;
// The rational power to which the base is raised.
ratio power{1};
constexpr std::intmax_t get_base() const { return base; }
};
/**
* @brief Deduction guides for base_power: only permit deducing integral bases.
*/
template<std::integral T, std::convertible_to<ratio> U>
base_power(T, U) -> base_power<std::intmax_t>;
template<std::integral T>
base_power(T) -> base_power<std::intmax_t>;
// Implementation for BasePower concept (below).
namespace detail { namespace detail {
template<typename T> template<typename T>
inline constexpr bool is_base_power = false; inline constexpr bool is_named_magnitude = Magnitude<T> && !detail::is_specialization_of_magnitude<T>;
template<BaseRep T>
inline constexpr bool is_base_power<base_power<T>> = true; }
template<typename T>
concept PowerVBase = one_of<T, int, std::intmax_t, long double> || detail::is_named_magnitude<T>;
// TODO Unify with `power` if UTPs (P1985) are accepted by the Committee
template<PowerVBase auto V, int Num, int... Den>
requires(detail::valid_ratio<Num, Den...> && !detail::ratio_one<Num, Den...>)
struct power_v {
static constexpr auto base = V;
static constexpr ratio exponent{Num, Den...};
};
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_power_v = false;
template<auto V, int... Ints>
inline constexpr bool is_specialization_of_power_v<power_v<V, Ints...>> = true;
} // namespace detail } // namespace detail
template<typename T>
concept MagnitudeSpec = PowerVBase<T> || detail::is_specialization_of_power_v<T>;
namespace detail {
template<MagnitudeSpec Element>
[[nodiscard]] consteval auto get_base(Element element)
{
if constexpr (detail::is_specialization_of_power_v<Element>)
return Element::base;
else
return element;
}
template<MagnitudeSpec Element>
[[nodiscard]] consteval ratio get_exponent(Element)
{
if constexpr (detail::is_specialization_of_power_v<Element>)
return Element::exponent;
else
return ratio{1};
}
} // namespace detail
/** /**
* @brief Concept to detect whether a _type_ is a valid base power. * @brief Concept to detect whether a _type_ is a valid base power.
* *
* Note that this is somewhat incomplete. We must also detect whether a _value_ of that type is valid for use with * Note that this is somewhat incomplete. We must also detect whether a _value_ of that type is valid for use with
* `magnitude<...>`. We will defer that second check to the constraints on the `magnitude` template. * `magnitude<...>`. We will defer that second check to the constraints on the `magnitude` template.
*/ */
template<typename T>
concept BasePower = detail::is_base_power<T>;
namespace detail { namespace detail {
constexpr auto inverse(BasePower auto bp) // We do not want magnitude type to have the `l` literal after a value for a small integral number.
// For example this modifies `magnitude<3l>` to be `magnitude<3>`
template<auto V>
[[nodiscard]] consteval auto shorten_T()
{ {
bp.power.num *= -1; if constexpr (std::integral<decltype(V)>) {
return bp; if constexpr (V <= std::numeric_limits<int>::max()) {
return static_cast<int>(V);
} else {
return static_cast<std::intmax_t>(V);
}
} else if constexpr (std::floating_point<decltype(V)>) {
return static_cast<long double>(V);
} else {
return V;
}
}
template<PowerVBase auto V, ratio R>
[[nodiscard]] consteval auto power_v_or_T()
{
constexpr auto shortT = shorten_T<V>();
if constexpr (R.den == 1) {
if constexpr (R.num == 1)
return shortT;
else
return power_v<shortT, R.num>{};
} else {
return power_v<shortT, R.num, R.den>{};
}
}
template<MagnitudeSpec M>
[[nodiscard]] consteval auto inverse(M)
{
return power_v_or_T<get_base(M{}), -1 * get_exponent(M{})>();
} }
// `widen_t` gives the widest arithmetic type in the same category, for intermediate computations. // `widen_t` gives the widest arithmetic type in the same category, for intermediate computations.
template<typename T> template<typename T>
using widen_t = using widen_t = conditional<std::is_arithmetic_v<T>,
std::conditional_t<std::is_arithmetic_v<T>, conditional<std::is_floating_point_v<T>, long double,
std::conditional_t<std::is_floating_point_v<T>, long double, conditional<std::is_signed_v<T>, std::intmax_t, std::uintmax_t>>,
std::conditional_t<std::is_signed_v<T>, std::intmax_t, std::uintmax_t>>, T>;
T>;
// Raise an arbitrary arithmetic type to a positive integer power at compile time. // Raise an arbitrary arithmetic type to a positive integer power at compile time.
template<typename T> template<typename T>
constexpr T int_power(T base, std::integral auto exp) [[nodiscard]] consteval T int_power(T base, std::integral auto exp)
{ {
// As this function should only be called at compile time, the terminations herein function as // As this function should only be called at compile time, the terminations herein function as
// "parameter-compatible static_asserts", and should not result in terminations at runtime. // "parameter-compatible static_asserts", and should not result in terminations at runtime.
@@ -182,27 +277,28 @@ constexpr T int_power(T base, std::integral auto exp)
template<typename T> template<typename T>
constexpr widen_t<T> compute_base_power(BasePower auto bp) [[nodiscard]] consteval widen_t<T> compute_base_power(MagnitudeSpec auto el)
{ {
// This utility can only handle integer powers. To compute rational powers at compile time, we'll // This utility can only handle integer powers. To compute rational powers at compile time, we'll
// need to write a custom function. // need to write a custom function.
// //
// Note that since this function should only be called at compile time, the point of these // Note that since this function should only be called at compile time, the point of these
// terminations is to act as "static_assert substitutes", not to actually terminate at runtime. // terminations is to act as "static_assert substitutes", not to actually terminate at runtime.
if (bp.power.den != 1) { const auto exp = get_exponent(el);
if (exp.den != 1) {
std::terminate(); // Rational powers not yet supported std::terminate(); // Rational powers not yet supported
} }
if (bp.power.num < 0) { if (exp.num < 0) {
if constexpr (std::is_integral_v<T>) { if constexpr (std::is_integral_v<T>) {
std::terminate(); // Cannot represent reciprocal as integer std::terminate(); // Cannot represent reciprocal as integer
} else { } else {
return T{1} / compute_base_power<T>(inverse(bp)); return T{1} / compute_base_power<T>(inverse(el));
} }
} }
auto power = bp.power.num; auto power = exp.num;
return int_power(static_cast<widen_t<T>>(bp.get_base()), power); return int_power(static_cast<widen_t<T>>(get_base(el)), power);
} }
// A converter for the value member variable of magnitude (below). // A converter for the value member variable of magnitude (below).
@@ -212,7 +308,7 @@ constexpr widen_t<T> compute_base_power(BasePower auto bp)
template<typename To, typename From> template<typename To, typename From>
// TODO(chogg): Migrate this to use `treat_as_floating_point`. // TODO(chogg): Migrate this to use `treat_as_floating_point`.
requires(!std::is_integral_v<To> || std::is_integral_v<From>) requires(!std::is_integral_v<To> || std::is_integral_v<From>)
constexpr To checked_static_cast(From x) [[nodiscard]] consteval To checked_static_cast(From x)
{ {
// This function should only ever be called at compile time. The purpose of these terminations is // This function should only ever be called at compile time. The purpose of these terminations is
// to produce compiler errors, because we cannot `static_assert` on function arguments. // to produce compiler errors, because we cannot `static_assert` on function arguments.
@@ -224,31 +320,15 @@ constexpr To checked_static_cast(From x)
return static_cast<To>(x); return static_cast<To>(x);
} }
} // namespace detail } // namespace detail
/**
* @brief Equality detection for two base powers.
*/
template<BasePower T, BasePower U>
constexpr bool operator==(T t, U u)
{
return std::is_same_v<T, U> && (t.get_base() == u.get_base()) && (t.power == u.power);
}
/**
* @brief A BasePower, raised to a rational power E.
*/
constexpr auto pow(BasePower auto bp, ratio p)
{
bp.power = bp.power * p;
return bp;
}
// A variety of implementation detail helpers. // A variety of implementation detail helpers.
namespace detail { namespace detail {
// The exponent of `factor` in the prime factorization of `n`. // The exponent of `factor` in the prime factorization of `n`.
constexpr std::intmax_t multiplicity(std::intmax_t factor, std::intmax_t n) [[nodiscard]] consteval std::intmax_t multiplicity(std::intmax_t factor, std::intmax_t n)
{ {
std::intmax_t m = 0; std::intmax_t m = 0;
while (n % factor == 0) { while (n % factor == 0) {
@@ -261,7 +341,7 @@ constexpr std::intmax_t multiplicity(std::intmax_t factor, std::intmax_t n)
// Divide a number by a given base raised to some power. // Divide a number by a given base raised to some power.
// //
// Undefined unless base > 1, pow >= 0, and (base ^ pow) evenly divides n. // Undefined unless base > 1, pow >= 0, and (base ^ pow) evenly divides n.
constexpr std::intmax_t remove_power(std::intmax_t base, std::intmax_t pow, std::intmax_t n) [[nodiscard]] consteval std::intmax_t remove_power(std::intmax_t base, std::intmax_t pow, std::intmax_t n)
{ {
while (pow-- > 0) { while (pow-- > 0) {
n /= base; n /= base;
@@ -270,181 +350,216 @@ constexpr std::intmax_t remove_power(std::intmax_t base, std::intmax_t pow, std:
} }
// A way to check whether a number is prime at compile time. // A way to check whether a number is prime at compile time.
constexpr bool is_prime(std::intmax_t n) { return (n >= 0) && factorizer::is_prime(static_cast<std::size_t>(n)); } // [[nodiscard]] consteval bool is_prime(std::intmax_t n)
// {
// return (n >= 0) && factorizer::is_prime(static_cast<std::size_t>(n));
// }
constexpr bool is_valid_base_power(const BasePower auto& bp) // template<MagnitudeSpec Element>
{ // [[nodiscard]] consteval bool is_valid_element(Element element)
if (bp.power == 0) { // {
return false; // if (get_exponent(element) == 0) {
} // return false;
// }
// if constexpr (std::integral<decltype(get_base(element))>) {
// // Some prime numbers are so big, that we can't check their primality without exhausting limits on constexpr
// steps
// // and/or iterations. We can still _perform_ the factorization for these by using the `known_first_factor`
// // workaround. However, we can't _check_ that they are prime, because this workaround depends on the input being
// // usable in a constexpr expression. This is true for `prime_factorization` (below), where the input `N` is a
// // template parameter, but is not true for our case, where the input `bp.get_base()` is a function parameter.
// (See
// // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1045r1.html for some background reading on this
// // distinction.)
// //
// // In our case: we simply give up on excluding every possible ill-formed base power, and settle for catching the
// // most likely and common mistakes.
// if (const bool too_big_to_check = (get_base_value(element) > 1'000'000'000)) {
// return true;
// }
if constexpr (std::is_same_v<decltype(bp.get_base()), std::intmax_t>) { // return is_prime(get_base_value(element));
// Some prime numbers are so big, that we can't check their primality without exhausting limits on constexpr steps // } else {
// and/or iterations. We can still _perform_ the factorization for these by using the `known_first_factor` // return get_base_value(element) > 0;
// workaround. However, we can't _check_ that they are prime, because this workaround depends on the input being // }
// usable in a constexpr expression. This is true for `prime_factorization` (below), where the input `N` is a // }
// template parameter, but is not true for our case, where the input `bp.get_base()` is a function parameter. (See
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1045r1.html for some background reading on this
// distinction.)
//
// In our case: we simply give up on excluding every possible ill-formed base power, and settle for catching the
// most likely and common mistakes.
if (const bool too_big_to_check = (bp.get_base() > 1'000'000'000)) {
return true;
}
return is_prime(bp.get_base());
} else {
return bp.get_base() > 0;
}
}
// A function object to apply a predicate to all consecutive pairs of values in a sequence. // A function object to apply a predicate to all consecutive pairs of values in a sequence.
template<typename Predicate> // template<typename Predicate>
struct pairwise_all { // struct pairwise_all {
Predicate predicate; // Predicate predicate;
template<typename... Ts> // template<typename... Ts>
constexpr bool operator()(Ts&&... ts) const // [[nodiscard]] consteval bool operator()(Ts&&... ts) const
{ // {
// Carefully handle different sizes, avoiding unsigned integer underflow. // // Carefully handle different sizes, avoiding unsigned integer underflow.
constexpr auto num_comparisons = [](auto num_elements) { // constexpr auto num_comparisons = [](auto num_elements) {
return (num_elements > 1) ? (num_elements - 1) : 0; // return (num_elements > 1) ? (num_elements - 1) : 0;
}(sizeof...(Ts)); // }(sizeof...(Ts));
// Compare zero or more pairs of neighbours as needed. // // Compare zero or more pairs of neighbours as needed.
return [this]<std::size_t... Is>(std::tuple<Ts...>&& t, std::index_sequence<Is...>) { // return [this]<std::size_t... Is>(std::tuple<Ts...> && t, std::index_sequence<Is...>)
return (predicate(std::get<Is>(t), std::get<Is + 1>(t)) && ...); // {
}(std::make_tuple(std::forward<Ts>(ts)...), std::make_index_sequence<num_comparisons>()); // return (predicate(std::get<Is>(t), std::get<Is + 1>(t)) && ...);
} // }
}; // (std::make_tuple(std::forward<Ts>(ts)...), std::make_index_sequence<num_comparisons>());
// }
// };
// Deduction guide: permit constructions such as `pairwise_all{std::less{}}`. // Deduction guide: permit constructions such as `pairwise_all{std::less{}}`.
template<typename T> // template<typename T>
pairwise_all(T) -> pairwise_all<T>; // pairwise_all(T) -> pairwise_all<T>;
// Check whether a sequence of (possibly heterogeneously typed) values are strictly increasing. // Check whether a sequence of (possibly heterogeneously typed) values are strictly increasing.
template<typename... Ts> // template<typename... Ts>
requires(std::is_signed_v<Ts> && ...) // requires(std::is_signed_v<Ts> && ...)
constexpr bool strictly_increasing(Ts&&... ts) // [[nodiscard]] consteval bool strictly_increasing(Ts&&... ts)
// {
// return pairwise_all{std::less{}}(std::forward<Ts>(ts)...);
// }
// template<MagnitudeSpec auto... Elements>
// inline constexpr bool all_elements_valid = (is_valid_element(Elements) && ...);
// template<MagnitudeSpec auto... Elements>
// inline constexpr bool all_elements_in_order = strictly_increasing(get_base_value(Elements)...);
// template<MagnitudeSpec auto... Elements>
// inline constexpr bool is_element_pack_valid = all_elements_valid<Elements...> && all_elements_in_order<Elements...>;
[[nodiscard]] consteval bool is_rational(MagnitudeSpec auto element)
{ {
return pairwise_all{std::less{}}(std::forward<Ts>(ts)...); static_assert(!Magnitude<decltype(element)>); // magnitudes are handles by another overload
return std::is_integral_v<decltype(get_base(element))> && (get_exponent(element).den == 1);
} }
template<BasePower auto... BPs> [[nodiscard]] consteval bool is_integral(MagnitudeSpec auto element)
inline constexpr bool all_base_powers_valid = (is_valid_base_power(BPs) && ...);
template<BasePower auto... BPs>
inline constexpr bool all_bases_in_order = strictly_increasing(BPs.get_base()...);
template<BasePower auto... BPs>
inline constexpr bool is_base_power_pack_valid = all_base_powers_valid<BPs...> && all_bases_in_order<BPs...>;
constexpr bool is_rational(BasePower auto bp)
{ {
return std::is_integral_v<decltype(bp.get_base())> && (bp.power.den == 1); static_assert(!Magnitude<decltype(element)>); // magnitudes are handles by another overload
return is_rational(element) && get_exponent(element).num > 0;
} }
constexpr bool is_integral(BasePower auto bp) { return is_rational(bp) && bp.power.num > 0; }
} // namespace detail } // namespace detail
/** /**
* @brief A representation for positive real numbers which optimizes taking products and rational powers. * @brief A representation for positive real numbers which optimizes taking products and rational powers.
* *
* Magnitudes can be treated as values. Each type encodes exactly one value. Users can multiply, divide, raise to * Magnitudes can be treated as values. Each type encodes exactly one value. Users can multiply, divide, raise to
* rational powers, and compare for equality. * rational powers, and compare for equality.
*/ */
template<BasePower auto... BPs> template<MagnitudeSpec auto... Ms>
requires detail::is_base_power_pack_valid<BPs...> // requires detail::is_element_pack_valid<Ms...>
struct magnitude { struct magnitude {
// Whether this magnitude represents an integer. [[nodiscard]] friend consteval bool is_integral(const magnitude&)
friend constexpr bool is_integral(const magnitude&) { return (detail::is_integral(BPs) && ...); } {
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
return (is_integral(Ms) && ...);
}
// Whether this magnitude represents a rational number. [[nodiscard]] friend consteval bool is_rational(const magnitude&)
friend constexpr bool is_rational(const magnitude&) { return (detail::is_rational(BPs) && ...); } {
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
return (is_rational(Ms) && ...);
}
}; };
// Implementation for Magnitude concept (below).
namespace detail { namespace detail {
template<auto... Ms>
void to_base_specialization_of_magnitude(const volatile magnitude<Ms...>*);
template<typename T> template<typename T>
inline constexpr bool is_magnitude = false; inline constexpr bool is_derived_from_specialization_of_magnitude =
template<auto... BPs> requires(T* t) { to_base_specialization_of_magnitude(t); };
inline constexpr bool is_magnitude<magnitude<BPs...>> = true;
template<typename T>
requires is_derived_from_specialization_of_magnitude<T>
inline constexpr bool is_magnitude<T> = true;
template<auto... Ms>
inline constexpr bool is_specialization_of_magnitude<magnitude<Ms...>> = true;
} // namespace detail } // namespace detail
/**
* @brief Concept to detect whether T is a valid Magnitude.
*/
template<typename T>
concept Magnitude = detail::is_magnitude<T>;
/** /**
* @brief The value of a Magnitude in a desired type T. * @brief The value of a Magnitude in a desired type T.
*/ */
template<typename T, auto... BPs> template<typename T, auto... Ms>
// TODO(chogg): Migrate this to use `treat_as_floating_point`. requires(is_integral(magnitude<Ms...>{})) || treat_as_floating_point<T>
requires(!std::is_integral_v<T> || is_integral(magnitude<BPs...>{})) constexpr T get_value(const magnitude<Ms...>&)
constexpr T get_value(const magnitude<BPs...>&)
{ {
// Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow. // Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow.
constexpr auto result = detail::checked_static_cast<T>((detail::compute_base_power<T>(BPs) * ... * T{1})); constexpr auto result = detail::checked_static_cast<T>((detail::compute_base_power<T>(Ms) * ... * T{1}));
return result; return result;
} }
/**
* @brief A base to represent pi.
*/
struct pi_base {
static constexpr long double value = std::numbers::pi_v<long double>;
};
/** /**
* @brief A convenient Magnitude constant for pi, which we can manipulate like a regular number. * @brief A convenient Magnitude constant for pi, which we can manipulate like a regular number.
*/ */
inline constexpr Magnitude auto mag_pi = magnitude<base_power<pi_base>{}>{}; inline constexpr struct mag_pi : magnitude<std::numbers::pi_v<long double>> {
} mag_pi;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude equality implementation. // Magnitude equality implementation.
template<auto... LeftBPs, auto... RightBPs> template<Magnitude M1, Magnitude M2>
constexpr bool operator==(magnitude<LeftBPs...>, magnitude<RightBPs...>) [[nodiscard]] consteval bool operator==(M1, M2)
{ {
if constexpr (sizeof...(LeftBPs) == sizeof...(RightBPs)) { return std::is_same_v<M1, M2>;
return ((LeftBPs == RightBPs) && ...);
} else {
return false;
}
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude rational powers implementation. // Magnitude rational powers implementation.
template<ratio E, auto... BPs> template<std::intmax_t Num, std::intmax_t Den = 1, auto... Ms>
constexpr auto pow(magnitude<BPs...>) [[nodiscard]] consteval auto pow(magnitude<Ms...>)
{ {
if constexpr (E.num == 0) { if constexpr (Num == 0) {
return magnitude<>{}; return magnitude<>{};
} else { } else {
return magnitude<pow(BPs, E)...>{}; return magnitude<detail::power_v_or_T<detail::get_base(Ms), detail::get_exponent(Ms) * ratio{Num, Den}>()...>{};
} }
} }
template<auto... BPs> template<auto... Ms>
constexpr auto sqrt(magnitude<BPs...> m) [[nodiscard]] consteval auto sqrt(magnitude<Ms...> m)
{ {
return pow<ratio{1, 2}>(m); return pow<1, 2>(m);
} }
template<auto... BPs> template<auto... Ms>
constexpr auto cbrt(magnitude<BPs...> m) [[nodiscard]] consteval auto cbrt(magnitude<Ms...> m)
{ {
return pow<ratio{1, 3}>(m); return pow<1, 3>(m);
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude product implementation. // Magnitude product implementation.
namespace detail {
consteval bool less(MagnitudeSpec auto lhs, MagnitudeSpec auto rhs)
{
using lhs_base_t = decltype(get_base(lhs));
using rhs_base_t = decltype(get_base(rhs));
if constexpr (is_named_magnitude<lhs_base_t> && is_named_magnitude<rhs_base_t>)
return type_name<lhs_base_t>() < type_name<rhs_base_t>();
else if constexpr (!is_named_magnitude<lhs_base_t> && !is_named_magnitude<rhs_base_t>)
return get_base(lhs) < get_base(rhs);
else
return is_named_magnitude<lhs_base_t>;
}
} // namespace detail
// Base cases, for when either (or both) inputs are the identity. // Base cases, for when either (or both) inputs are the identity.
constexpr Magnitude auto operator*(magnitude<>, magnitude<>) { return magnitude<>{}; } constexpr Magnitude auto operator*(magnitude<>, magnitude<>) { return magnitude<>{}; }
constexpr Magnitude auto operator*(magnitude<>, Magnitude auto m) { return m; } constexpr Magnitude auto operator*(magnitude<>, Magnitude auto m) { return m; }
@@ -452,37 +567,38 @@ constexpr Magnitude auto operator*(Magnitude auto m, magnitude<>) { return m; }
// Recursive case for the product of any two non-identity Magnitudes. // Recursive case for the product of any two non-identity Magnitudes.
template<auto H1, auto... T1, auto H2, auto... T2> template<auto H1, auto... T1, auto H2, auto... T2>
constexpr Magnitude auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>) [[nodiscard]] consteval Magnitude auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>)
{ {
// Case for when H1 has the smaller base. using namespace detail;
if constexpr (H1.get_base() < H2.get_base()) {
if constexpr (less(H1, H2)) {
if constexpr (sizeof...(T1) == 0) { if constexpr (sizeof...(T1) == 0) {
// Shortcut for the "pure prepend" case, which makes it easier to implement some of the other cases. // Shortcut for the "pure prepend" case, which makes it easier to implement some of the other cases.
return magnitude<H1, H2, T2...>{}; return magnitude<H1, H2, T2...>{};
} else { } else {
return magnitude<H1>{} * (magnitude<T1...>{} * magnitude<H2, T2...>{}); return magnitude<H1>{} * (magnitude<T1...>{} * magnitude<H2, T2...>{});
} }
} } else if constexpr (less(H2, H1)) {
// Case for when H2 has the smaller base.
if constexpr (H1.get_base() > H2.get_base()) {
return magnitude<H2>{} * (magnitude<H1, T1...>{} * magnitude<T2...>{}); return magnitude<H2>{} * (magnitude<H1, T1...>{} * magnitude<T2...>{});
} } else {
if constexpr (is_same_v<decltype(get_base(H1)), decltype(get_base(H2))>) {
constexpr auto partial_product = magnitude<T1...>{} * magnitude<T2...>{};
if constexpr (get_exponent(H1) + get_exponent(H2) == 0) {
return partial_product;
} else {
// Make a new power_v with the common base of H1 and H2, whose power is their powers' sum.
constexpr auto new_head = power_v_or_T<get_base(H1), get_exponent(H1) + get_exponent(H2)>();
// "Same leading base" case. if constexpr (get_exponent(new_head) == 0) {
if constexpr (H1.get_base() == H2.get_base()) { return partial_product;
constexpr auto partial_product = magnitude<T1...>{} * magnitude<T2...>{}; } else {
return magnitude<new_head>{} * partial_product;
// Make a new base_power with the common base of H1 and H2, whose power is their powers' sum. }
constexpr auto new_head = [&](auto head) { }
head.power = H1.power + H2.power; } else if constexpr (is_named_magnitude<decltype(get_base(H1))>) {
return head; return magnitude<H1>{} * (magnitude<T1...>{} * magnitude<H2, T2...>{});
}(H1);
if constexpr (new_head.power == 0) {
return partial_product;
} else { } else {
return magnitude<new_head>{} * partial_product; return magnitude<H2>{} * (magnitude<H1, T1...>{} * magnitude<T2...>{});
} }
} }
} }
@@ -490,7 +606,7 @@ constexpr Magnitude auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude quotient implementation. // Magnitude quotient implementation.
constexpr auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1>(r); } [[nodiscard]] consteval auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1>(r); }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude numerator and denominator implementation. // Magnitude numerator and denominator implementation.
@@ -498,19 +614,15 @@ constexpr auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1
namespace detail { namespace detail {
// The largest integer which can be extracted from any magnitude with only a single basis vector. // The largest integer which can be extracted from any magnitude with only a single basis vector.
template<auto BP> template<auto M>
constexpr auto integer_part(magnitude<BP>) [[nodiscard]] consteval auto integer_part(magnitude<M>)
{ {
constexpr auto power_num = BP.power.num; constexpr auto power_num = get_exponent(M).num;
constexpr auto power_den = BP.power.den; constexpr auto power_den = get_exponent(M).den;
if constexpr (std::is_integral_v<decltype(BP.get_base())> && (power_num >= power_den)) { if constexpr (std::is_integral_v<decltype(get_base(M))> && (power_num >= power_den)) {
constexpr auto largest_integer_power = [=](BasePower auto bp) { // largest integer power
bp.power = (power_num / power_den); // Note: integer division intended. return magnitude<power_v_or_T<get_base(M), power_num / power_den>()>{}; // Note: integer division intended
return bp;
}(BP); // Note: lambda is immediately invoked.
return magnitude<largest_integer_power>{};
} else { } else {
return magnitude<>{}; return magnitude<>{};
} }
@@ -518,13 +630,13 @@ constexpr auto integer_part(magnitude<BP>)
} // namespace detail } // namespace detail
template<auto... BPs> template<auto... Ms>
constexpr auto numerator(magnitude<BPs...>) [[nodiscard]] consteval auto numerator(magnitude<Ms...>)
{ {
return (detail::integer_part(magnitude<BPs>{}) * ... * magnitude<>{}); return (detail::integer_part(magnitude<Ms>{}) * ... * magnitude<>{});
} }
constexpr auto denominator(Magnitude auto m) { return numerator(pow<-1>(m)); } [[nodiscard]] consteval auto denominator(Magnitude auto m) { return numerator(pow<-1>(m)); }
// Implementation of conversion to ratio goes here, because it needs `numerator()` and `denominator()`. // Implementation of conversion to ratio goes here, because it needs `numerator()` and `denominator()`.
constexpr ratio as_ratio(Magnitude auto m) constexpr ratio as_ratio(Magnitude auto m)
@@ -556,44 +668,50 @@ constexpr ratio as_ratio(Magnitude auto m)
// minimum power for each base (where absent bases implicitly have a power of 0). // minimum power for each base (where absent bases implicitly have a power of 0).
namespace detail { namespace detail {
template<auto BP>
constexpr auto remove_positive_power(magnitude<BP> m) template<auto M>
[[nodiscard]] consteval auto remove_positive_power(magnitude<M> m)
{ {
if constexpr (BP.power.num < 0) { if constexpr (get_exponent(M).num < 0) {
return m; return m;
} else { } else {
return magnitude<>{}; return magnitude<>{};
} }
} }
template<auto... BPs> template<auto... Ms>
constexpr auto remove_positive_powers(magnitude<BPs...>) [[nodiscard]] consteval auto remove_positive_powers(magnitude<Ms...>)
{ {
return (magnitude<>{} * ... * remove_positive_power(magnitude<BPs>{})); return (magnitude<>{} * ... * remove_positive_power(magnitude<Ms>{}));
} }
} // namespace detail
// Base cases, for when either (or both) inputs are the identity. // Base cases, for when either (or both) inputs are the identity.
constexpr auto common_magnitude(magnitude<>, magnitude<>) { return magnitude<>{}; } [[nodiscard]] consteval auto common_magnitude(magnitude<>, magnitude<>) { return magnitude<>{}; }
constexpr auto common_magnitude(magnitude<>, Magnitude auto m) { return detail::remove_positive_powers(m); } [[nodiscard]] consteval auto common_magnitude(magnitude<>, Magnitude auto m)
constexpr auto common_magnitude(Magnitude auto m, magnitude<>) { return detail::remove_positive_powers(m); } {
return detail::remove_positive_powers(m);
}
[[nodiscard]] consteval auto common_magnitude(Magnitude auto m, magnitude<>)
{
return detail::remove_positive_powers(m);
}
// Recursive case for the common Magnitude of any two non-identity Magnitudes. // Recursive case for the common Magnitude of any two non-identity Magnitudes.
template<auto H1, auto... T1, auto H2, auto... T2> template<auto H1, auto... T1, auto H2, auto... T2>
constexpr auto common_magnitude(magnitude<H1, T1...>, magnitude<H2, T2...>) [[nodiscard]] consteval auto common_magnitude(magnitude<H1, T1...>, magnitude<H2, T2...>)
{ {
using detail::remove_positive_power; using detail::remove_positive_power;
if constexpr (H1.get_base() < H2.get_base()) { if constexpr (detail::get_base(H1) < detail::get_base(H2)) {
// When H1 has the smaller base, prepend to result from recursion. // When H1 has the smaller base, prepend to result from recursion.
return remove_positive_power(magnitude<H1>{}) * common_magnitude(magnitude<T1...>{}, magnitude<H2, T2...>{}); return remove_positive_power(magnitude<H1>{}) * common_magnitude(magnitude<T1...>{}, magnitude<H2, T2...>{});
} else if constexpr (H2.get_base() < H1.get_base()) { } else if constexpr (detail::get_base(H2) < detail::get_base(H1)) {
// When H2 has the smaller base, prepend to result from recursion. // When H2 has the smaller base, prepend to result from recursion.
return remove_positive_power(magnitude<H2>{}) * common_magnitude(magnitude<H1, T1...>{}, magnitude<T2...>{}); return remove_positive_power(magnitude<H2>{}) * common_magnitude(magnitude<H1, T1...>{}, magnitude<T2...>{});
} else { } else {
// When the bases are equal, pick whichever has the lower power. // When the bases are equal, pick whichever has the lower power.
constexpr auto common_tail = common_magnitude(magnitude<T1...>{}, magnitude<T2...>{}); constexpr auto common_tail = common_magnitude(magnitude<T1...>{}, magnitude<T2...>{});
if constexpr (H1.power < H2.power) { if constexpr (detail::get_exponent(H1) < detail::get_exponent(H2)) {
return magnitude<H1>{} * common_tail; return magnitude<H1>{} * common_tail;
} else { } else {
return magnitude<H2>{} * common_tail; return magnitude<H2>{} * common_tail;
@@ -601,6 +719,18 @@ constexpr auto common_magnitude(magnitude<H1, T1...>, magnitude<H2, T2...>)
} }
} }
template<auto... Ms>
[[nodiscard]] consteval auto common_magnitude_type_impl(magnitude<Ms...>)
{
return (... * decltype(get_base(Ms)){}) * std::intmax_t{};
}
// Returns the most precise type to express the magnitude factor
template<Magnitude auto M>
using common_magnitude_type = decltype(common_magnitude_type_impl(M));
} // namespace detail
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// `mag()` implementation. // `mag()` implementation.
@@ -614,11 +744,12 @@ template<std::intmax_t N>
inline constexpr std::optional<std::intmax_t> known_first_factor = std::nullopt; inline constexpr std::optional<std::intmax_t> known_first_factor = std::nullopt;
namespace detail { namespace detail {
// Helper to perform prime factorization at compile time. // Helper to perform prime factorization at compile time.
template<std::intmax_t N> template<std::intmax_t N>
requires(N > 0) requires gt_zero<N>
struct prime_factorization { struct prime_factorization {
static constexpr std::intmax_t get_or_compute_first_factor() [[nodiscard]] static consteval std::intmax_t get_or_compute_first_factor()
{ {
if constexpr (known_first_factor<N>.has_value()) { if constexpr (known_first_factor<N>.has_value()) {
return known_first_factor<N>.value(); return known_first_factor<N>.value();
@@ -632,7 +763,7 @@ struct prime_factorization {
static constexpr std::intmax_t remainder = remove_power(first_base, first_power, N); static constexpr std::intmax_t remainder = remove_power(first_base, first_power, N);
static constexpr auto value = static constexpr auto value =
magnitude<base_power{first_base, first_power}>{} * prime_factorization<remainder>::value; magnitude<power_v_or_T<first_base, ratio{first_power}>()>{} * prime_factorization<remainder>::value;
}; };
// Specialization for the prime factorization of 1 (base case). // Specialization for the prime factorization of 1 (base case).
@@ -643,6 +774,7 @@ struct prime_factorization<1> {
template<std::intmax_t N> template<std::intmax_t N>
inline constexpr auto prime_factorization_v = prime_factorization<N>::value; inline constexpr auto prime_factorization_v = prime_factorization<N>::value;
} // namespace detail } // namespace detail
/** /**
@@ -652,42 +784,74 @@ inline constexpr auto prime_factorization_v = prime_factorization<N>::value;
* manually adding base powers. * manually adding base powers.
*/ */
template<ratio R> template<ratio R>
requires(R.num > 0) requires detail::gt_zero<R.num>
constexpr Magnitude auto mag() inline constexpr Magnitude auto mag = detail::prime_factorization_v<R.num> / detail::prime_factorization_v<R.den>;
{
return detail::prime_factorization_v<R.num> / detail::prime_factorization_v<R.den>;
}
/** /**
* @brief Create a Magnitude which is some rational number raised to a rational power. * @brief Create a Magnitude which is some rational number raised to a rational power.
*/ */
template<ratio Base, ratio Pow> template<ratio Base, ratio Pow>
requires(Base.num > 0) requires detail::gt_zero<Base.num>
constexpr Magnitude auto mag_power() inline constexpr Magnitude auto mag_power = pow<Pow.num, Pow.den>(mag<Base>);
{
return pow<Pow>(mag<Base>());
}
namespace detail { namespace detail {
template<typename T, BasePower auto... BPs>
constexpr ratio get_power(T base, magnitude<BPs...>) template<typename T, auto... Ms>
[[nodiscard]] consteval ratio get_power(T base, magnitude<Ms...>)
{ {
return ((BPs.get_base() == base ? BPs.power : ratio{0}) + ... + ratio{0}); return ((get_base(Ms) == base ? get_exponent(Ms) : ratio{0}) + ... + ratio{0});
} }
constexpr std::intmax_t integer_part(ratio r) { return r.num / r.den; } [[nodiscard]] consteval std::intmax_t integer_part(ratio r) { return r.num / r.den; }
constexpr std::intmax_t extract_power_of_10(Magnitude auto m) [[nodiscard]] consteval std::intmax_t extract_power_of_10(Magnitude auto m)
{ {
const auto power_of_2 = get_power(2, m); const auto power_of_2 = get_power(2, m);
const auto power_of_5 = get_power(5, m); const auto power_of_5 = get_power(5, m);
if ((power_of_2 * power_of_5).num <= 0) { if ((power_of_2 * power_of_5).num <= 0) return 0;
return 0;
}
return integer_part((detail::abs(power_of_2) < detail::abs(power_of_5)) ? power_of_2 : power_of_5); return integer_part((detail::abs(power_of_2) < detail::abs(power_of_5)) ? power_of_2 : power_of_5);
} }
} // namespace detail
} // namespace units inline constexpr basic_symbol_text base_multiplier("× 10", "x 10");
template<Magnitude auto M>
[[nodiscard]] consteval auto magnitude_text()
{
constexpr auto exp10 = extract_power_of_10(M);
constexpr Magnitude auto base = M / mag_power<10, exp10>;
constexpr Magnitude auto num = numerator(base);
constexpr Magnitude auto den = denominator(base);
// TODO address the below
static_assert(base == num / den, "Printing rational powers, or irrational bases, not yet supported");
constexpr auto num_value = get_value<std::intmax_t>(num);
constexpr auto den_value = get_value<std::intmax_t>(den);
if constexpr (num_value == 1 && den_value == 1 && exp10 != 0) {
return base_multiplier + superscript<exp10>();
} else if constexpr (num_value != 1 || den_value != 1 || exp10 != 0) {
auto txt = basic_fixed_string("[") + regular<num_value>();
if constexpr (den_value == 1) {
if constexpr (exp10 == 0) {
return txt + basic_fixed_string("]");
} else {
return txt + " " + base_multiplier + superscript<exp10>() + basic_fixed_string("]");
}
} else {
if constexpr (exp10 == 0) {
return txt + basic_fixed_string("/") + regular<den_value>() + basic_fixed_string("]");
} else {
return txt + basic_fixed_string("/") + regular<den_value>() + " " + base_multiplier + superscript<exp10>() +
basic_fixed_string("]");
}
}
} else {
return basic_fixed_string("");
}
}
} // namespace detail
} // namespace mp_units

View File

@@ -24,12 +24,12 @@
#include <cstdint> #include <cstdint>
namespace units::detail { namespace mp_units::detail {
template<std::intmax_t N> template<auto N>
concept gt_zero = (N > 0); concept gt_zero = (N > 0);
template<std::intmax_t N> template<auto N>
concept non_zero = (N != 0); concept non_zero = (N != 0);
} // namespace units::detail } // namespace mp_units::detail

View File

@@ -22,7 +22,7 @@
#pragma once #pragma once
#include <units/bits/algorithm.h> #include <mp-units/bits/algorithm.h>
#include <array> #include <array>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
@@ -30,9 +30,9 @@
#include <optional> #include <optional>
#include <tuple> #include <tuple>
namespace units::detail { namespace mp_units::detail {
constexpr bool is_prime_by_trial_division(std::uintmax_t n) [[nodiscard]] consteval bool is_prime_by_trial_division(std::uintmax_t n)
{ {
for (std::uintmax_t f = 2; f * f <= n; f += 1 + (f % 2)) { for (std::uintmax_t f = 2; f * f <= n; f += 1 + (f % 2)) {
if (n % f == 0) { if (n % f == 0) {
@@ -45,7 +45,7 @@ constexpr bool is_prime_by_trial_division(std::uintmax_t n)
// Return the first factor of n, as long as it is either k or n. // Return the first factor of n, as long as it is either k or n.
// //
// Precondition: no integer smaller than k evenly divides n. // Precondition: no integer smaller than k evenly divides n.
constexpr std::optional<std::uintmax_t> first_factor_maybe(std::uintmax_t n, std::uintmax_t k) [[nodiscard]] constexpr std::optional<std::uintmax_t> first_factor_maybe(std::uintmax_t n, std::uintmax_t k)
{ {
if (n % k == 0) { if (n % k == 0) {
return k; return k;
@@ -57,7 +57,7 @@ constexpr std::optional<std::uintmax_t> first_factor_maybe(std::uintmax_t n, std
} }
template<std::size_t N> template<std::size_t N>
constexpr std::array<std::uintmax_t, N> first_n_primes() [[nodiscard]] consteval std::array<std::uintmax_t, N> first_n_primes()
{ {
std::array<std::uintmax_t, N> primes; std::array<std::uintmax_t, N> primes;
primes[0] = 2; primes[0] = 2;
@@ -71,7 +71,7 @@ constexpr std::array<std::uintmax_t, N> first_n_primes()
} }
template<std::size_t N, typename Callable> template<std::size_t N, typename Callable>
constexpr void call_for_coprimes_up_to(std::uintmax_t n, const std::array<std::uintmax_t, N>& basis, Callable&& call) consteval void call_for_coprimes_up_to(std::uintmax_t n, const std::array<std::uintmax_t, N>& basis, Callable&& call)
{ {
for (std::uintmax_t i = 0u; i < n; ++i) { for (std::uintmax_t i = 0u; i < n; ++i) {
if (std::apply([&i](auto... primes) { return ((i % primes != 0) && ...); }, basis)) { if (std::apply([&i](auto... primes) { return ((i % primes != 0) && ...); }, basis)) {
@@ -81,7 +81,7 @@ constexpr void call_for_coprimes_up_to(std::uintmax_t n, const std::array<std::u
} }
template<std::size_t N> template<std::size_t N>
constexpr std::size_t num_coprimes_up_to(std::uintmax_t n, const std::array<std::uintmax_t, N>& basis) [[nodiscard]] consteval std::size_t num_coprimes_up_to(std::uintmax_t n, const std::array<std::uintmax_t, N>& basis)
{ {
std::size_t count = 0u; std::size_t count = 0u;
call_for_coprimes_up_to(n, basis, [&count](auto) { ++count; }); call_for_coprimes_up_to(n, basis, [&count](auto) { ++count; });
@@ -89,7 +89,7 @@ constexpr std::size_t num_coprimes_up_to(std::uintmax_t n, const std::array<std:
} }
template<std::size_t ResultSize, std::size_t N> template<std::size_t ResultSize, std::size_t N>
constexpr auto coprimes_up_to(std::size_t n, const std::array<std::uintmax_t, N>& basis) [[nodiscard]] consteval auto coprimes_up_to(std::size_t n, const std::array<std::uintmax_t, N>& basis)
{ {
std::array<std::uintmax_t, ResultSize> coprimes; std::array<std::uintmax_t, ResultSize> coprimes;
std::size_t i = 0u; std::size_t i = 0u;
@@ -100,7 +100,7 @@ constexpr auto coprimes_up_to(std::size_t n, const std::array<std::uintmax_t, N>
} }
template<std::size_t N> template<std::size_t N>
constexpr std::uintmax_t product(const std::array<std::uintmax_t, N>& values) [[nodiscard]] consteval std::uintmax_t product(const std::array<std::uintmax_t, N>& values)
{ {
std::uintmax_t product = 1; std::uintmax_t product = 1;
for (const auto& v : values) { for (const auto& v : values) {
@@ -135,7 +135,7 @@ struct wheel_factorizer {
static constexpr auto coprimes_in_first_wheel = static constexpr auto coprimes_in_first_wheel =
coprimes_up_to<num_coprimes_up_to(wheel_size, basis)>(wheel_size, basis); coprimes_up_to<num_coprimes_up_to(wheel_size, basis)>(wheel_size, basis);
static constexpr std::uintmax_t find_first_factor(std::uintmax_t n) [[nodiscard]] static consteval std::uintmax_t find_first_factor(std::uintmax_t n)
{ {
if (const auto k = detail::get_first_of(basis, [&](auto p) { return first_factor_maybe(n, p); })) return *k; if (const auto k = detail::get_first_of(basis, [&](auto p) { return first_factor_maybe(n, p); })) return *k;
@@ -150,7 +150,7 @@ struct wheel_factorizer {
return n; return n;
} }
static constexpr bool is_prime(std::size_t n) { return (n > 1) && find_first_factor(n) == n; } [[nodiscard]] static consteval bool is_prime(std::size_t n) { return (n > 1) && find_first_factor(n) == n; }
}; };
} // namespace units::detail } // namespace mp_units::detail

View File

@@ -22,23 +22,34 @@
#pragma once #pragma once
#include "hacks.h" #include <mp-units/bits/quantity_concepts.h>
#include <mp-units/reference.h>
#include <type_traits> #include <type_traits>
namespace units { namespace mp_units {
template<typename BaseType> /**
struct downcast_base { * @brief Explicit cast of a quantity type
using downcast_base_type = BaseType; *
}; * This cast converts only a quantity type. It might be used to force some quantity type
* conversions that are not implicitly allowed but are allowed explicitly.
*
* For example:
*
* @code{.cpp}
* auto length = isq::length(42 * m);
* auto distance = quantity_cast<isq::distance>(length);
* @endcode
*
* @note This cast does not affect the underlying value of a number stored in a quantity.
*
* @tparam ToQS a quantity specification to use for a target quantity
*/
template<QuantitySpec auto ToQS, typename Q>
[[nodiscard]] constexpr Quantity auto quantity_cast(Q&& q)
requires Quantity<std::remove_cvref_t<Q>> && (castable(q.quantity_spec, ToQS))
{
return make_quantity<reference<ToQS, q.unit>{}>(std::forward<Q>(q).number());
}
template<typename T> } // namespace mp_units
using downcast_base_t = T::downcast_base_type;
template<typename T>
struct downcast_traits : std::type_identity<T> {};
template<typename T>
using downcast_traits_t = downcast_traits<T>::type;
} // namespace units

View File

@@ -0,0 +1,81 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp-units/bits/quantity_spec_concepts.h>
#include <mp-units/bits/reference_concepts.h>
#include <mp-units/bits/representation_concepts.h>
#include <mp-units/customization_points.h>
namespace mp_units {
template<Reference auto R, RepresentationOf<get_quantity_spec(R).character> Rep>
class quantity;
namespace detail {
template<auto R, typename Rep>
void to_base_specialization_of_quantity(const volatile quantity<R, Rep>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_quantity =
requires(T* t) { to_base_specialization_of_quantity(t); };
} // namespace detail
/**
* @brief A concept matching all quantities in the library
*
* Satisfied by all types being a either specialization or derived from `quantity`
*/
template<typename T>
concept Quantity = detail::is_derived_from_specialization_of_quantity<T>;
/**
* @brief A concept matching all quantities with provided dimension or quantity spec
*
* Satisfied by all quantities with a dimension/quantity_spec being the instantiation derived from
* the provided dimension/quantity_spec type.
*/
template<typename Q, auto V>
concept QuantityOf = Quantity<Q> && ReferenceOf<std::remove_const_t<decltype(Q::reference)>, V>;
/**
* @brief A concept matching all external quantities like types
*
* Satisfied by all external types (not-defined in mp-units) that via a `quantity_like_traits` provide
* all quantity-specific information.
*/
template<typename T>
concept QuantityLike = requires(T q) {
quantity_like_traits<T>::reference;
typename quantity_like_traits<T>::rep;
requires Reference<std::remove_const_t<decltype(quantity_like_traits<T>::reference)>>;
requires RepresentationOf<typename quantity_like_traits<T>::rep,
get_quantity_spec(quantity_like_traits<T>::reference).character>;
{
quantity_like_traits<T>::number(q)
} -> std::convertible_to<typename quantity_like_traits<T>::rep>;
};
} // namespace mp_units

View File

@@ -0,0 +1,128 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp-units/bits/quantity_spec_concepts.h>
#include <mp-units/bits/reference_concepts.h>
#include <mp-units/bits/representation_concepts.h>
#include <mp-units/customization_points.h>
namespace mp_units {
template<QuantitySpec auto Q>
struct absolute_point_origin;
namespace detail {
template<typename T>
inline constexpr bool is_quantity_point = false;
template<auto Q>
void to_base_specialization_of_absolute_point_origin(const volatile absolute_point_origin<Q>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_absolute_point_origin =
requires(T* t) { to_base_specialization_of_absolute_point_origin(t); };
} // namespace detail
/**
* @brief A concept matching all quantity points in the library
*
* Satisfied by all types being a either specialization or derived from `quantity_point`
*/
template<typename T>
concept QuantityPoint = detail::is_quantity_point<T>;
/**
* @brief A concept matching all quantity point origins in the library
*
* Satisfied by either quantity points or by all types derived from `absolute_point_origin` class template.
*/
template<typename T>
concept PointOrigin = QuantityPoint<T> || detail::is_derived_from_specialization_of_absolute_point_origin<T>;
/**
* @brief A concept matching all quantity point origins for a specified quantity type in the library
*
* Satisfied by all quantity point origins that are defined using a provided quantity specification.
*/
template<typename T, auto Q>
concept PointOriginFor =
PointOrigin<T> && QuantitySpec<std::remove_const_t<decltype(Q)>> && implicitly_convertible(Q, T::quantity_spec);
template<Reference auto R, PointOriginFor<get_quantity_spec(R)> auto PO,
RepresentationOf<get_quantity_spec(R).character> Rep>
class quantity_point;
namespace detail {
template<auto R, auto PO, typename Rep>
void to_base_specialization_of_quantity_point(const volatile quantity_point<R, PO, Rep>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_quantity_point =
requires(T* t) { to_base_specialization_of_quantity_point(t); };
template<typename T>
requires is_derived_from_specialization_of_quantity_point<T>
inline constexpr bool is_quantity_point<T> = true;
} // namespace detail
/**
* @brief A concept matching all quantity points with provided dimension or quantity spec
*
* Satisfied by all quantity points with a dimension/quantity_spec being the instantiation derived from
* the provided dimension/quantity_spec type, or quantity points having the origin with the same
* `absolute_point_origin`.
*/
template<typename QP, auto V>
concept QuantityPointOf =
QuantityPoint<QP> &&
(ReferenceOf<std::remove_const_t<decltype(QP::reference)>, V> ||
(PointOrigin<std::remove_const_t<decltype(V)>> &&
std::convertible_to<std::remove_const_t<decltype(QP::absolute_point_origin)>, std::remove_const_t<decltype(V)>>));
/**
* @brief A concept matching all external quantity point like types
*
* Satisfied by all external types (not-defined in mp-units) that via a `quantity_point_like_traits` provide
* all quantity_point-specific information.
*/
template<typename T>
concept QuantityPointLike = requires(T q) {
quantity_point_like_traits<T>::reference;
quantity_point_like_traits<T>::point_origin;
typename quantity_point_like_traits<T>::rep;
requires Reference<std::remove_const_t<decltype(quantity_point_like_traits<T>::reference)>>;
requires PointOrigin<std::remove_const_t<decltype(quantity_point_like_traits<T>::point_origin)>>;
requires RepresentationOf<typename quantity_point_like_traits<T>::rep,
get_quantity_spec(quantity_point_like_traits<T>::reference).character>;
requires std::constructible_from<
typename quantity_point<quantity_point_like_traits<T>::reference, quantity_point_like_traits<T>::point_origin,
typename quantity_point_like_traits<T>::rep>::quantity_type,
decltype(quantity_point_like_traits<T>::relative(q))>;
};
} // namespace mp_units

View File

@@ -0,0 +1,167 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp-units/bits/dimension_concepts.h>
#include <mp-units/bits/expression_template.h>
namespace mp_units {
#ifdef __cpp_explicit_this_parameter
template<auto...>
#else
template<typename, auto...>
#endif
struct quantity_spec;
template<auto Q>
struct kind_of_;
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_kind_of = false;
template<auto Q>
inline constexpr bool is_specialization_of_kind_of<kind_of_<Q>> = true;
template<typename T>
concept QuantityKindSpec = is_specialization_of_kind_of<T>;
#ifdef __cpp_explicit_this_parameter
template<auto... Args>
void to_base_specialization_of_quantity_spec(const volatile quantity_spec<Args...>*);
#else
template<typename T, auto... Args>
void to_base_specialization_of_quantity_spec(const volatile quantity_spec<T, Args...>*);
#endif
template<typename T>
inline constexpr bool is_derived_from_specialization_of_quantity_spec =
requires(T* t) { to_base_specialization_of_quantity_spec(t); };
#ifdef __cpp_explicit_this_parameter
template<BaseDimension auto Dim, auto... Args>
template<auto... Args>
void to_base_specialization_of_base_quantity_spec(const volatile quantity_spec<Dim, Args...>*);
#else
template<typename Self, BaseDimension auto Dim, auto... Args>
void to_base_specialization_of_base_quantity_spec(const volatile quantity_spec<Self, Dim, Args...>*);
#endif
template<typename T>
inline constexpr bool is_derived_from_specialization_of_base_quantity_spec =
requires(T* t) { to_base_specialization_of_base_quantity_spec(t); };
template<typename T>
inline constexpr bool is_specialization_of_quantity_spec = false;
#ifdef __cpp_explicit_this_parameter
template<auto... Args>
inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<Args...>> = true;
#else
template<typename T, auto... Args>
inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<T, Args...>> = true;
#endif
/**
* @brief Concept matching all named quantity specification types
*
* Satisfied by all types that derive from `quantity_spec`.
*/
template<typename T>
concept NamedQuantitySpec = is_derived_from_specialization_of_quantity_spec<T> &&
(!is_specialization_of_quantity_spec<T>)&&(!QuantityKindSpec<T>);
/**
* @brief Concept matching all named base quantity specification types
*
* Satisfied by all types that derive from `quantity_spec` taking a base dimension
* as a template parameter.
*/
template<typename T>
concept BaseQuantitySpec = NamedQuantitySpec<T> && is_derived_from_specialization_of_base_quantity_spec<T>;
template<typename T>
struct is_dimensionless : std::false_type {};
template<typename T>
inline constexpr bool is_power_of_quantity_spec = requires {
requires is_specialization_of_power<T> &&
(NamedQuantitySpec<typename T::factor> || is_dimensionless<typename T::factor>::value);
};
template<typename T>
inline constexpr bool is_per_of_quantity_specs = false;
template<typename... Ts>
inline constexpr bool is_per_of_quantity_specs<per<Ts...>> =
(... && (NamedQuantitySpec<Ts> || is_dimensionless<Ts>::value || is_power_of_quantity_spec<Ts>));
template<typename T>
concept IntermediateDerivedQuantitySpecExpr =
detail::NamedQuantitySpec<T> || detail::is_dimensionless<T>::value || detail::is_power_of_quantity_spec<T> ||
detail::is_per_of_quantity_specs<T>;
} // namespace detail
template<detail::IntermediateDerivedQuantitySpecExpr... Expr>
struct derived_quantity_spec;
namespace detail {
/**
* @brief Concept matching all derived quantity specification types
*
* Satisfied by all `derived_quantity_spec` specializations.
*
* @note Deriving a strong type from it is considered a logic error and thus is
* explicitly not supported here.
*/
template<typename T>
concept IntermediateDerivedQuantitySpec =
is_specialization_of<T, derived_quantity_spec> ||
(QuantityKindSpec<T> &&
is_specialization_of<std::remove_const_t<decltype(T::_quantity_spec_)>, derived_quantity_spec>);
} // namespace detail
template<typename T>
concept QuantitySpec =
detail::NamedQuantitySpec<T> || detail::IntermediateDerivedQuantitySpec<T> || detail::QuantityKindSpec<T>;
template<QuantitySpec Q>
[[nodiscard]] consteval QuantitySpec auto get_kind(Q q);
namespace detail {
template<auto To, auto From>
concept DerivedFromQuantityKindSpecOf =
QuantitySpec<std::remove_const_t<decltype(From)>> && QuantitySpec<std::remove_const_t<decltype(To)>> &&
get_kind(From) != get_kind(To) &&
std::derived_from<std::remove_cvref_t<decltype(To)>, std::remove_cvref_t<decltype(get_kind(From))>>;
}
} // namespace mp_units

Some files were not shown because too many files have changed in this diff Show More