forked from mpusz/mp-units
Merge commit 'ec04d9c03ddf12e46dca5348d4ca57b526e425f1'
This commit is contained in:
@@ -132,8 +132,11 @@ IncludeCategories:
|
||||
# InsertTrailingCommas: None
|
||||
# IntegerLiteralSeparator:
|
||||
# Binary: 0
|
||||
# BinaryMinDigits: 0
|
||||
# Decimal: 0
|
||||
# DecimalMinDigits: 0
|
||||
# Hex: 0
|
||||
# HexMinDigits: 0
|
||||
# JavaScriptQuotes: Leave
|
||||
# JavaScriptWrapImports: true
|
||||
# KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
|
||||
@@ -35,7 +35,7 @@ env:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.config.name }} ${{ matrix.build_type }} [downcast=${{ matrix.downcast_mode }}]
|
||||
name: ${{ matrix.config.name }} ${{ matrix.build_type }}
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -190,7 +190,6 @@ jobs:
|
||||
conan-config: "",
|
||||
}
|
||||
build_type: ["Release", "Debug"]
|
||||
downcast_mode: ["on", "auto"]
|
||||
|
||||
env:
|
||||
CC: ${{ matrix.config.compiler.cc }}
|
||||
@@ -214,7 +213,7 @@ jobs:
|
||||
- uses: hendrikmuhs/ccache-action@v1.2
|
||||
if: runner.os == 'Linux'
|
||||
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
|
||||
- name: Install gcc-12
|
||||
if: matrix.config.compiler.type == 'GCC' && matrix.config.compiler.version == '12'
|
||||
@@ -282,7 +281,7 @@ jobs:
|
||||
run: |
|
||||
conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \
|
||||
-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
|
||||
id: get-package-ref
|
||||
shell: bash
|
||||
|
||||
+12
-3
@@ -36,8 +36,7 @@ vscode:
|
||||
- vivaxy.vscode-conventional-commits
|
||||
- hbenl.vscode-test-explorer
|
||||
- matepek.vscode-catch2-test-adapter
|
||||
- trond-snekvik.simple-rst
|
||||
- lextudio.restructuredtext
|
||||
- redhat.vscode-yaml
|
||||
- ritwickdey.liveserver
|
||||
- ms-python.python
|
||||
|
||||
@@ -60,7 +59,17 @@ tasks:
|
||||
"restructuredtext.preview.scrollPreviewWithEditor": false,
|
||||
"liveServer.settings.root": "/build/docs/docs/sphinx/",
|
||||
"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
|
||||
|
||||
@@ -11,7 +11,7 @@ repos:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v16.0.0
|
||||
rev: v16.0.4
|
||||
hooks:
|
||||
- id: clang-format
|
||||
- repo: https://github.com/cheshirekow/cmake-format-precommit
|
||||
|
||||
+8
-2
@@ -27,6 +27,9 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||
|
||||
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
|
||||
include(modern_project_structure)
|
||||
ensure_entry_point()
|
||||
@@ -44,6 +47,7 @@ add_compile_definitions($<$<CONFIG:Debug>:gsl_CONFIG_CONTRACT_CHECKING_AUDIT>)
|
||||
|
||||
# enable include-what-you-use
|
||||
option(${projectPrefix}IWYU "Enables include-what-you-use" OFF)
|
||||
|
||||
if(${projectPrefix}IWYU)
|
||||
include(include-what-you-use)
|
||||
enable_iwyu(
|
||||
@@ -52,12 +56,13 @@ if(${projectPrefix}IWYU)
|
||||
MAX_LINE_LENGTH 120
|
||||
NO_COMMENTS
|
||||
)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(${projectPrefix}AS_SYSTEM_HEADERS ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#enable_clang_tidy()
|
||||
# enable_clang_tidy()
|
||||
|
||||
# add project code
|
||||
add_subdirectory(src)
|
||||
@@ -66,10 +71,11 @@ add_subdirectory(src)
|
||||
add_subdirectory(example)
|
||||
|
||||
# generate project documentation
|
||||
add_subdirectory(docs)
|
||||
# add_subdirectory(docs)
|
||||
|
||||
# add unit tests
|
||||
enable_testing()
|
||||
|
||||
add_subdirectory(test)
|
||||
|
||||
# tests for standalone headers
|
||||
|
||||
@@ -115,6 +115,7 @@ function(add_header_test target)
|
||||
set(source "${CMAKE_CURRENT_BINARY_DIR}/headers/${directory}/${filename}.cpp")
|
||||
if(NOT EXISTS "${source}")
|
||||
file(WRITE "${source}" "#include <${header}>")
|
||||
file(APPEND "${source}" "\n#include <${header}>") # do it twice to ensure that header guards are provided
|
||||
endif()
|
||||
list(APPEND sources "${source}")
|
||||
endforeach()
|
||||
|
||||
+25
-25
@@ -54,8 +54,6 @@ class MPUnitsConan(ConanFile):
|
||||
license = "MIT"
|
||||
url = "https://github.com/mpusz/units"
|
||||
settings = "os", "arch", "compiler", "build_type"
|
||||
options = {"downcast_mode": ["off", "on", "auto"]}
|
||||
default_options = {"downcast_mode": "on"}
|
||||
exports = ["LICENSE.md"]
|
||||
exports_sources = [
|
||||
"docs/*",
|
||||
@@ -86,7 +84,7 @@ class MPUnitsConan(ConanFile):
|
||||
|
||||
@property
|
||||
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
|
||||
def _use_libfmt(self):
|
||||
@@ -117,7 +115,7 @@ class MPUnitsConan(ConanFile):
|
||||
|
||||
def build_requirements(self):
|
||||
if self._build_all:
|
||||
self.test_requires("catch2/3.1.0")
|
||||
self.test_requires("catch2/3.3.2")
|
||||
if not self._skip_la:
|
||||
self.test_requires("wg21-linear_algebra/0.7.3")
|
||||
if not self._skip_docs:
|
||||
@@ -144,7 +142,6 @@ class MPUnitsConan(ConanFile):
|
||||
|
||||
def generate(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_DOCS"] = self._build_all and not self._skip_docs
|
||||
tc.variables["UNITS_USE_LIBFMT"] = self._use_libfmt
|
||||
@@ -188,29 +185,32 @@ class MPUnitsConan(ConanFile):
|
||||
self.cpp_info.components["core-fmt"].requires = ["core"]
|
||||
if self._use_libfmt:
|
||||
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-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-cgs"].requires = ["si"]
|
||||
self.cpp_info.components["si-fps"].requires = ["si-international"]
|
||||
self.cpp_info.components["si-hep"].requires = ["si"]
|
||||
self.cpp_info.components["si-iau"].requires = ["si"]
|
||||
self.cpp_info.components["si-imperial"].requires = ["si"]
|
||||
self.cpp_info.components["si-international"].requires = ["si"]
|
||||
self.cpp_info.components["si-typographic"].requires = ["si"]
|
||||
self.cpp_info.components["si-uscs"].requires = ["si"]
|
||||
self.cpp_info.components["isq-iec80000"].requires = ["si"]
|
||||
self.cpp_info.components["cgs"].requires = ["si"]
|
||||
self.cpp_info.components["hep"].requires = ["si"]
|
||||
self.cpp_info.components["iau"].requires = ["si"]
|
||||
self.cpp_info.components["imperial"].requires = ["si"]
|
||||
self.cpp_info.components["international"].requires = ["si"]
|
||||
self.cpp_info.components["typographic"].requires = ["usc"]
|
||||
self.cpp_info.components["usc"].requires = ["international"]
|
||||
self.cpp_info.components["iec80000"].requires = ["isq", "si"]
|
||||
self.cpp_info.components["systems"].requires = [
|
||||
"isq",
|
||||
"isq-natural",
|
||||
"angular",
|
||||
"isq_angular",
|
||||
"natural",
|
||||
"si",
|
||||
"si-cgs",
|
||||
"si-fps",
|
||||
"si-hep",
|
||||
"si-iau",
|
||||
"si-imperial",
|
||||
"si-international",
|
||||
"si-typographic",
|
||||
"si-uscs",
|
||||
"isq-iec80000",
|
||||
"cgs",
|
||||
"hep",
|
||||
"iau",
|
||||
"imperial",
|
||||
"international",
|
||||
"typographic",
|
||||
"usc",
|
||||
"iec80000",
|
||||
]
|
||||
|
||||
@@ -6,6 +6,11 @@ quantity
|
||||
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
|
||||
`std::chrono::duration <https://en.cppreference.com/w/cpp/chrono/duration>`_.
|
||||
The difference is that it uses ``double`` as a default representation and has
|
||||
|
||||
@@ -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_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.
|
||||
|
||||
@@ -436,3 +436,18 @@ class template::
|
||||
|
||||
To learn more about unknown units please refer to the
|
||||
: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.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
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
|
||||
|
||||
+32
-9
@@ -33,17 +33,40 @@ function(add_example target)
|
||||
target_link_libraries(${target} PRIVATE ${ARGN})
|
||||
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(custom_systems mp-units::core-io mp-units::si)
|
||||
add_example(hello_units mp-units::core-fmt mp-units::core-io mp-units::si mp-units::si-international)
|
||||
add_example(currency mp-units::core-io)
|
||||
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(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)
|
||||
endif()
|
||||
|
||||
add_subdirectory(aliases)
|
||||
add_subdirectory(glide_computer)
|
||||
add_subdirectory(kalman_filter)
|
||||
add_subdirectory(literals)
|
||||
add_subdirectory(references)
|
||||
|
||||
@@ -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()
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -20,53 +20,49 @@
|
||||
// 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 <mp-units/iostream.h>
|
||||
#include <mp-units/systems/cgs/cgs.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 <exception>
|
||||
#include <iostream>
|
||||
|
||||
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,
|
||||
si::time<si::second, int> t)
|
||||
constexpr quantity<si::metre / si::second, int> fixed_int_si_avg_speed(quantity<si::metre, int> d,
|
||||
quantity<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)
|
||||
constexpr quantity<si::metre / si::second> fixed_double_si_avg_speed(quantity<si::metre> d, quantity<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)
|
||||
constexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d, QuantityOf<isq::time> auto 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>
|
||||
template<QuantityOf<isq::length> D, QuantityOf<isq::time> T, QuantityOf<isq::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);
|
||||
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
|
||||
<< ".\n";
|
||||
}
|
||||
|
||||
void example()
|
||||
{
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
|
||||
// SI (int)
|
||||
{
|
||||
using namespace units::isq::si::references;
|
||||
constexpr auto distance = 220 * km;
|
||||
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_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::references;
|
||||
constexpr auto distance = 220. * km;
|
||||
constexpr auto duration = 2. * 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_int_si_avg_speed(value_cast<int>(distance), value_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::time_references;
|
||||
using namespace units::isq::si::international::length_references;
|
||||
using namespace mp_units::international::unit_symbols;
|
||||
|
||||
constexpr auto distance = 140 * mi;
|
||||
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
|
||||
// (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, si_avg_speed(distance, duration));
|
||||
print_result(distance, duration, avg_speed(distance, duration));
|
||||
}
|
||||
|
||||
// Customary Units (double)
|
||||
{
|
||||
using namespace units::isq::si::time_references;
|
||||
using namespace units::isq::si::international::length_references;
|
||||
using namespace mp_units::international::unit_symbols;
|
||||
|
||||
constexpr auto distance = 140. * mi;
|
||||
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
|
||||
// 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_int_si_avg_speed(value_cast<int>(value_cast<si::metre>(distance)), value_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::time_references;
|
||||
using namespace units::isq::si::cgs::length_references;
|
||||
constexpr auto distance = 22'000'000 * cm;
|
||||
constexpr auto duration = 2 * h;
|
||||
constexpr auto distance = 22'000'000 * cgs::centimetre;
|
||||
constexpr auto duration = 7200 * cgs::second;
|
||||
|
||||
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_int_si_avg_speed(value_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::time_references;
|
||||
using namespace units::isq::si::cgs::length_references;
|
||||
constexpr auto distance = 22'000'000. * cm;
|
||||
constexpr auto duration = 2. * h;
|
||||
constexpr auto distance = 22'000'000. * cgs::centimetre;
|
||||
constexpr auto duration = 7200. * cgs::second;
|
||||
|
||||
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_int_si_avg_speed(value_cast<int>(value_cast<m>(distance)), value_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));
|
||||
}
|
||||
}
|
||||
@@ -20,47 +20,42 @@
|
||||
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 <mp-units/iostream.h>
|
||||
#include <mp-units/math.h> // IWYU pragma: keep
|
||||
#include <mp-units/systems/isq/electromagnetism.h>
|
||||
#include <mp-units/systems/si/si.h>
|
||||
#include <iostream>
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace units::isq;
|
||||
using namespace units::isq::si::capacitance_references;
|
||||
using namespace units::isq::si::voltage_references;
|
||||
using namespace units::isq::si::resistance_references;
|
||||
using namespace units::isq::si::time_references;
|
||||
using namespace mp_units;
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
|
||||
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 * uF;
|
||||
constexpr auto V0 = 5.0 * V;
|
||||
constexpr auto RR = 4.7 * kR; // cannot use just 'R' here
|
||||
constexpr auto C = isq::capacitance(0.47 * uF);
|
||||
constexpr auto V0 = isq::voltage(5.0 * V);
|
||||
constexpr auto R = isq::resistance(4.7 * si::kilo<si::ohm>);
|
||||
|
||||
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 ";
|
||||
|
||||
if (Vt >= 1 * V)
|
||||
std::cout << Vt;
|
||||
std::cout << Vt[V];
|
||||
else if (Vt >= 1 * mV)
|
||||
std::cout << quantity_cast<si::millivolt>(Vt);
|
||||
std::cout << Vt[mV];
|
||||
else if (Vt >= 1 * uV)
|
||||
std::cout << quantity_cast<si::microvolt>(Vt);
|
||||
std::cout << Vt[uV];
|
||||
else if (Vt >= 1 * nV)
|
||||
std::cout << quantity_cast<si::nanovolt>(Vt);
|
||||
std::cout << Vt[nV];
|
||||
else
|
||||
std::cout << quantity_cast<si::picovolt>(Vt);
|
||||
std::cout << Vt[pV];
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -15,8 +15,10 @@
|
||||
along with this program. If not, see http://www.gnu.org/licenses./
|
||||
*/
|
||||
|
||||
#include <units/format.h>
|
||||
#include <units/isq/si/length.h>
|
||||
#include <mp-units/format.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 <type_traits>
|
||||
|
||||
@@ -27,27 +29,24 @@
|
||||
|
||||
namespace {
|
||||
|
||||
template<units::Quantity Target, units::Quantity Source>
|
||||
requires units::equivalent<typename Source::dimension, typename Target::dimension>
|
||||
inline constexpr std::common_type_t<typename Target::rep, typename Source::rep> conversion_factor(Target, Source)
|
||||
template<mp_units::Quantity Target, mp_units::Quantity Source>
|
||||
requires std::constructible_from<Target, Source>
|
||||
inline constexpr double conversion_factor(Target, Source)
|
||||
{
|
||||
// get quantities looking like inputs but with Q::rep that doesn't have narrowing conversion
|
||||
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();
|
||||
return value_cast<Target::unit>(1. * Source::reference).number();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
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";
|
||||
|
||||
constexpr length<metre> lengthA(2.0);
|
||||
constexpr length<millimetre> lengthB = lengthA;
|
||||
constexpr auto lengthA = 2.0 * m;
|
||||
constexpr auto lengthB = lengthA[mm];
|
||||
|
||||
std::cout << UNITS_STD_FMT::format("lengthA( {} ) and lengthB( {} )\n", lengthA, lengthB)
|
||||
<< "represent the same length in different units.\n\n";
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -21,11 +21,11 @@
|
||||
// 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 <mp-units/bits/fmt_hacks.h>
|
||||
#include <mp-units/chrono.h>
|
||||
#include <mp-units/math.h>
|
||||
#include <mp-units/systems/international/international.h>
|
||||
#include <mp-units/systems/si/unit_symbols.h>
|
||||
#include <array>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
@@ -36,41 +36,39 @@
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace geographic;
|
||||
using namespace glide_computer;
|
||||
|
||||
using namespace units::isq;
|
||||
using namespace mp_units;
|
||||
|
||||
auto get_gliders()
|
||||
{
|
||||
using namespace units::aliases::isq::si;
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
UNITS_DIAGNOSTIC_PUSH
|
||||
UNITS_DIAGNOSTIC_IGNORE_MISSING_BRACES
|
||||
static const std::array gliders = {
|
||||
glider{"SZD-30 Pirat", {velocity(km_per_h<>(83)), rate_of_climb(m_per_s<>(-0.7389))}},
|
||||
glider{"SZD-51 Junior", {velocity(km_per_h<>(80)), rate_of_climb(m_per_s<>(-0.6349))}},
|
||||
glider{"SZD-48 Jantar Std 3", {velocity(km_per_h<>(110)), rate_of_climb(m_per_s<>(-0.77355))}},
|
||||
glider{"SZD-56 Diana", {velocity(km_per_h<>(110)), rate_of_climb(m_per_s<>(-0.63657))}}};
|
||||
static const std::array gliders = {glider{"SZD-30 Pirat", {83 * (km / h), -0.7389 * (m / s)}},
|
||||
glider{"SZD-51 Junior", {80 * (km / h), -0.6349 * (m / s)}},
|
||||
glider{"SZD-48 Jantar Std 3", {110 * (km / h), -0.77355 * (m / s)}},
|
||||
glider{"SZD-56 Diana", {110 * (km / h), -0.63657 * (m / s)}}};
|
||||
UNITS_DIAGNOSTIC_POP
|
||||
return gliders;
|
||||
}
|
||||
|
||||
auto get_weather_conditions()
|
||||
{
|
||||
using namespace units::aliases::isq::si;
|
||||
static const std::array weather_conditions = {
|
||||
std::pair("Good", weather{height(m<>(1900)), rate_of_climb(m_per_s<>(4.3))}),
|
||||
std::pair("Medium", weather{height(m<>(1550)), rate_of_climb(m_per_s<>(2.8))}),
|
||||
std::pair("Bad", weather{height(m<>(850)), rate_of_climb(m_per_s<>(1.8))})};
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
static const std::array weather_conditions = {std::pair{"Good", weather{1900 * m, 4.3 * (m / s)}},
|
||||
std::pair{"Medium", weather{1550 * m, 2.8 * (m / s)}},
|
||||
std::pair{"Bad", weather{850 * m, 1.8 * (m / s)}}};
|
||||
return weather_conditions;
|
||||
}
|
||||
|
||||
auto get_waypoints()
|
||||
{
|
||||
using namespace geographic::literals;
|
||||
using namespace units::aliases::isq::si::international;
|
||||
using namespace mp_units::international::unit_symbols;
|
||||
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{"EPGI", {53.52442_N, 18.84947_E}, altitude(ft<>(115))} // N53°31'27.9" E18°50'58.1"
|
||||
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}, msl_altitude{115. * ft}} // N53°31'27.9" E18°50'58.1"
|
||||
};
|
||||
return waypoints;
|
||||
}
|
||||
@@ -85,9 +83,12 @@ void print(const R& 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]));
|
||||
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,
|
||||
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";
|
||||
}
|
||||
@@ -126,12 +127,12 @@ void print(const task& t)
|
||||
|
||||
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 << "- Length: " << UNITS_STD_FMT::format("{:%.1Q %q}", t.get_distance()) << "\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 << UNITS_STD_FMT::format(" * {} -> {} ({:%.1Q %q})\n", l.begin().name, l.end().name, l.get_distance());
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
@@ -155,14 +156,14 @@ void print(const aircraft_tow& tow)
|
||||
|
||||
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 waypoints = get_waypoints();
|
||||
const auto weather_conditions = get_weather_conditions();
|
||||
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
|
||||
// set `start_time` to 11:00 am today
|
||||
const timestamp start_time(std::chrono::system_clock::now());
|
||||
@@ -22,6 +22,6 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
add_library(glide_computer STATIC include/geographic.h glide_computer.cpp include/glide_computer.h)
|
||||
target_link_libraries(glide_computer PRIVATE mp-units::core-fmt PUBLIC mp-units::si example_utils)
|
||||
target_include_directories(glide_computer PUBLIC include)
|
||||
add_library(glide_computer_lib STATIC glide_computer.cpp include/glide_computer.h)
|
||||
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_lib PUBLIC include)
|
||||
|
||||
@@ -21,13 +21,14 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include "glide_computer.h"
|
||||
#include <mp-units/format.h>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <string_view>
|
||||
|
||||
namespace glide_computer {
|
||||
|
||||
using namespace units::isq;
|
||||
using namespace mp_units;
|
||||
|
||||
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;
|
||||
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);
|
||||
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 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.
|
||||
// y = -x / glide_ratio + pos.alt;
|
||||
// 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;
|
||||
return distance((ground_alt + s.min_agl_height - pos.alt).common() /
|
||||
((ground_alt - t.get_finish().alt) / dist_to_finish - 1 / glide_ratio(g.polar[0])));
|
||||
const auto dist_to_finish = t.get_distance() - pos.dist;
|
||||
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]));
|
||||
}
|
||||
|
||||
} // 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(
|
||||
"| {:<12} | {:>9%.1Q %q} (Total: {:>9%.1Q %q}) | {:>8%.1Q %q} (Total: {:>8%.1Q %q}) | {:>7%.0Q %q} ({:>6%.0Q %q}) "
|
||||
"|\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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
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};
|
||||
|
||||
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 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 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};
|
||||
|
||||
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 alt = ground_alt + s.min_agl_height;
|
||||
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),
|
||||
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)
|
||||
{
|
||||
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 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};
|
||||
|
||||
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
|
||||
pos = tow(start_ts, pos, at);
|
||||
|
||||
// estimate the 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]));
|
||||
// estimate the msl_altitude needed to reach the finish line from this place
|
||||
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?
|
||||
height height_to_gain = final_glide_alt - pos.alt;
|
||||
|
||||
@@ -22,17 +22,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "geographic.h"
|
||||
#include <units/isq/si/length.h>
|
||||
#include <units/isq/si/speed.h>
|
||||
#include <units/isq/si/time.h>
|
||||
#include <units/quantity_point_kind.h>
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include <units/chrono.h>
|
||||
#include <units/format.h>
|
||||
#include <units/math.h> // IWYU pragma: keep
|
||||
#include <mp-units/chrono.h>
|
||||
#include <mp-units/math.h> // IWYU pragma: keep
|
||||
#include <mp-units/quantity_point.h>
|
||||
#include <mp-units/systems/isq/space_and_time.h>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
@@ -56,67 +50,25 @@
|
||||
// - no ground obstacles (i.e. mountains) to pass
|
||||
// - 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 {
|
||||
|
||||
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
|
||||
QUANTITY_SPEC(rate_of_climb_speed, mp_units::isq::speed, mp_units::isq::height / mp_units::isq::time);
|
||||
|
||||
// length
|
||||
using distance = units::quantity_kind<horizontal_kind, units::isq::si::kilometre>;
|
||||
using height = units::quantity_kind<vertical_kind, units::isq::si::metre>;
|
||||
using altitude = units::quantity_point_kind<vertical_point_kind, units::isq::si::metre>;
|
||||
using distance = mp_units::quantity<mp_units::isq::distance[mp_units::si::kilo<mp_units::si::metre>]>;
|
||||
using height = mp_units::quantity<mp_units::isq::height[mp_units::si::metre]>;
|
||||
|
||||
// time
|
||||
using duration = units::isq::si::time<units::isq::si::second>;
|
||||
using timestamp = units::quantity_point<units::clock_origin<std::chrono::system_clock>, units::isq::si::second>;
|
||||
using duration = mp_units::quantity<mp_units::isq::duration[mp_units::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
|
||||
using velocity = units::quantity_kind<velocity_kind, units::isq::si::kilometre_per_hour>;
|
||||
using rate_of_climb = units::quantity_kind<rate_of_climb_kind, units::isq::si::metre_per_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");
|
||||
}
|
||||
};
|
||||
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 = mp_units::quantity<rate_of_climb_speed[mp_units::si::metre / mp_units::si::second]>;
|
||||
|
||||
// definition of glide computer databases and utilities
|
||||
namespace glide_computer {
|
||||
|
||||
struct glider {
|
||||
struct polar_point {
|
||||
velocity v;
|
||||
@@ -127,7 +79,10 @@ struct glider {
|
||||
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 {
|
||||
height cloud_base;
|
||||
@@ -137,7 +92,7 @@ struct weather {
|
||||
struct waypoint {
|
||||
std::string name;
|
||||
geographic::position<long double> pos;
|
||||
altitude alt;
|
||||
geographic::msl_altitude alt;
|
||||
};
|
||||
|
||||
class task {
|
||||
@@ -152,7 +107,7 @@ public:
|
||||
leg(const waypoint& b, const waypoint& e) noexcept : begin_(&b), end_(&e) {}
|
||||
constexpr const waypoint& begin() const { return *begin_; };
|
||||
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>;
|
||||
|
||||
@@ -170,7 +125,7 @@ public:
|
||||
const waypoint& get_start() const { return waypoints_.front(); }
|
||||
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
|
||||
{
|
||||
@@ -203,21 +158,26 @@ struct aircraft_tow {
|
||||
|
||||
struct flight_point {
|
||||
timestamp ts;
|
||||
altitude alt;
|
||||
geographic::msl_altitude alt;
|
||||
std::size_t leg_idx = 0;
|
||||
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; }
|
||||
|
||||
inline units::isq::si::length<units::isq::si::kilometre> length_3d(distance dist, height h)
|
||||
constexpr height agl(geographic::msl_altitude glider_alt, geographic::msl_altitude terrain_level)
|
||||
{
|
||||
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,
|
||||
const aircraft_tow& at);
|
||||
|
||||
+19
-24
@@ -20,37 +20,32 @@
|
||||
// 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/international/length.h>
|
||||
#include <units/isq/si/international/speed.h> // IWYU pragma: keep
|
||||
#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 <mp-units/format.h>
|
||||
#include <mp-units/iostream.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 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()
|
||||
{
|
||||
using namespace units::isq::si::literals;
|
||||
using namespace units::isq::si::references;
|
||||
using namespace units::aliases::isq::si::international;
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
using namespace mp_units::international::unit_symbols;
|
||||
|
||||
constexpr Speed auto v1 = 110 * (km / h);
|
||||
constexpr Speed auto v2 = mi_per_h<>(70.);
|
||||
constexpr Speed auto v3 = avg_speed(220_q_km, 2_q_h);
|
||||
constexpr Speed auto v4 = avg_speed(si::length<si::international::mile>(140), si::time<si::hour>(2));
|
||||
#if UNITS_DOWNCAST_MODE == 0
|
||||
constexpr Speed auto v5 = quantity_cast<si::speed<si::metre_per_second>>(v3);
|
||||
constexpr Speed auto v6 = quantity_cast<si::dim_speed, si::metre_per_second>(v4);
|
||||
#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);
|
||||
constexpr auto v1 = 110 * (km / h);
|
||||
constexpr auto v2 = 70 * mph;
|
||||
constexpr auto v3 = avg_speed(220. * km, 2 * h);
|
||||
constexpr auto v4 = avg_speed(isq::distance(140. * mi), 2 * isq::duration[h]);
|
||||
constexpr auto v5 = v3[m / s];
|
||||
constexpr auto v6 = value_cast<m / s>(v4);
|
||||
constexpr auto v7 = value_cast<int>(v6);
|
||||
|
||||
std::cout << v1 << '\n'; // 110 km/h
|
||||
std::cout << v2 << '\n'; // 70 mi/h
|
||||
|
||||
@@ -23,26 +23,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "ranged_representation.h"
|
||||
#include <units/bits/external/hacks.h>
|
||||
#include <units/bits/fmt_hacks.h>
|
||||
#include <units/generic/angle.h>
|
||||
#include <units/isq/si/length.h>
|
||||
#include <units/quantity_kind.h>
|
||||
#include <mp-units/bits/fmt_hacks.h>
|
||||
#include <mp-units/format.h>
|
||||
#include <mp-units/math.h>
|
||||
#include <mp-units/quantity.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 <numbers>
|
||||
#include <ostream>
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include <compare>
|
||||
// IWYU pragma: end_exports
|
||||
namespace geographic {
|
||||
|
||||
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 {
|
||||
|
||||
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>
|
||||
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>
|
||||
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 {
|
||||
|
||||
constexpr auto operator"" _N(long double v) { return latitude<long double>(latitude<long double>::rep(v)); }
|
||||
constexpr auto operator"" _S(long double v) { return latitude<long double>(latitude<long double>::rep(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)
|
||||
constexpr latitude<long double> operator"" _N(long double v) { return latitude<long double>{v * mp_units::si::degree}; }
|
||||
constexpr latitude<long double> operator"" _S(long double v)
|
||||
{
|
||||
gsl_ExpectsAudit(std::in_range<std::int64_t>(v));
|
||||
return latitude<std::int64_t>(latitude<std::int64_t>::rep(static_cast<std::int64_t>(v)));
|
||||
return latitude<long double>{-v * mp_units::si::degree};
|
||||
}
|
||||
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 latitude<std::int64_t>(-latitude<std::int64_t>::rep(static_cast<std::int64_t>(v)));
|
||||
return longitude<long double>{v * mp_units::si::degree};
|
||||
}
|
||||
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<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)));
|
||||
return longitude<long double>{-v * mp_units::si::degree};
|
||||
}
|
||||
|
||||
} // namespace literals
|
||||
@@ -129,8 +146,7 @@ struct UNITS_STD_FMT::formatter<geographic::longitude<T>> : formatter<T> {
|
||||
|
||||
namespace geographic {
|
||||
|
||||
struct horizontal_kind : units::kind<horizontal_kind, units::isq::si::dim_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>]>;
|
||||
|
||||
template<typename T>
|
||||
struct position {
|
||||
@@ -141,31 +157,26 @@ struct position {
|
||||
template<typename T>
|
||||
distance spherical_distance(position<T> from, position<T> to)
|
||||
{
|
||||
using namespace units::isq::si;
|
||||
constexpr length<kilometre> earth_radius(6371);
|
||||
using namespace mp_units;
|
||||
constexpr auto earth_radius = 6'371 * isq::radius[si::kilo<si::metre>];
|
||||
|
||||
constexpr auto p = std::numbers::pi_v<T> / 180;
|
||||
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;
|
||||
using isq::sin, isq::cos, isq::asin, isq::acos;
|
||||
|
||||
// https://en.wikipedia.org/wiki/Great-circle_distance#Formulae
|
||||
if constexpr (sizeof(T) >= 8) {
|
||||
// spherical law of cosines
|
||||
const auto central_angle =
|
||||
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(lat2_rad - lat1_rad) / 2 + cos(lat1_rad) * cos(lat2_rad) * (1
|
||||
// - cos(lon2_rad - lon1_rad)) / 2));
|
||||
return distance(earth_radius * central_angle);
|
||||
const auto central_angle = acos(sin(from.lat) * sin(to.lat) + cos(from.lat) * cos(to.lat) * cos(to.lon - from.lon));
|
||||
// const auto central_angle = 2 * asin(sqrt(0.5 - cos(to.lat - from.lat) / 2 + cos(from.lat) * cos(to.lat) * (1
|
||||
// - cos(lon2_rad - from.lon)) / 2));
|
||||
|
||||
return quantity_cast<isq::distance>(earth_radius * central_angle);
|
||||
} else {
|
||||
// the haversine formula
|
||||
const auto sin_lat = sin((lat2_rad - lat1_rad) / 2);
|
||||
const auto sin_lon = sin((lon2_rad - lon1_rad) / 2);
|
||||
const auto central_angle = 2 * asin(sqrt(sin_lat * sin_lat + cos(lat1_rad) * cos(lat2_rad) * sin_lon * sin_lon));
|
||||
return distance(earth_radius * central_angle);
|
||||
const auto sin_lat = sin((to.lat - from.lat) / 2);
|
||||
const auto sin_lon = sin((to.lon - from.lon) / 2);
|
||||
const auto central_angle = 2 * asin(sqrt(sin_lat * sin_lat + cos(from.lat) * cos(to.lat) * sin_lon * sin_lon));
|
||||
|
||||
return quantity_cast<isq::distance>(earth_radius * central_angle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
#pragma once
|
||||
|
||||
#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 <concepts>
|
||||
#include <type_traits>
|
||||
@@ -51,9 +52,4 @@ public:
|
||||
};
|
||||
|
||||
template<typename T, auto Min, auto Max>
|
||||
struct std::common_type<std::intmax_t, ranged_representation<T, Min, Max>> :
|
||||
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>> {};
|
||||
inline constexpr bool mp_units::is_scalar<ranged_representation<T, Min, Max>> = mp_units::is_scalar<T>;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <gsl/gsl-lite.hpp>
|
||||
#include <units/bits/external/hacks.h>
|
||||
#include <mp-units/bits/external/hacks.h>
|
||||
#include <utility>
|
||||
|
||||
inline constexpr struct validated_tag {
|
||||
|
||||
@@ -28,17 +28,13 @@ cmake_minimum_required(VERSION 3.2)
|
||||
function(add_example target)
|
||||
add_executable(${target} ${target}.cpp)
|
||||
target_link_libraries(${target} PRIVATE ${ARGN})
|
||||
target_compile_definitions(${target} PRIVATE ${projectPrefix}NO_LITERALS ${projectPrefix}NO_ALIASES)
|
||||
endfunction()
|
||||
|
||||
add_example(kalman_filter-example_1 mp-units::core-fmt mp-units::si)
|
||||
add_example(kalman_filter-example_2 mp-units::core-fmt mp-units::si)
|
||||
add_example(kalman_filter-example_3 mp-units::core-fmt mp-units::si)
|
||||
add_example(kalman_filter-example_4 mp-units::core-fmt mp-units::si)
|
||||
add_example(kalman_filter-example_5 mp-units::core-fmt mp-units::si)
|
||||
|
||||
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
add_example(kalman_filter-example_6 mp-units::core-fmt mp-units::si)
|
||||
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()
|
||||
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 mp-units::utility)
|
||||
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 mp-units::utility)
|
||||
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)
|
||||
add_example(kalman_filter-example_7 mp-units::core-fmt mp-units::si mp-units::utility)
|
||||
add_example(kalman_filter-example_8 mp-units::core-fmt mp-units::si mp-units::utility)
|
||||
|
||||
@@ -22,43 +22,39 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <units/bits/fmt_hacks.h>
|
||||
#include <units/format.h>
|
||||
#include <units/generic/dimensionless.h>
|
||||
#include <units/isq/dimensions/time.h>
|
||||
#include <units/math.h>
|
||||
#include <units/quantity.h>
|
||||
#include <units/quantity_point.h>
|
||||
#include <mp-units/bits/fmt_hacks.h>
|
||||
#include <mp-units/format.h>
|
||||
#include <mp-units/math.h>
|
||||
#include <mp-units/quantity.h>
|
||||
#include <mp-units/quantity_point.h>
|
||||
#include <mp-units/systems/isq/space_and_time.h>
|
||||
#include <tuple>
|
||||
|
||||
namespace kalman {
|
||||
|
||||
template<typename T>
|
||||
concept QuantityOrQuantityPoint =
|
||||
units::Quantity<T> || units::QuantityPoint<T>; // TODO Should it also account for `kinds`?
|
||||
concept QuantityOrQuantityPoint = mp_units::Quantity<T> || mp_units::QuantityPoint<T>;
|
||||
|
||||
template<typename... Qs>
|
||||
template<mp_units::Dimension auto... Ds>
|
||||
inline constexpr bool are_time_derivatives = false;
|
||||
|
||||
template<typename Q>
|
||||
inline constexpr bool are_time_derivatives<Q> = true;
|
||||
template<mp_units::Dimension auto D>
|
||||
inline constexpr bool are_time_derivatives<D> = true;
|
||||
|
||||
template<typename Q1, typename Q2, typename... Qs>
|
||||
inline constexpr bool are_time_derivatives<Q1, Q2, Qs...> =
|
||||
units::DimensionOfT<typename decltype(Q1::reference / Q2::reference)::dimension,
|
||||
units::isq::dim_time> && // TODO Think on how to simplify this
|
||||
are_time_derivatives<Q2, Qs...>;
|
||||
template<mp_units::Dimension auto D1, mp_units::Dimension auto D2, mp_units::Dimension auto... Ds>
|
||||
inline constexpr bool are_time_derivatives<D1, D2, Ds...> =
|
||||
(D1 / D2 == mp_units::isq::dim_time) && are_time_derivatives<D2, Ds...>;
|
||||
|
||||
// state
|
||||
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 {
|
||||
std::tuple<QQPs...> variables_;
|
||||
constexpr state(QQPs... qqps) : variables_(std::move(qqps)...) {}
|
||||
};
|
||||
|
||||
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>
|
||||
constexpr auto& get(state<Qs...>& s)
|
||||
@@ -76,38 +72,35 @@ constexpr const auto& get(const state<Qs...>& s)
|
||||
template<QuantityOrQuantityPoint QQP, QuantityOrQuantityPoint... QQPs>
|
||||
struct estimation {
|
||||
private:
|
||||
using uncertainty_ref = decltype(QQP::reference * QQP::reference);
|
||||
using uncertainty_type =
|
||||
units::quantity<typename uncertainty_ref::dimension, typename uncertainty_ref::unit, typename QQP::rep>;
|
||||
static constexpr auto uncertainty_ref = QQP::reference * QQP::reference;
|
||||
using uncertainty_type = mp_units::quantity<uncertainty_ref, typename QQP::rep>;
|
||||
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;
|
||||
};
|
||||
|
||||
#if UNITS_COMP_MSVC || (UNITS_COMP_CLANG && UNITS_COMP_CLANG <= 16)
|
||||
|
||||
template<QuantityOrQuantityPoint QQP, units::Quantity U>
|
||||
template<QuantityOrQuantityPoint QQP, mp_units::Quantity U>
|
||||
estimation(state<QQP>, U) -> estimation<QQP>;
|
||||
|
||||
#endif
|
||||
|
||||
// kalman gain
|
||||
template<units::Quantity Q>
|
||||
constexpr units::dimensionless<units::one> kalman_gain(Q estimate_uncertainty, Q measurement_uncertainty)
|
||||
template<mp_units::Quantity Q>
|
||||
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);
|
||||
}
|
||||
|
||||
// state update
|
||||
template<typename Q, QuantityOrQuantityPoint QM, units::Dimensionless K>
|
||||
requires units::equivalent<typename Q::dimension, typename QM::dimension>
|
||||
template<typename Q, QuantityOrQuantityPoint QM, mp_units::QuantityOf<mp_units::dimensionless> K>
|
||||
requires(Q::quantity_spec == QM::quantity_spec)
|
||||
constexpr state<Q> state_update(const state<Q>& predicted, QM measured, K gain)
|
||||
{
|
||||
return {get<0>(predicted) + gain * (measured - get<0>(predicted))};
|
||||
}
|
||||
|
||||
template<typename Q1, typename Q2, QuantityOrQuantityPoint QM, units::Dimensionless K, units::isq::Time T>
|
||||
requires units::equivalent<typename Q1::dimension, typename QM::dimension>
|
||||
template<typename Q1, typename Q2, QuantityOrQuantityPoint QM, 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> 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));
|
||||
@@ -115,8 +108,9 @@ constexpr state<Q1, Q2> state_update(const state<Q1, Q2>& predicted, QM measured
|
||||
return {q1, q2};
|
||||
}
|
||||
|
||||
template<typename Q1, typename Q2, typename Q3, QuantityOrQuantityPoint QM, units::Dimensionless K, units::isq::Time T>
|
||||
requires units::equivalent<typename Q1::dimension, typename QM::dimension>
|
||||
template<typename Q1, typename Q2, typename Q3, QuantityOrQuantityPoint QM,
|
||||
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,
|
||||
T interval)
|
||||
{
|
||||
@@ -127,14 +121,14 @@ constexpr state<Q1, Q2, Q3> state_update(const state<Q1, Q2, Q3>& predicted, QM
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
return (1 - gain) * uncertainty;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
template<units::Quantity Q>
|
||||
template<mp_units::Quantity Q>
|
||||
constexpr Q covariance_extrapolation(Q uncertainty, Q process_noise_variance)
|
||||
{
|
||||
return uncertainty + process_noise_variance;
|
||||
@@ -164,8 +158,8 @@ template<typename... Qs>
|
||||
struct UNITS_STD_FMT::formatter<kalman::state<Qs...>> {
|
||||
constexpr auto parse(format_parse_context& ctx)
|
||||
{
|
||||
units::detail::dynamic_specs_handler handler(specs, ctx);
|
||||
return units::detail::parse_format_specs(ctx.begin(), ctx.end(), handler);
|
||||
mp_units::detail::dynamic_specs_handler handler(specs, ctx);
|
||||
return mp_units::detail::parse_format_specs(ctx.begin(), ctx.end(), handler);
|
||||
}
|
||||
|
||||
template<typename FormatContext>
|
||||
@@ -193,28 +187,28 @@ struct UNITS_STD_FMT::formatter<kalman::state<Qs...>> {
|
||||
}
|
||||
|
||||
std::string global_format_buffer;
|
||||
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::quantity_global_format_specs<char> global_specs = {specs.fill, specs.align, specs.width};
|
||||
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));
|
||||
}
|
||||
private:
|
||||
units::detail::dynamic_format_specs<char> specs;
|
||||
mp_units::detail::dynamic_format_specs<char> specs;
|
||||
};
|
||||
|
||||
template<typename Q>
|
||||
struct UNITS_STD_FMT::formatter<kalman::estimation<Q>> {
|
||||
constexpr auto parse(format_parse_context& ctx)
|
||||
{
|
||||
units::detail::dynamic_specs_handler handler(specs, ctx);
|
||||
return units::detail::parse_format_specs(ctx.begin(), ctx.end(), handler);
|
||||
mp_units::detail::dynamic_specs_handler handler(specs, ctx);
|
||||
return mp_units::detail::parse_format_specs(ctx.begin(), ctx.end(), handler);
|
||||
}
|
||||
|
||||
template<typename FormatContext>
|
||||
auto format(kalman::estimation<Q> e, FormatContext& ctx)
|
||||
{
|
||||
units::Quantity auto q = [](const Q& t) {
|
||||
if constexpr (units::Quantity<Q>)
|
||||
mp_units::Quantity auto q = [](const Q& t) {
|
||||
if constexpr (mp_units::Quantity<Q>)
|
||||
return t;
|
||||
else
|
||||
return t.relative();
|
||||
@@ -230,11 +224,11 @@ struct UNITS_STD_FMT::formatter<kalman::estimation<Q>> {
|
||||
}
|
||||
|
||||
std::string global_format_buffer;
|
||||
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::quantity_global_format_specs<char> global_specs = {specs.fill, specs.align, specs.width};
|
||||
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));
|
||||
}
|
||||
private:
|
||||
units::detail::dynamic_format_specs<char> specs;
|
||||
mp_units::detail::dynamic_format_specs<char> specs;
|
||||
};
|
||||
|
||||
@@ -21,15 +21,15 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include "kalman.h"
|
||||
#include <units/format.h>
|
||||
#include <units/generic/dimensionless.h>
|
||||
#include <units/isq/si/mass.h>
|
||||
#include <mp-units/format.h>
|
||||
#include <mp-units/systems/isq/space_and_time.h>
|
||||
#include <mp-units/systems/si/unit_symbols.h>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
// Based on: https://www.kalmanfilter.net/alphabeta.html#ex1
|
||||
|
||||
using namespace units;
|
||||
using namespace mp_units;
|
||||
|
||||
void print_header(const kalman::State auto& initial)
|
||||
{
|
||||
@@ -38,27 +38,26 @@ void print_header(const kalman::State auto& initial)
|
||||
"Curr. Estimate", "Next Estimate");
|
||||
}
|
||||
|
||||
void print(auto iteration, Dimensionless auto gain, Quantity auto measured, const kalman::State auto& current,
|
||||
const kalman::State auto& next)
|
||||
void print(auto iteration, QuantityOf<dimensionless> auto gain, Quantity auto measured,
|
||||
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);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace units::isq;
|
||||
using namespace units::isq::si::references;
|
||||
using state = kalman::state<si::mass<si::gram>>;
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
using state = kalman::state<quantity<isq::mass[g]>>;
|
||||
|
||||
const state initial = {1 * kg};
|
||||
const std::array measurements = {1030 * g, 989 * g, 1017 * g, 1009 * g, 1013 * g,
|
||||
979 * g, 1008 * g, 1042 * g, 1012 * g, 1011 * g};
|
||||
const std::array measurements = {1'030 * g, 989 * g, 1'017 * g, 1'009 * g, 1'013 * g,
|
||||
979 * g, 1'008 * g, 1'042 * g, 1'012 * g, 1'011 * g};
|
||||
|
||||
print_header(initial);
|
||||
state next = initial;
|
||||
for (int index = 1; const auto& m : measurements) {
|
||||
const auto& previous = next;
|
||||
const dimensionless<one> gain = 1. / index;
|
||||
const auto gain = 1. / index * one;
|
||||
const auto current = state_update(previous, m, gain);
|
||||
next = current;
|
||||
print(index++, gain, m, current, next);
|
||||
|
||||
@@ -21,17 +21,19 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include "kalman.h"
|
||||
#include <units/format.h>
|
||||
#include <units/generic/dimensionless.h>
|
||||
#include <units/isq/si/length.h>
|
||||
#include <units/isq/si/speed.h>
|
||||
#include <units/isq/si/time.h>
|
||||
#include <mp-units/format.h>
|
||||
#include <mp-units/systems/isq/space_and_time.h>
|
||||
#include <mp-units/systems/si/unit_symbols.h>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
// 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)
|
||||
{
|
||||
@@ -47,15 +49,15 @@ void print(auto iteration, Quantity auto measured, const kalman::State auto& cur
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace units::isq;
|
||||
using namespace units::isq::si::references;
|
||||
using state = kalman::state<si::length<si::metre>, si::speed<si::metre_per_second>>;
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
using state = kalman::state<quantity<isq::position_vector[m]>, quantity<isq::velocity[m / s]>>;
|
||||
|
||||
const auto interval = 5 * s;
|
||||
const auto interval = isq::duration(5 * s);
|
||||
const state initial = {30 * km, 40 * (m / s)};
|
||||
const std::array measurements = {30110 * m, 30265 * m, 30740 * m, 30750 * m, 31135 * m,
|
||||
31015 * m, 31180 * m, 31610 * m, 31960 * m, 31865 * m};
|
||||
std::array gain = {dimensionless<one>(0.2), dimensionless<one>(0.1)};
|
||||
const quantity<isq::position_vector[m], int> measurements[] = {30'110 * m, 30'265 * m, 30'740 * m, 30'750 * m,
|
||||
31'135 * m, 31'015 * m, 31'180 * m, 31'610 * m,
|
||||
31'960 * m, 31'865 * m};
|
||||
std::array gain = {0.2 * one, 0.1 * one};
|
||||
|
||||
print_header(initial);
|
||||
state next = state_extrapolation(initial, interval);
|
||||
|
||||
@@ -21,17 +21,19 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include "kalman.h"
|
||||
#include <units/format.h>
|
||||
#include <units/generic/dimensionless.h>
|
||||
#include <units/isq/si/length.h>
|
||||
#include <units/isq/si/speed.h>
|
||||
#include <units/isq/si/time.h>
|
||||
#include <mp-units/format.h>
|
||||
#include <mp-units/systems/isq/space_and_time.h>
|
||||
#include <mp-units/systems/si/unit_symbols.h>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
// 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)
|
||||
{
|
||||
@@ -47,15 +49,15 @@ void print(auto iteration, Quantity auto measured, const kalman::State auto& cur
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace units::isq;
|
||||
using namespace units::isq::si::references;
|
||||
using state = kalman::state<si::length<si::metre>, si::speed<si::metre_per_second>>;
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
using state = kalman::state<quantity<isq::position_vector[m]>, quantity<isq::velocity[m / s]>>;
|
||||
|
||||
const auto interval = 5 * s;
|
||||
const auto interval = isq::duration(5 * s);
|
||||
const state initial = {30 * km, 50 * (m / s)};
|
||||
const std::array measurements = {30160 * m, 30365 * m, 30890 * m, 31050 * m, 31785 * m,
|
||||
32215 * m, 33130 * m, 34510 * m, 36010 * m, 37265 * m};
|
||||
std::array gain = {dimensionless<one>(0.2), dimensionless<one>(0.1)};
|
||||
const quantity<isq::position_vector[m], int> measurements[] = {30'160 * m, 30'365 * m, 30'890 * m, 31'050 * m,
|
||||
31'785 * m, 32'215 * m, 33'130 * m, 34'510 * m,
|
||||
36'010 * m, 37'265 * m};
|
||||
std::array gain = {0.2 * one, 0.1 * one};
|
||||
|
||||
print_header(initial);
|
||||
state next = state_extrapolation(initial, interval);
|
||||
|
||||
@@ -21,18 +21,19 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include "kalman.h"
|
||||
#include <units/format.h>
|
||||
#include <units/generic/dimensionless.h>
|
||||
#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 <mp-units/format.h>
|
||||
#include <mp-units/systems/isq/space_and_time.h>
|
||||
#include <mp-units/systems/si/unit_symbols.h>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
// 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)
|
||||
{
|
||||
@@ -48,18 +49,16 @@ void print(auto iteration, Quantity auto measured, const kalman::State auto& cur
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace units::isq;
|
||||
using namespace units::isq::si::references;
|
||||
using state =
|
||||
kalman::state<si::length<si::metre>, si::speed<si::metre_per_second>, si::acceleration<si::metre_per_second_sq>>;
|
||||
constexpr auto mps = m / s;
|
||||
constexpr auto mps2 = mps / s;
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
using state = kalman::state<quantity<isq::position_vector[m]>, quantity<isq::velocity[m / s]>,
|
||||
quantity<isq::acceleration[m / s2]>>;
|
||||
const auto interval = isq::duration(5. * s);
|
||||
const state initial = {30 * km, 50 * (m / s), 0 * (m / s2)};
|
||||
|
||||
const auto interval = 5. * s;
|
||||
const state initial = {30 * km, 50 * mps, 0 * mps2};
|
||||
const std::array measurements = {30160 * m, 30365 * m, 30890 * m, 31050 * m, 31785 * m,
|
||||
32215 * m, 33130 * m, 34510 * m, 36010 * m, 37265 * m};
|
||||
std::array gain = {dimensionless<one>(0.5), dimensionless<one>(0.4), dimensionless<one>(0.1)};
|
||||
const quantity<isq::position_vector[m], int> measurements[] = {30'160 * m, 30'365 * m, 30'890 * m, 31'050 * m,
|
||||
31'785 * m, 32'215 * m, 33'130 * m, 34'510 * m,
|
||||
36'010 * m, 37'265 * m};
|
||||
std::array gain = {0.5 * one, 0.4 * one, 0.1 * one};
|
||||
|
||||
print_header(initial);
|
||||
state next = state_extrapolation(initial, interval);
|
||||
|
||||
@@ -21,15 +21,16 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include "kalman.h"
|
||||
#include <units/format.h>
|
||||
#include <units/isq/si/length.h>
|
||||
#include <units/math.h>
|
||||
#include <mp-units/format.h>
|
||||
#include <mp-units/math.h>
|
||||
#include <mp-units/systems/isq/space_and_time.h>
|
||||
#include <mp-units/systems/si/unit_symbols.h>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
// Based on: https://www.kalmanfilter.net/kalman1d.html#ex5
|
||||
|
||||
using namespace units;
|
||||
using namespace mp_units;
|
||||
|
||||
template<Quantity Q>
|
||||
void print_header(kalman::estimation<Q> initial)
|
||||
@@ -39,7 +40,7 @@ void print_header(kalman::estimation<Q> initial)
|
||||
"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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
using namespace kalman;
|
||||
using namespace units::isq;
|
||||
using namespace units::isq::si::references;
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
|
||||
const estimation initial = {state{60. * m}, pow<2>(15. * m)};
|
||||
const std::array 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};
|
||||
const auto measurement_uncertainty = pow<2>(5. * m);
|
||||
const estimation initial = {state{isq::height(60. * m)}, pow<2>(isq::height(15. * 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};
|
||||
const auto measurement_uncertainty = pow<2>(isq::height(5. * m));
|
||||
|
||||
auto update = [=]<Quantity Q>(const estimation<Q>& previous, const Q& meassurement, Dimensionless auto gain) {
|
||||
return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)};
|
||||
auto update = [=]<Quantity Q>(const estimation<Q>& previous, const Q& measurement,
|
||||
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; };
|
||||
|
||||
@@ -21,36 +21,17 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include "kalman.h"
|
||||
#include <units/format.h>
|
||||
#include <units/isq/si/thermodynamic_temperature.h>
|
||||
#include <units/math.h>
|
||||
#include <units/quantity_point.h>
|
||||
#include <units/unit.h>
|
||||
#include <mp-units/format.h>
|
||||
#include <mp-units/math.h>
|
||||
#include <mp-units/quantity_point.h>
|
||||
#include <mp-units/systems/isq/thermodynamics.h>
|
||||
#include <mp-units/systems/si/unit_symbols.h>
|
||||
#include <array>
|
||||
#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
|
||||
|
||||
using namespace units;
|
||||
using namespace mp_units;
|
||||
|
||||
template<QuantityPoint QP>
|
||||
void print_header(kalman::estimation<QP> initial)
|
||||
@@ -60,7 +41,7 @@ void print_header(kalman::estimation<QP> initial)
|
||||
"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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
constexpr auto deg_C = isq::Celsius_temperature[si::degree_Celsius];
|
||||
|
||||
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 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),
|
||||
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.933 * deg_C), quantity_point(50.007 * deg_C),
|
||||
quantity_point(50.023 * deg_C), quantity_point(49.99 * 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},
|
||||
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.933 * deg_C}, quantity_point{50.007 * deg_C},
|
||||
quantity_point{50.023 * deg_C}, quantity_point{49.99 * 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)};
|
||||
};
|
||||
|
||||
|
||||
@@ -21,36 +21,17 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include "kalman.h"
|
||||
#include <units/format.h>
|
||||
#include <units/isq/si/thermodynamic_temperature.h>
|
||||
#include <units/math.h>
|
||||
#include <units/quantity_point.h>
|
||||
#include <units/unit.h>
|
||||
#include <mp-units/format.h>
|
||||
#include <mp-units/math.h>
|
||||
#include <mp-units/quantity_point.h>
|
||||
#include <mp-units/systems/isq/thermodynamics.h>
|
||||
#include <mp-units/systems/si/unit_symbols.h>
|
||||
#include <array>
|
||||
#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
|
||||
|
||||
using namespace units;
|
||||
using namespace mp_units;
|
||||
|
||||
template<QuantityPoint QP>
|
||||
void print_header(kalman::estimation<QP> initial)
|
||||
@@ -60,7 +41,7 @@ void print_header(kalman::estimation<QP> initial)
|
||||
"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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
constexpr auto deg_C = isq::Celsius_temperature[si::degree_Celsius];
|
||||
|
||||
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 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),
|
||||
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(53.433 * deg_C), quantity_point(54.007 * deg_C),
|
||||
quantity_point(54.523 * deg_C), quantity_point(54.99 * 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},
|
||||
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{53.433 * deg_C}, quantity_point{54.007 * deg_C},
|
||||
quantity_point{54.523 * deg_C}, quantity_point{54.99 * 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)};
|
||||
};
|
||||
|
||||
|
||||
@@ -21,36 +21,17 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include "kalman.h"
|
||||
#include <units/format.h>
|
||||
#include <units/isq/si/thermodynamic_temperature.h>
|
||||
#include <units/math.h>
|
||||
#include <units/quantity_point.h>
|
||||
#include <units/unit.h>
|
||||
#include <mp-units/format.h>
|
||||
#include <mp-units/math.h>
|
||||
#include <mp-units/quantity_point.h>
|
||||
#include <mp-units/systems/isq/thermodynamics.h>
|
||||
#include <mp-units/systems/si/unit_symbols.h>
|
||||
#include <array>
|
||||
#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
|
||||
|
||||
using namespace units;
|
||||
using namespace mp_units;
|
||||
|
||||
template<QuantityPoint QP>
|
||||
void print_header(kalman::estimation<QP> initial)
|
||||
@@ -60,7 +41,7 @@ void print_header(kalman::estimation<QP> initial)
|
||||
"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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
constexpr auto deg_C = isq::Celsius_temperature[si::degree_Celsius];
|
||||
|
||||
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 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),
|
||||
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(53.433 * deg_C), quantity_point(54.007 * deg_C),
|
||||
quantity_point(54.523 * deg_C), quantity_point(54.99 * 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},
|
||||
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{53.433 * deg_C}, quantity_point{54.007 * deg_C},
|
||||
quantity_point{54.523 * deg_C}, quantity_point{54.99 * 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)};
|
||||
};
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
+24
-25
@@ -20,37 +20,36 @@
|
||||
// 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 <mp-units/iostream.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 <cmath>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
|
||||
namespace {
|
||||
|
||||
template<class T>
|
||||
template<typename T>
|
||||
class measurement {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
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
|
||||
using namespace std;
|
||||
uncertainty_ = abs(err);
|
||||
}
|
||||
|
||||
constexpr const value_type& value() const { return value_; }
|
||||
constexpr const value_type& uncertainty() const { return uncertainty_; }
|
||||
[[nodiscard]] constexpr const value_type& value() const { return value_; }
|
||||
[[nodiscard]] 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 value_type relative_uncertainty() const { return uncertainty() / value(); }
|
||||
[[nodiscard]] constexpr value_type lower_bound() const { return value() - uncertainty(); }
|
||||
[[nodiscard]] constexpr value_type upper_bound() const { return value() + uncertainty(); }
|
||||
|
||||
[[nodiscard]] constexpr measurement operator-() const { return measurement(-value(), uncertainty()); }
|
||||
|
||||
@@ -118,27 +117,27 @@ private:
|
||||
|
||||
} // 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 {
|
||||
|
||||
static_assert(units::Representation<measurement<double>>);
|
||||
|
||||
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 t = si::time<si::second, measurement<double>>(measurement(1.2, 0.1));
|
||||
const auto a = isq::acceleration(measurement{9.8, 0.1} * (m / s2));
|
||||
const auto t = measurement{1.2, 0.1} * s;
|
||||
|
||||
const Speed auto v1 = a * t;
|
||||
#if UNITS_DOWNCAST_MODE == 0
|
||||
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
|
||||
const QuantityOf<isq::velocity> auto v = a * t;
|
||||
std::cout << a << " * " << t << " = " << v << " = " << v[km / h] << '\n';
|
||||
|
||||
si::length<si::metre, measurement<double>> length(measurement(123., 1.));
|
||||
const auto length = measurement{123., 1.} * m;
|
||||
std::cout << "10 * " << length << " = " << 10 * length << '\n';
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
+27
-11
@@ -20,21 +20,37 @@
|
||||
// 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/constants.h>
|
||||
#include <mp-units/format.h>
|
||||
#include <mp-units/systems/si/constants.h>
|
||||
#include <mp-units/systems/si/unit_symbols.h>
|
||||
#include <iostream>
|
||||
|
||||
template<class T>
|
||||
requires mp_units::is_scalar<T>
|
||||
inline constexpr bool mp_units::is_vector<T> = true;
|
||||
|
||||
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 << UNITS_STD_FMT::format("- hyperfine transition frequency of Cs: {:%.0Q %q}\n",
|
||||
hyperfine_structure_transition_frequency<>);
|
||||
std::cout << UNITS_STD_FMT::format("- speed of light in vacuum: {:%.0Q %q}\n", speed_of_light<>);
|
||||
std::cout << UNITS_STD_FMT::format("- Planck constant: {}\n", planck_constant<>);
|
||||
std::cout << UNITS_STD_FMT::format("- elementary charge: {}\n", elementary_charge<>);
|
||||
std::cout << UNITS_STD_FMT::format("- Boltzmann constant: {}\n", boltzmann_constant<>);
|
||||
std::cout << UNITS_STD_FMT::format("- Avogadro constant: {}\n", avogadro_constant<>);
|
||||
std::cout << UNITS_STD_FMT::format("- luminous efficacy: {}\n", luminous_efficacy<>);
|
||||
std::cout << UNITS_STD_FMT::format("- hyperfine transition frequency of Cs: {} = {:%.0Q %q}\n",
|
||||
1. * si2019::hyperfine_structure_transition_frequency_of_cs,
|
||||
(1. * si2019::hyperfine_structure_transition_frequency_of_cs)[Hz]);
|
||||
std::cout << UNITS_STD_FMT::format("- speed of light in vacuum: {} = {:%.0Q %q}\n",
|
||||
1. * si2019::speed_of_light_in_vacuum,
|
||||
(1. * si2019::speed_of_light_in_vacuum)[m / s]);
|
||||
std::cout << UNITS_STD_FMT::format("- Planck constant: {} = {:%.8eQ %q}\n",
|
||||
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]);
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
@@ -20,31 +20,29 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include <units/generic/angle.h>
|
||||
|
||||
UNITS_DIAGNOSTIC_PUSH
|
||||
UNITS_DIAGNOSTIC_IGNORE_SHADOW
|
||||
#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 <mp-units/iostream.h>
|
||||
#include <mp-units/math.h>
|
||||
#include <mp-units/systems/isq_angle/isq_angle.h>
|
||||
#include <mp-units/systems/si/unit_symbols.h>
|
||||
#include <iostream>
|
||||
|
||||
template<class T>
|
||||
requires mp_units::is_scalar<T>
|
||||
inline constexpr bool mp_units::is_vector<T> = true;
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace units;
|
||||
using namespace units::isq;
|
||||
using namespace units::isq::si::references;
|
||||
using namespace units::references;
|
||||
using namespace mp_units;
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
using namespace mp_units::angular::unit_symbols;
|
||||
using mp_units::angular::unit_symbols::deg;
|
||||
using mp_units::angular::unit_symbols::rad;
|
||||
|
||||
const Length auto lever = 20 * cm;
|
||||
const Force auto force = 500 * N;
|
||||
const Angle auto angle = 90. * deg;
|
||||
const Torque auto torque = lever * force * sin(angle) / cotes_angle<>;
|
||||
const auto lever = isq_angle::position_vector(20 * cm);
|
||||
const auto force = isq_angle::force(500 * N);
|
||||
const auto angle = isq_angle::angular_measure(90. * deg);
|
||||
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 "
|
||||
<< quantity_cast<si::newton_metre_per_radian>(torque) << " of torque.\n";
|
||||
<< torque[N * m / rad] << " of torque.\n";
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
+5
-2
@@ -21,7 +21,7 @@
|
||||
# SOFTWARE.
|
||||
|
||||
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_)
|
||||
|
||||
@@ -44,10 +44,13 @@ add_subdirectory(core)
|
||||
add_subdirectory(core-fmt)
|
||||
add_subdirectory(core-io)
|
||||
add_subdirectory(systems)
|
||||
add_subdirectory(utility)
|
||||
|
||||
# project-wide wrapper
|
||||
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)
|
||||
install(TARGETS mp-units EXPORT mp-unitsTargets)
|
||||
|
||||
|
||||
@@ -65,5 +65,5 @@ function(add_units_module name)
|
||||
add_library(mp-units::${name} ALIAS mp-units-${name})
|
||||
|
||||
install(TARGETS mp-units-${name} EXPORT mp-unitsTargets)
|
||||
install(DIRECTORY include/units TYPE INCLUDE)
|
||||
install(DIRECTORY include/mp-units TYPE INCLUDE)
|
||||
endfunction()
|
||||
|
||||
@@ -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)
|
||||
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}>)
|
||||
|
||||
if(${projectPrefix}USE_LIBFMT)
|
||||
|
||||
@@ -28,14 +28,14 @@
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include <gsl/gsl-lite.hpp>
|
||||
#include <units/bits/fmt_hacks.h>
|
||||
#include <mp-units/bits/fmt_hacks.h>
|
||||
#include <concepts>
|
||||
#include <limits>
|
||||
#include <string_view>
|
||||
|
||||
// most of the below code is based on/copied from libfmt
|
||||
|
||||
namespace units::detail {
|
||||
namespace mp_units::detail {
|
||||
|
||||
struct auto_id {};
|
||||
|
||||
@@ -382,11 +382,11 @@ template<std::input_iterator It, std::sentinel_for<It> S, typename SpecHandler>
|
||||
|
||||
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;
|
||||
|
||||
// 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 == '#') {
|
||||
@@ -400,12 +400,12 @@ template<std::input_iterator It, std::sentinel_for<It> S, typename SpecHandler>
|
||||
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;
|
||||
|
||||
// Parse precision.
|
||||
if (*begin == '.') {
|
||||
begin = ::units::detail::parse_precision(begin, end, handler);
|
||||
begin = ::mp_units::detail::parse_precision(begin, end, handler);
|
||||
if (begin == end) return begin;
|
||||
}
|
||||
|
||||
@@ -465,4 +465,4 @@ private:
|
||||
ParseContext& context_;
|
||||
};
|
||||
|
||||
} // namespace units::detail
|
||||
} // namespace mp_units::detail
|
||||
+1
-1
@@ -27,7 +27,7 @@
|
||||
//
|
||||
// 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
|
||||
#define UNITS_USE_LIBFMT 1
|
||||
@@ -22,16 +22,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <units/bits/fmt.h>
|
||||
#include <units/customization_points.h>
|
||||
#include <units/quantity.h>
|
||||
#include <algorithm>
|
||||
#include <mp-units/bits/algorithm.h>
|
||||
#include <mp-units/bits/fmt.h>
|
||||
#include <mp-units/customization_points.h>
|
||||
#include <mp-units/quantity.h>
|
||||
#include <mp-units/unit.h>
|
||||
#include <cstdint>
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include <units/bits/unit_text.h>
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
// Grammar
|
||||
//
|
||||
// units-format-spec ::= [fill-and-align] [width] [units-specs]
|
||||
@@ -42,39 +39,14 @@
|
||||
// conversion-spec ::= '%' units-type
|
||||
// units-type ::= [units-rep-modifier] 'Q'
|
||||
// [units-unit-modifier] 'q'
|
||||
// one of "nt%"
|
||||
// units-rep-modifier ::= [sign] [#] [precision] [L] [units-rep-type]
|
||||
// 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
|
||||
//
|
||||
// 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 {
|
||||
namespace mp_units::detail {
|
||||
|
||||
// Holds specs about the whole object
|
||||
template<typename CharT>
|
||||
@@ -96,9 +68,7 @@ struct quantity_rep_format_specs {
|
||||
};
|
||||
|
||||
// Holds specs about the unit (%[specs]q)
|
||||
struct quantity_unit_format_specs {
|
||||
bool ascii_only = false;
|
||||
};
|
||||
struct quantity_unit_format_specs : unit_symbol_formatting {};
|
||||
|
||||
template<typename CharT>
|
||||
struct quantity_format_specs {
|
||||
@@ -109,7 +79,7 @@ struct quantity_format_specs {
|
||||
|
||||
// Parse a `units-rep-modifier`
|
||||
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
|
||||
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) {
|
||||
begin = parse_precision(begin, end, handler);
|
||||
} 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;
|
||||
}
|
||||
|
||||
@@ -156,35 +126,18 @@ constexpr It parse_units_format(It begin, S end, Handler&& handler)
|
||||
}
|
||||
if (begin != ptr) handler.on_text(begin, ptr);
|
||||
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++;
|
||||
|
||||
switch (c) {
|
||||
// units-type
|
||||
case '%':
|
||||
handler.on_text(ptr - 1, ptr);
|
||||
break;
|
||||
case 'n': {
|
||||
const char newline[] = "\n";
|
||||
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;
|
||||
constexpr auto units_types = std::string_view{"Qq"};
|
||||
const auto new_end = find_first_of(begin, end, units_types.begin(), units_types.end());
|
||||
if (new_end == end) 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, new_end); // Edit `on_quantity_unit` to add an unit modifier
|
||||
}
|
||||
ptr = new_end + 1;
|
||||
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, "}}");
|
||||
}
|
||||
|
||||
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 {
|
||||
OutputIt out;
|
||||
Rep val;
|
||||
const quantity_format_specs<CharT>& specs;
|
||||
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) :
|
||||
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);
|
||||
}
|
||||
|
||||
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>();
|
||||
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());
|
||||
}
|
||||
out = unit_symbol_to<CharT>(out, get_unit(Reference), specs.unit);
|
||||
}
|
||||
};
|
||||
|
||||
} // 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>
|
||||
struct UNITS_STD_FMT::formatter<units::quantity<Dimension, Unit, Rep>, CharT> {
|
||||
} // namespace mp_units::detail
|
||||
|
||||
template<auto Reference, typename Rep, typename CharT>
|
||||
struct UNITS_STD_FMT::formatter<mp_units::quantity<Reference, Rep>, CharT> {
|
||||
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;
|
||||
|
||||
bool quantity_value = 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;
|
||||
|
||||
struct spec_handler {
|
||||
@@ -318,9 +277,9 @@ private:
|
||||
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_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_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_precision(int precision) { f.specs.rep.precision = precision; }
|
||||
constexpr void on_localized() { f.specs.rep.localized = true; }
|
||||
@@ -331,30 +290,20 @@ private:
|
||||
if (valid_rep_types.find(type) != std::string_view::npos) {
|
||||
f.specs.rep.type = type;
|
||||
} else {
|
||||
UNITS_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"));
|
||||
throw UNITS_STD_FMT::format_error("invalid quantity type specifier");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename 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>
|
||||
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>
|
||||
@@ -365,13 +314,47 @@ private:
|
||||
template<std::input_iterator It, std::sentinel_for<It> S>
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
@@ -387,19 +370,19 @@ private:
|
||||
spec_handler handler{*this, ctx};
|
||||
|
||||
// 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};
|
||||
|
||||
// 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};
|
||||
|
||||
// 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)
|
||||
specs.global.align = units::detail::fmt_align::right;
|
||||
specs.global.align = mp_units::detail::fmt_align::right;
|
||||
|
||||
return {begin, end};
|
||||
}
|
||||
@@ -412,16 +395,15 @@ private:
|
||||
|
||||
if (begin == end || *begin == '}') {
|
||||
// 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());
|
||||
constexpr auto symbol = units::detail::unit_text<Dimension, Unit>();
|
||||
if constexpr (symbol.standard().size() > 0) {
|
||||
out = mp_units::detail::format_units_quantity_value<CharT>(out, q.number(), specs.rep, ctx.locale());
|
||||
if constexpr (mp_units::detail::has_unit_symbol(get_unit(Reference))) {
|
||||
*out++ = CharT(' ');
|
||||
UNITS_STD_FMT::format_to(out, "{}", symbol.standard().c_str());
|
||||
out = unit_symbol_to<CharT>(out, get_unit(Reference));
|
||||
}
|
||||
} else {
|
||||
// user provided format
|
||||
units::detail::quantity_formatter f(out, q, specs, ctx.locale());
|
||||
units::detail::parse_units_format(begin, end, f);
|
||||
mp_units::detail::quantity_formatter f(out, q, specs, ctx.locale());
|
||||
mp_units::detail::parse_units_format(begin, end, f);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -441,10 +423,10 @@ public:
|
||||
// process dynamic width and precision
|
||||
if (specs.global.dynamic_width_index >= 0)
|
||||
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)
|
||||
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) {
|
||||
// Avoid extra copying if width is not specified
|
||||
@@ -461,7 +443,7 @@ public:
|
||||
// In `global_format_buffer` we will create a global format string
|
||||
// e.g. "{:*^10%.1Q_%q}, 1.23_q_m" => "{:*^10}"
|
||||
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`
|
||||
// In the example, equivalent to UNITS_STD_FMT::format("{:*^10}", "1.2_m")
|
||||
@@ -22,4 +22,4 @@
|
||||
|
||||
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)
|
||||
|
||||
@@ -23,32 +23,28 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <units/quantity.h>
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include <units/bits/external/fixed_string_io.h>
|
||||
#include <units/bits/unit_text.h>
|
||||
#include <mp-units/quantity.h>
|
||||
#include <mp-units/unit.h>
|
||||
#include <sstream>
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
namespace units {
|
||||
namespace mp_units {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename CharT, class Traits, typename D, typename U, typename Rep>
|
||||
void to_stream(std::basic_ostream<CharT, Traits>& os, const quantity<D, U, Rep>& q)
|
||||
template<typename CharT, class Traits, auto R, typename Rep>
|
||||
void to_stream(std::basic_ostream<CharT, Traits>& os, const quantity<R, Rep>& q)
|
||||
{
|
||||
os << q.number();
|
||||
constexpr auto symbol = detail::unit_text<D, U>();
|
||||
if constexpr (!symbol.standard().empty()) {
|
||||
os << " " << symbol.standard();
|
||||
if constexpr (has_unit_symbol(get_unit(R))) {
|
||||
os << " ";
|
||||
unit_symbol_to<CharT>(std::ostream_iterator<CharT>(os), get_unit(R));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename CharT, typename Traits, typename D, typename U, typename Rep>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const quantity<D, U, Rep>& q)
|
||||
template<typename CharT, typename Traits, auto R, typename Rep>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const quantity<R, Rep>& q)
|
||||
requires requires { os << q.number(); }
|
||||
{
|
||||
if (os.width()) {
|
||||
@@ -65,4 +61,4 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace units
|
||||
} // namespace mp_units
|
||||
@@ -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
|
||||
+36
-43
@@ -22,10 +22,6 @@
|
||||
|
||||
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
|
||||
if(NOT TARGET gsl::gsl-lite)
|
||||
find_package(gsl-lite CONFIG REQUIRED)
|
||||
@@ -39,30 +35,41 @@ check_libcxx_in_use(${projectPrefix}LIBCXX)
|
||||
add_library(
|
||||
mp-units-core
|
||||
INTERFACE
|
||||
include/units/base_dimension.h
|
||||
include/units/chrono.h
|
||||
include/units/concepts.h
|
||||
include/units/customization_points.h
|
||||
include/units/derived_dimension.h
|
||||
include/units/exponent.h
|
||||
include/units/generic/angle.h
|
||||
include/units/generic/dimensionless.h
|
||||
include/units/generic/solid_angle.h
|
||||
include/units/kind.h
|
||||
include/units/magnitude.h
|
||||
include/units/math.h
|
||||
include/units/point_origin.h
|
||||
include/units/prefix.h
|
||||
include/units/quantity.h
|
||||
include/units/quantity_cast.h
|
||||
include/units/quantity_kind.h
|
||||
include/units/quantity_point.h
|
||||
include/units/quantity_point_kind.h
|
||||
include/units/random.h
|
||||
include/units/ratio.h
|
||||
include/units/reference.h
|
||||
include/units/symbol_text.h
|
||||
include/units/unit.h
|
||||
include/mp-units/bits/external/fixed_string.h
|
||||
include/mp-units/bits/external/hacks.h
|
||||
include/mp-units/bits/external/type_list.h
|
||||
include/mp-units/bits/external/type_name.h
|
||||
include/mp-units/bits/external/type_traits.h
|
||||
include/mp-units/bits/algorithm.h
|
||||
include/mp-units/bits/dimension_concepts.h
|
||||
include/mp-units/bits/expression_template.h
|
||||
include/mp-units/bits/get_associated_quantity.h
|
||||
include/mp-units/bits/get_common_base.h
|
||||
include/mp-units/bits/magnitude.h
|
||||
include/mp-units/bits/math_concepts.h
|
||||
include/mp-units/bits/prime.h
|
||||
include/mp-units/bits/quantity_cast.h
|
||||
include/mp-units/bits/quantity_concepts.h
|
||||
include/mp-units/bits/quantity_point_concepts.h
|
||||
include/mp-units/bits/quantity_spec_concepts.h
|
||||
include/mp-units/bits/ratio.h
|
||||
include/mp-units/bits/reference_concepts.h
|
||||
include/mp-units/bits/representation_concepts.h
|
||||
include/mp-units/bits/sudo_cast.h
|
||||
include/mp-units/bits/symbol_text.h
|
||||
include/mp-units/bits/text_tools.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_link_libraries(mp-units-core INTERFACE gsl::gsl-lite)
|
||||
@@ -85,23 +92,9 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
)
|
||||
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)
|
||||
add_library(mp-units::core ALIAS mp-units-core)
|
||||
|
||||
# installation
|
||||
install(TARGETS mp-units-core EXPORT mp-unitsTargets)
|
||||
install(DIRECTORY include/units TYPE INCLUDE)
|
||||
install(DIRECTORY include/mp-units TYPE INCLUDE)
|
||||
|
||||
+80
-4
@@ -22,11 +22,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <units/bits/external/hacks.h> // IWYU pragma: keep
|
||||
#include <mp-units/bits/external/hacks.h> // IWYU pragma: keep
|
||||
#include <compare>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <ranges>
|
||||
|
||||
namespace units::detail {
|
||||
namespace mp_units::detail {
|
||||
|
||||
// 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
|
||||
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>
|
||||
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>
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
+4
-4
@@ -22,9 +22,9 @@
|
||||
|
||||
#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)
|
||||
#include <units/bits/algorithm.h>
|
||||
#include <mp-units/bits/algorithm.h>
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include <compare>
|
||||
@@ -34,7 +34,7 @@
|
||||
#include <cstddef>
|
||||
|
||||
|
||||
namespace units {
|
||||
namespace mp_units {
|
||||
|
||||
/**
|
||||
* @brief A compile-time fixed string
|
||||
@@ -111,4 +111,4 @@ basic_fixed_string(CharT) -> basic_fixed_string<CharT, 1>;
|
||||
template<std::size_t N>
|
||||
using fixed_string = basic_fixed_string<char, N>;
|
||||
|
||||
} // namespace units
|
||||
} // namespace mp_units
|
||||
Vendored
+2
@@ -52,6 +52,7 @@
|
||||
#define UNITS_DIAGNOSTIC_IGNORE_SHADOW UNITS_DIAGNOSTIC_IGNORE("-Wshadow")
|
||||
#define UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE
|
||||
#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
|
||||
#define UNITS_DIAGNOSTIC_PUSH UNITS_PRAGMA(warning(push))
|
||||
#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_UNREACHABLE UNITS_DIAGNOSTIC_IGNORE(4702)
|
||||
#define UNITS_DIAGNOSTIC_IGNORE_ZERO_AS_NULLPOINTER_CONSTANT
|
||||
#define UNITS_DIAGNOSTIC_IGNORE_DEPRECATED
|
||||
#endif
|
||||
|
||||
#if _LIBCPP_VERSION
|
||||
+108
-32
@@ -22,12 +22,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mp-units/bits/external/hacks.h> // IWYU pragma: keep
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
UNITS_DIAGNOSTIC_PUSH
|
||||
UNITS_DIAGNOSTIC_IGNORE_EXPR_ALWAYS_TF
|
||||
|
||||
namespace units {
|
||||
namespace mp_units {
|
||||
|
||||
namespace detail {
|
||||
|
||||
@@ -42,6 +44,94 @@ inline constexpr bool is_type_list<T<Types...>> = true;
|
||||
template<typename 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
|
||||
|
||||
namespace detail {
|
||||
@@ -59,6 +149,7 @@ struct type_list_push_front_impl<List<OldTypes...>, NewTypes...> {
|
||||
template<TypeList List, typename... Types>
|
||||
using type_list_push_front = TYPENAME detail::type_list_push_front_impl<List, Types...>::type;
|
||||
|
||||
|
||||
// push_back
|
||||
|
||||
namespace detail {
|
||||
@@ -76,6 +167,7 @@ struct type_list_push_back_impl<List<OldTypes...>, NewTypes...> {
|
||||
template<TypeList List, typename... Types>
|
||||
using type_list_push_back = TYPENAME detail::type_list_push_back_impl<List, Types...>::type;
|
||||
|
||||
|
||||
// join
|
||||
|
||||
namespace detail {
|
||||
@@ -95,46 +187,29 @@ struct type_list_join_impl<List<First...>, List<Second...>, Rest...> {
|
||||
template<TypeList... Lists>
|
||||
using type_list_join = TYPENAME detail::type_list_join_impl<Lists...>::type;
|
||||
|
||||
|
||||
// split
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<template<typename...> typename List, std::size_t Idx, std::size_t N, typename... Types>
|
||||
struct split_impl;
|
||||
template<typename List, typename First, typename Second>
|
||||
struct type_list_split_impl;
|
||||
|
||||
template<template<typename...> typename List, std::size_t Idx, std::size_t N>
|
||||
struct split_impl<List, Idx, N> {
|
||||
using first_list = List<>;
|
||||
using second_list = List<>;
|
||||
};
|
||||
|
||||
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;
|
||||
template<template<typename...> typename List, typename... Args, std::size_t... First, std::size_t... Second>
|
||||
struct type_list_split_impl<List<Args...>, std::index_sequence<First...>, std::index_sequence<Second...>> {
|
||||
using indexed_list = indexed_type_list<Args...>;
|
||||
using first_list = List<type_list_element_indexed<indexed_list, First>...>;
|
||||
using second_list = List<type_list_element_indexed<indexed_list, sizeof...(First) + Second>...>;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
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
|
||||
|
||||
@@ -144,6 +219,7 @@ struct type_list_split_half;
|
||||
template<template<typename...> typename List, typename... Types>
|
||||
struct type_list_split_half<List<Types...>> : type_list_split<List<Types...>, (sizeof...(Types) + 1) / 2> {};
|
||||
|
||||
|
||||
// merge_sorted
|
||||
|
||||
namespace detail {
|
||||
@@ -166,7 +242,6 @@ struct type_list_merge_sorted_impl<List<>, List<Rhs...>, Pred> {
|
||||
using type = List<Rhs...>;
|
||||
};
|
||||
|
||||
|
||||
template<template<typename...> typename List, typename Lhs1, typename... LhsRest, typename Rhs1, typename... RhsRest,
|
||||
template<typename, typename> typename Pred>
|
||||
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>
|
||||
using type_list_merge_sorted = TYPENAME detail::type_list_merge_sorted_impl<SortedList1, SortedList2, Pred>::type;
|
||||
|
||||
|
||||
// sort
|
||||
|
||||
namespace detail {
|
||||
@@ -218,6 +294,6 @@ struct type_list_sort_impl<List<Types...>, Pred> {
|
||||
template<TypeList List, template<typename, typename> typename Pred>
|
||||
using type_list_sort = TYPENAME detail::type_list_sort_impl<List, Pred>::type;
|
||||
|
||||
} // namespace units
|
||||
} // namespace mp_units
|
||||
|
||||
UNITS_DIAGNOSTIC_POP
|
||||
@@ -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;
|
||||
}
|
||||
+77
-3
@@ -22,11 +22,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <units/bits/external/hacks.h>
|
||||
#include <mp-units/bits/external/hacks.h>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace units {
|
||||
namespace mp_units {
|
||||
|
||||
// conditional
|
||||
namespace detail {
|
||||
@@ -65,6 +65,12 @@ inline constexpr bool is_specialization_of = false;
|
||||
template<typename... Params, template<typename...> typename Type>
|
||||
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
|
||||
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
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -22,23 +22,74 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <units/bits/external/hacks.h>
|
||||
#include <units/bits/prime.h>
|
||||
#include <units/ratio.h>
|
||||
#include <mp-units/bits/expression_template.h>
|
||||
#include <mp-units/bits/external/hacks.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 <cstdint>
|
||||
#include <exception>
|
||||
#include <numbers>
|
||||
#include <optional>
|
||||
|
||||
namespace units {
|
||||
namespace mp_units {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Higher numbers use fewer trial divisions, at the price of more storage space.
|
||||
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
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
@@ -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.,
|
||||
* 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.
|
||||
@@ -80,72 +128,119 @@ concept BaseRep =
|
||||
* _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}}`.
|
||||
*/
|
||||
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 {
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_base_power = false;
|
||||
template<BaseRep T>
|
||||
inline constexpr bool is_base_power<base_power<T>> = true;
|
||||
inline constexpr bool is_named_magnitude = Magnitude<T> && !detail::is_specialization_of_magnitude<T>;
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
||||
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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
template<typename T>
|
||||
concept BasePower = detail::is_base_power<T>;
|
||||
|
||||
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;
|
||||
return bp;
|
||||
if constexpr (std::integral<decltype(V)>) {
|
||||
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.
|
||||
template<typename T>
|
||||
using widen_t =
|
||||
std::conditional_t<std::is_arithmetic_v<T>,
|
||||
std::conditional_t<std::is_floating_point_v<T>, long double,
|
||||
std::conditional_t<std::is_signed_v<T>, std::intmax_t, std::uintmax_t>>,
|
||||
T>;
|
||||
using widen_t = conditional<std::is_arithmetic_v<T>,
|
||||
conditional<std::is_floating_point_v<T>, long double,
|
||||
conditional<std::is_signed_v<T>, std::intmax_t, std::uintmax_t>>,
|
||||
T>;
|
||||
|
||||
// Raise an arbitrary arithmetic type to a positive integer power at compile time.
|
||||
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
|
||||
// "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>
|
||||
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
|
||||
// need to write a custom function.
|
||||
//
|
||||
// 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.
|
||||
if (bp.power.den != 1) {
|
||||
const auto exp = get_exponent(el);
|
||||
if (exp.den != 1) {
|
||||
std::terminate(); // Rational powers not yet supported
|
||||
}
|
||||
|
||||
if (bp.power.num < 0) {
|
||||
if (exp.num < 0) {
|
||||
if constexpr (std::is_integral_v<T>) {
|
||||
std::terminate(); // Cannot represent reciprocal as integer
|
||||
} else {
|
||||
return T{1} / compute_base_power<T>(inverse(bp));
|
||||
return T{1} / compute_base_power<T>(inverse(el));
|
||||
}
|
||||
}
|
||||
|
||||
auto power = bp.power.num;
|
||||
return int_power(static_cast<widen_t<T>>(bp.get_base()), power);
|
||||
auto power = exp.num;
|
||||
return int_power(static_cast<widen_t<T>>(get_base(el)), power);
|
||||
}
|
||||
|
||||
// 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>
|
||||
// TODO(chogg): Migrate this to use `treat_as_floating_point`.
|
||||
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
|
||||
// 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);
|
||||
}
|
||||
|
||||
} // 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.
|
||||
namespace detail {
|
||||
|
||||
// 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;
|
||||
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.
|
||||
//
|
||||
// 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) {
|
||||
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.
|
||||
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)
|
||||
{
|
||||
if (bp.power == 0) {
|
||||
return false;
|
||||
}
|
||||
// template<MagnitudeSpec Element>
|
||||
// [[nodiscard]] consteval bool is_valid_element(Element element)
|
||||
// {
|
||||
// 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>) {
|
||||
// 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 = (bp.get_base() > 1'000'000'000)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return is_prime(bp.get_base());
|
||||
} else {
|
||||
return bp.get_base() > 0;
|
||||
}
|
||||
}
|
||||
// return is_prime(get_base_value(element));
|
||||
// } else {
|
||||
// return get_base_value(element) > 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
// A function object to apply a predicate to all consecutive pairs of values in a sequence.
|
||||
template<typename Predicate>
|
||||
struct pairwise_all {
|
||||
Predicate predicate;
|
||||
// template<typename Predicate>
|
||||
// struct pairwise_all {
|
||||
// Predicate predicate;
|
||||
|
||||
template<typename... Ts>
|
||||
constexpr bool operator()(Ts&&... ts) const
|
||||
{
|
||||
// Carefully handle different sizes, avoiding unsigned integer underflow.
|
||||
constexpr auto num_comparisons = [](auto num_elements) {
|
||||
return (num_elements > 1) ? (num_elements - 1) : 0;
|
||||
}(sizeof...(Ts));
|
||||
// template<typename... Ts>
|
||||
// [[nodiscard]] consteval bool operator()(Ts&&... ts) const
|
||||
// {
|
||||
// // Carefully handle different sizes, avoiding unsigned integer underflow.
|
||||
// constexpr auto num_comparisons = [](auto num_elements) {
|
||||
// return (num_elements > 1) ? (num_elements - 1) : 0;
|
||||
// }(sizeof...(Ts));
|
||||
|
||||
// Compare zero or more pairs of neighbours as needed.
|
||||
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>());
|
||||
}
|
||||
};
|
||||
// // Compare zero or more pairs of neighbours as needed.
|
||||
// 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>());
|
||||
// }
|
||||
// };
|
||||
|
||||
// Deduction guide: permit constructions such as `pairwise_all{std::less{}}`.
|
||||
template<typename T>
|
||||
pairwise_all(T) -> pairwise_all<T>;
|
||||
// template<typename T>
|
||||
// pairwise_all(T) -> pairwise_all<T>;
|
||||
|
||||
// Check whether a sequence of (possibly heterogeneously typed) values are strictly increasing.
|
||||
template<typename... Ts>
|
||||
requires(std::is_signed_v<Ts> && ...)
|
||||
constexpr bool strictly_increasing(Ts&&... ts)
|
||||
// template<typename... Ts>
|
||||
// requires(std::is_signed_v<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>
|
||||
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)
|
||||
[[nodiscard]] consteval bool is_integral(MagnitudeSpec auto element)
|
||||
{
|
||||
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
|
||||
|
||||
|
||||
/**
|
||||
* @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
|
||||
* rational powers, and compare for equality.
|
||||
*/
|
||||
template<BasePower auto... BPs>
|
||||
requires detail::is_base_power_pack_valid<BPs...>
|
||||
template<MagnitudeSpec auto... Ms>
|
||||
// requires detail::is_element_pack_valid<Ms...>
|
||||
struct magnitude {
|
||||
// Whether this magnitude represents an integer.
|
||||
friend constexpr bool is_integral(const magnitude&) { return (detail::is_integral(BPs) && ...); }
|
||||
[[nodiscard]] friend consteval bool is_integral(const magnitude&)
|
||||
{
|
||||
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
|
||||
return (is_integral(Ms) && ...);
|
||||
}
|
||||
|
||||
// Whether this magnitude represents a rational number.
|
||||
friend constexpr bool is_rational(const magnitude&) { return (detail::is_rational(BPs) && ...); }
|
||||
[[nodiscard]] friend consteval bool is_rational(const magnitude&)
|
||||
{
|
||||
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
|
||||
return (is_rational(Ms) && ...);
|
||||
}
|
||||
};
|
||||
|
||||
// Implementation for Magnitude concept (below).
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<auto... Ms>
|
||||
void to_base_specialization_of_magnitude(const volatile magnitude<Ms...>*);
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_magnitude = false;
|
||||
template<auto... BPs>
|
||||
inline constexpr bool is_magnitude<magnitude<BPs...>> = true;
|
||||
inline constexpr bool is_derived_from_specialization_of_magnitude =
|
||||
requires(T* t) { to_base_specialization_of_magnitude(t); };
|
||||
|
||||
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
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
template<typename T, auto... BPs>
|
||||
// TODO(chogg): Migrate this to use `treat_as_floating_point`.
|
||||
requires(!std::is_integral_v<T> || is_integral(magnitude<BPs...>{}))
|
||||
constexpr T get_value(const magnitude<BPs...>&)
|
||||
template<typename T, auto... Ms>
|
||||
requires(is_integral(magnitude<Ms...>{})) || treat_as_floating_point<T>
|
||||
constexpr T get_value(const magnitude<Ms...>&)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
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.
|
||||
|
||||
template<auto... LeftBPs, auto... RightBPs>
|
||||
constexpr bool operator==(magnitude<LeftBPs...>, magnitude<RightBPs...>)
|
||||
template<Magnitude M1, Magnitude M2>
|
||||
[[nodiscard]] consteval bool operator==(M1, M2)
|
||||
{
|
||||
if constexpr (sizeof...(LeftBPs) == sizeof...(RightBPs)) {
|
||||
return ((LeftBPs == RightBPs) && ...);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return std::is_same_v<M1, M2>;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Magnitude rational powers implementation.
|
||||
|
||||
template<ratio E, auto... BPs>
|
||||
constexpr auto pow(magnitude<BPs...>)
|
||||
template<std::intmax_t Num, std::intmax_t Den = 1, auto... Ms>
|
||||
[[nodiscard]] consteval auto pow(magnitude<Ms...>)
|
||||
{
|
||||
if constexpr (E.num == 0) {
|
||||
if constexpr (Num == 0) {
|
||||
return magnitude<>{};
|
||||
} 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>
|
||||
constexpr auto sqrt(magnitude<BPs...> m)
|
||||
template<auto... Ms>
|
||||
[[nodiscard]] consteval auto sqrt(magnitude<Ms...> m)
|
||||
{
|
||||
return pow<ratio{1, 2}>(m);
|
||||
return pow<1, 2>(m);
|
||||
}
|
||||
|
||||
template<auto... BPs>
|
||||
constexpr auto cbrt(magnitude<BPs...> m)
|
||||
template<auto... Ms>
|
||||
[[nodiscard]] consteval auto cbrt(magnitude<Ms...> m)
|
||||
{
|
||||
return pow<ratio{1, 3}>(m);
|
||||
return pow<1, 3>(m);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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.
|
||||
constexpr Magnitude auto operator*(magnitude<>, magnitude<>) { return magnitude<>{}; }
|
||||
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.
|
||||
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.
|
||||
if constexpr (H1.get_base() < H2.get_base()) {
|
||||
using namespace detail;
|
||||
|
||||
if constexpr (less(H1, H2)) {
|
||||
if constexpr (sizeof...(T1) == 0) {
|
||||
// Shortcut for the "pure prepend" case, which makes it easier to implement some of the other cases.
|
||||
return magnitude<H1, H2, T2...>{};
|
||||
} else {
|
||||
return magnitude<H1>{} * (magnitude<T1...>{} * magnitude<H2, T2...>{});
|
||||
}
|
||||
}
|
||||
|
||||
// Case for when H2 has the smaller base.
|
||||
if constexpr (H1.get_base() > H2.get_base()) {
|
||||
} else if constexpr (less(H2, H1)) {
|
||||
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 (H1.get_base() == H2.get_base()) {
|
||||
constexpr auto partial_product = magnitude<T1...>{} * magnitude<T2...>{};
|
||||
|
||||
// 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;
|
||||
return head;
|
||||
}(H1);
|
||||
|
||||
if constexpr (new_head.power == 0) {
|
||||
return partial_product;
|
||||
if constexpr (get_exponent(new_head) == 0) {
|
||||
return partial_product;
|
||||
} else {
|
||||
return magnitude<new_head>{} * partial_product;
|
||||
}
|
||||
}
|
||||
} else if constexpr (is_named_magnitude<decltype(get_base(H1))>) {
|
||||
return magnitude<H1>{} * (magnitude<T1...>{} * magnitude<H2, T2...>{});
|
||||
} 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.
|
||||
|
||||
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.
|
||||
@@ -498,19 +614,15 @@ constexpr auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1
|
||||
namespace detail {
|
||||
|
||||
// The largest integer which can be extracted from any magnitude with only a single basis vector.
|
||||
template<auto BP>
|
||||
constexpr auto integer_part(magnitude<BP>)
|
||||
template<auto M>
|
||||
[[nodiscard]] consteval auto integer_part(magnitude<M>)
|
||||
{
|
||||
constexpr auto power_num = BP.power.num;
|
||||
constexpr auto power_den = BP.power.den;
|
||||
constexpr auto power_num = get_exponent(M).num;
|
||||
constexpr auto power_den = get_exponent(M).den;
|
||||
|
||||
if constexpr (std::is_integral_v<decltype(BP.get_base())> && (power_num >= power_den)) {
|
||||
constexpr auto largest_integer_power = [=](BasePower auto bp) {
|
||||
bp.power = (power_num / power_den); // Note: integer division intended.
|
||||
return bp;
|
||||
}(BP); // Note: lambda is immediately invoked.
|
||||
|
||||
return magnitude<largest_integer_power>{};
|
||||
if constexpr (std::is_integral_v<decltype(get_base(M))> && (power_num >= power_den)) {
|
||||
// largest integer power
|
||||
return magnitude<power_v_or_T<get_base(M), power_num / power_den>()>{}; // Note: integer division intended
|
||||
} else {
|
||||
return magnitude<>{};
|
||||
}
|
||||
@@ -518,13 +630,13 @@ constexpr auto integer_part(magnitude<BP>)
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<auto... BPs>
|
||||
constexpr auto numerator(magnitude<BPs...>)
|
||||
template<auto... Ms>
|
||||
[[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()`.
|
||||
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).
|
||||
|
||||
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;
|
||||
} else {
|
||||
return magnitude<>{};
|
||||
}
|
||||
}
|
||||
|
||||
template<auto... BPs>
|
||||
constexpr auto remove_positive_powers(magnitude<BPs...>)
|
||||
template<auto... Ms>
|
||||
[[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.
|
||||
constexpr auto common_magnitude(magnitude<>, magnitude<>) { return magnitude<>{}; }
|
||||
constexpr auto common_magnitude(magnitude<>, Magnitude auto m) { return detail::remove_positive_powers(m); }
|
||||
constexpr auto common_magnitude(Magnitude auto m, magnitude<>) { return detail::remove_positive_powers(m); }
|
||||
[[nodiscard]] consteval auto common_magnitude(magnitude<>, magnitude<>) { return magnitude<>{}; }
|
||||
[[nodiscard]] consteval auto common_magnitude(magnitude<>, Magnitude auto 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.
|
||||
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;
|
||||
|
||||
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.
|
||||
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.
|
||||
return remove_positive_power(magnitude<H2>{}) * common_magnitude(magnitude<H1, T1...>{}, magnitude<T2...>{});
|
||||
} else {
|
||||
// When the bases are equal, pick whichever has the lower power.
|
||||
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;
|
||||
} else {
|
||||
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.
|
||||
|
||||
@@ -614,11 +744,12 @@ template<std::intmax_t N>
|
||||
inline constexpr std::optional<std::intmax_t> known_first_factor = std::nullopt;
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Helper to perform prime factorization at compile time.
|
||||
template<std::intmax_t N>
|
||||
requires(N > 0)
|
||||
requires gt_zero<N>
|
||||
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()) {
|
||||
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 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).
|
||||
@@ -643,6 +774,7 @@ struct prime_factorization<1> {
|
||||
|
||||
template<std::intmax_t N>
|
||||
inline constexpr auto prime_factorization_v = prime_factorization<N>::value;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
@@ -652,42 +784,74 @@ inline constexpr auto prime_factorization_v = prime_factorization<N>::value;
|
||||
* manually adding base powers.
|
||||
*/
|
||||
template<ratio R>
|
||||
requires(R.num > 0)
|
||||
constexpr Magnitude auto mag()
|
||||
{
|
||||
return detail::prime_factorization_v<R.num> / detail::prime_factorization_v<R.den>;
|
||||
}
|
||||
requires detail::gt_zero<R.num>
|
||||
inline constexpr Magnitude auto mag = 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.
|
||||
*/
|
||||
template<ratio Base, ratio Pow>
|
||||
requires(Base.num > 0)
|
||||
constexpr Magnitude auto mag_power()
|
||||
{
|
||||
return pow<Pow>(mag<Base>());
|
||||
}
|
||||
requires detail::gt_zero<Base.num>
|
||||
inline constexpr Magnitude auto mag_power = pow<Pow.num, Pow.den>(mag<Base>);
|
||||
|
||||
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_5 = get_power(5, m);
|
||||
|
||||
if ((power_of_2 * power_of_5).num <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if ((power_of_2 * power_of_5).num <= 0) return 0;
|
||||
|
||||
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
|
||||
+4
-4
@@ -24,12 +24,12 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace units::detail {
|
||||
namespace mp_units::detail {
|
||||
|
||||
template<std::intmax_t N>
|
||||
template<auto N>
|
||||
concept gt_zero = (N > 0);
|
||||
|
||||
template<std::intmax_t N>
|
||||
template<auto N>
|
||||
concept non_zero = (N != 0);
|
||||
|
||||
} // namespace units::detail
|
||||
} // namespace mp_units::detail
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <units/bits/algorithm.h>
|
||||
#include <mp-units/bits/algorithm.h>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
@@ -30,9 +30,9 @@
|
||||
#include <optional>
|
||||
#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)) {
|
||||
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.
|
||||
//
|
||||
// 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) {
|
||||
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>
|
||||
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;
|
||||
primes[0] = 2;
|
||||
@@ -71,7 +71,7 @@ constexpr std::array<std::uintmax_t, N> first_n_primes()
|
||||
}
|
||||
|
||||
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) {
|
||||
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>
|
||||
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;
|
||||
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>
|
||||
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::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>
|
||||
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;
|
||||
for (const auto& v : values) {
|
||||
@@ -135,7 +135,7 @@ struct wheel_factorizer {
|
||||
static constexpr auto coprimes_in_first_wheel =
|
||||
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;
|
||||
|
||||
@@ -150,7 +150,7 @@ struct wheel_factorizer {
|
||||
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
|
||||
+27
-16
@@ -22,23 +22,34 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hacks.h"
|
||||
#include <mp-units/bits/quantity_concepts.h>
|
||||
#include <mp-units/reference.h>
|
||||
#include <type_traits>
|
||||
|
||||
namespace units {
|
||||
namespace mp_units {
|
||||
|
||||
template<typename BaseType>
|
||||
struct downcast_base {
|
||||
using downcast_base_type = BaseType;
|
||||
};
|
||||
/**
|
||||
* @brief Explicit cast of a quantity type
|
||||
*
|
||||
* 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>
|
||||
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
|
||||
} // namespace mp_units
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user