mirror of
https://github.com/mpusz/mp-units.git
synced 2025-08-05 21:24:27 +02:00
Merge branch 'master' into chiphogg/mod#509
This commit is contained in:
3
.flake8
3
.flake8
@@ -7,3 +7,6 @@ ignore =
|
|||||||
E712,
|
E712,
|
||||||
# line break before binary operator
|
# line break before binary operator
|
||||||
W503
|
W503
|
||||||
|
per-file-ignores =
|
||||||
|
# flake8 is just plain wrong here, contradicting black
|
||||||
|
.github/generate-job-matrix.py:E225,E231
|
||||||
|
206
.github/generate-job-matrix.py
vendored
Normal file
206
.github/generate-job-matrix.py
vendored
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import typing
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
from job_matrix import CombinationCollector, Compiler, Configuration
|
||||||
|
|
||||||
|
|
||||||
|
def make_gcc_config(version: int) -> Configuration:
|
||||||
|
return Configuration(
|
||||||
|
name=f"GCC-{version}",
|
||||||
|
os="ubuntu-24.04",
|
||||||
|
compiler=Compiler(
|
||||||
|
type="GCC",
|
||||||
|
version=version,
|
||||||
|
cc=f"gcc-{version}",
|
||||||
|
cxx=f"g++-{version}",
|
||||||
|
),
|
||||||
|
cxx_modules=False,
|
||||||
|
std_format_support=version >= 13,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def make_clang_config(
|
||||||
|
version: int, platform: typing.Literal["x86-64", "arm64"] = "x86-64"
|
||||||
|
) -> Configuration:
|
||||||
|
cfg = SimpleNamespace(
|
||||||
|
name=f"Clang-{version} ({platform})",
|
||||||
|
compiler=SimpleNamespace(
|
||||||
|
type="CLANG",
|
||||||
|
version=version,
|
||||||
|
),
|
||||||
|
lib="libc++",
|
||||||
|
cxx_modules=version >= 17,
|
||||||
|
std_format_support=version >= 17,
|
||||||
|
)
|
||||||
|
match platform:
|
||||||
|
case "x86-64":
|
||||||
|
cfg.os = "ubuntu-22.04" if version < 17 else "ubuntu-24.04"
|
||||||
|
cfg.compiler.cc = f"clang-{version}"
|
||||||
|
cfg.compiler.cxx = f"clang++-{version}"
|
||||||
|
case "arm64":
|
||||||
|
cfg.os = "macos-14"
|
||||||
|
pfx = f"/opt/homebrew/opt/llvm@{version}/bin"
|
||||||
|
cfg.compiler.cc = f"{pfx}/clang"
|
||||||
|
cfg.compiler.cxx = f"{pfx}/clang++"
|
||||||
|
case _:
|
||||||
|
raise KeyError(f"Unsupported platform {platform!r} for Clang")
|
||||||
|
ret = cfg
|
||||||
|
ret.compiler = Compiler(**vars(cfg.compiler))
|
||||||
|
return Configuration(**vars(ret))
|
||||||
|
|
||||||
|
|
||||||
|
def make_apple_clang_config(version: int) -> Configuration:
|
||||||
|
ret = Configuration(
|
||||||
|
name=f"Apple Clang {version}",
|
||||||
|
os="macos-13",
|
||||||
|
compiler=Compiler(
|
||||||
|
type="APPLE_CLANG",
|
||||||
|
version=f"{version}.0",
|
||||||
|
cc="clang",
|
||||||
|
cxx="clang++",
|
||||||
|
),
|
||||||
|
cxx_modules=False,
|
||||||
|
std_format_support=False,
|
||||||
|
)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def make_msvc_config(release: str, version: int) -> Configuration:
|
||||||
|
ret = Configuration(
|
||||||
|
name=f"MSVC {release}",
|
||||||
|
os="windows-2022",
|
||||||
|
compiler=Compiler(
|
||||||
|
type="MSVC",
|
||||||
|
version=version,
|
||||||
|
cc="",
|
||||||
|
cxx="",
|
||||||
|
),
|
||||||
|
cxx_modules=False,
|
||||||
|
std_format_support=True,
|
||||||
|
)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
configs = {
|
||||||
|
c.name: c
|
||||||
|
for c in [make_gcc_config(ver) for ver in [12, 13, 14]]
|
||||||
|
+ [
|
||||||
|
make_clang_config(ver, platform)
|
||||||
|
for ver in [16, 17, 18]
|
||||||
|
for platform in ["x86-64", "arm64"]
|
||||||
|
# arm64 runners are expensive; only consider one version
|
||||||
|
if ver == 18 or platform != "arm64"
|
||||||
|
]
|
||||||
|
+ [make_apple_clang_config(ver) for ver in [15]]
|
||||||
|
+ [make_msvc_config(release="14.4", version=194)]
|
||||||
|
}
|
||||||
|
|
||||||
|
full_matrix = dict(
|
||||||
|
config=list(configs.values()),
|
||||||
|
std=[20, 23],
|
||||||
|
formatting=["std::format", "fmtlib"],
|
||||||
|
contracts=["none", "gsl-lite", "ms-gsl"],
|
||||||
|
build_type=["Release", "Debug"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
# parser.add_argument("-I","--include",nargs="+",action="append")
|
||||||
|
# parser.add_argument("-X","--exclude",nargs="+",action="append")
|
||||||
|
parser.add_argument("--seed", type=int, default=42)
|
||||||
|
parser.add_argument("--preset", default=None)
|
||||||
|
parser.add_argument("--debug", nargs="+", default=["combinations"])
|
||||||
|
parser.add_argument("--suppress-output", default=False, action="store_true")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
rgen = random.Random(args.seed)
|
||||||
|
|
||||||
|
collector = CombinationCollector(
|
||||||
|
full_matrix,
|
||||||
|
hard_excludes=lambda e: (
|
||||||
|
e.formatting == "std::format" and not e.config.std_format_support
|
||||||
|
),
|
||||||
|
)
|
||||||
|
match args.preset:
|
||||||
|
case None:
|
||||||
|
pass
|
||||||
|
case "all":
|
||||||
|
collector.all_combinations()
|
||||||
|
case "conan" | "cmake":
|
||||||
|
collector.all_combinations(
|
||||||
|
formatting="std::format",
|
||||||
|
contracts="gsl-lite",
|
||||||
|
build_type="Debug",
|
||||||
|
std=20,
|
||||||
|
)
|
||||||
|
collector.all_combinations(
|
||||||
|
filter=lambda me: not me.config.std_format_support,
|
||||||
|
formatting="fmtlib",
|
||||||
|
contracts="gsl-lite",
|
||||||
|
build_type="Debug",
|
||||||
|
std=20,
|
||||||
|
)
|
||||||
|
collector.sample_combinations(rgen=rgen, min_samples_per_value=2)
|
||||||
|
case "clang-tidy":
|
||||||
|
collector.all_combinations(config=configs["Clang-18 (x86-64)"])
|
||||||
|
case "freestanding":
|
||||||
|
# TODO For some reason Clang-18 Debug with -ffreestanding does not pass CMakeTestCXXCompiler
|
||||||
|
collector.all_combinations(
|
||||||
|
filter=lambda e: not (
|
||||||
|
e.config.name.startswith("Clang-18") and e.build_type == "Debug"
|
||||||
|
),
|
||||||
|
config=[configs[c] for c in ["GCC-14", "Clang-18 (x86-64)"]],
|
||||||
|
contracts="none",
|
||||||
|
std=23,
|
||||||
|
)
|
||||||
|
case _:
|
||||||
|
raise KeyError(f"Unsupported preset {args.preset!r}")
|
||||||
|
|
||||||
|
if not collector.combinations:
|
||||||
|
raise ValueError("No combination has been produced")
|
||||||
|
|
||||||
|
data = sorted(collector.combinations)
|
||||||
|
|
||||||
|
json_data = [e.as_json() for e in data]
|
||||||
|
|
||||||
|
output_file = os.environ.get("GITHUB_OUTPUT")
|
||||||
|
if not args.suppress_output:
|
||||||
|
if output_file:
|
||||||
|
print(f"Writing outputs to {output_file}")
|
||||||
|
with open(output_file, "wt") as fh:
|
||||||
|
fh.write(f"matrix={json.dumps(json_data)}")
|
||||||
|
else:
|
||||||
|
print("No output file received!")
|
||||||
|
|
||||||
|
for dbg in args.debug:
|
||||||
|
match dbg:
|
||||||
|
case "yaml":
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
json_data = json.loads(json.dumps(json_data))
|
||||||
|
print(yaml.safe_dump(json_data))
|
||||||
|
case "json":
|
||||||
|
print(json.dumps(json_data, indent=4))
|
||||||
|
case "combinations":
|
||||||
|
for e in data:
|
||||||
|
print(
|
||||||
|
f"{e.config!s:17s} c++{e.std:2d} {e.formatting:11s} {e.contracts:8s} {e.build_type:8s}"
|
||||||
|
)
|
||||||
|
case "counts":
|
||||||
|
print(f"Total combinations {len(data)}")
|
||||||
|
for (k, v), n in sorted(collector.per_value_counts.items()):
|
||||||
|
print(f" {k}={v}: {n}")
|
||||||
|
case "none":
|
||||||
|
pass
|
||||||
|
case _:
|
||||||
|
raise KeyError(f"Unknown debug mode {dbg!r}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
139
.github/job_matrix.py
vendored
Normal file
139
.github/job_matrix.py
vendored
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import dataclasses
|
||||||
|
import itertools
|
||||||
|
import random
|
||||||
|
import typing
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, order=True)
|
||||||
|
class Compiler:
|
||||||
|
type: typing.Literal["GCC", "CLANG", "APPLE_CLANG", "MSVC"]
|
||||||
|
version: str | int
|
||||||
|
cc: str
|
||||||
|
cxx: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, order=True)
|
||||||
|
class Configuration:
|
||||||
|
name: str
|
||||||
|
os: str
|
||||||
|
compiler: Compiler
|
||||||
|
cxx_modules: bool
|
||||||
|
std_format_support: bool
|
||||||
|
conan_config: str = ""
|
||||||
|
lib: typing.Literal["libc++", "libstdc++"] | None = None
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, order=True)
|
||||||
|
class MatrixElement:
|
||||||
|
config: Configuration
|
||||||
|
std: typing.Literal[20, 23]
|
||||||
|
formatting: typing.Literal["std::format", "fmtlib"]
|
||||||
|
contracts: typing.Literal["none", "gsl-lite", "ms-gsl"]
|
||||||
|
build_type: typing.Literal["Release", "Debug"]
|
||||||
|
|
||||||
|
def as_json(self):
|
||||||
|
def dataclass_to_json(obj):
|
||||||
|
"""Convert dataclasses to something json-serialisable"""
|
||||||
|
if dataclasses.is_dataclass(obj):
|
||||||
|
return {
|
||||||
|
k: dataclass_to_json(v) for k, v in dataclasses.asdict(obj).items()
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
|
||||||
|
ret = dataclass_to_json(self)
|
||||||
|
# patch boolean conan configuration options
|
||||||
|
config = ret["config"]
|
||||||
|
for k in ["cxx_modules"]:
|
||||||
|
config[k] = "True" if config[k] else "False"
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
class CombinationCollector:
|
||||||
|
"""Incremental builder of MatrixElements, allowing successive selection of entries."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
full_matrix: dict[str, list[typing.Any]],
|
||||||
|
*,
|
||||||
|
hard_excludes: typing.Callable[[MatrixElement], bool] | None = None,
|
||||||
|
):
|
||||||
|
self.full_matrix = full_matrix
|
||||||
|
self.hard_excludes = hard_excludes
|
||||||
|
self.combinations: set[MatrixElement] = set()
|
||||||
|
self.per_value_counts: dict[tuple[str, typing.Any], int] = {
|
||||||
|
(k, v): 0 for k, options in full_matrix.items() for v in options
|
||||||
|
}
|
||||||
|
|
||||||
|
def _make_submatrix(self, **overrides):
|
||||||
|
new_matrix = dict(self.full_matrix)
|
||||||
|
for k, v in overrides.items():
|
||||||
|
if not isinstance(v, list):
|
||||||
|
v = [v]
|
||||||
|
new_matrix[k] = v
|
||||||
|
return new_matrix
|
||||||
|
|
||||||
|
def _add_combination(self, e: MatrixElement):
|
||||||
|
if e in self.combinations or (
|
||||||
|
self.hard_excludes is not None and self.hard_excludes(e)
|
||||||
|
):
|
||||||
|
return
|
||||||
|
self.combinations.add(e)
|
||||||
|
# update per_value_counts
|
||||||
|
for k, v in vars(e).items():
|
||||||
|
idx = (k, v)
|
||||||
|
self.per_value_counts[idx] = self.per_value_counts.get(idx, 0) + 1
|
||||||
|
|
||||||
|
def all_combinations(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
filter: typing.Callable[[MatrixElement], bool] | None = None,
|
||||||
|
**overrides,
|
||||||
|
):
|
||||||
|
"""Adds all combinations in the submatrix defined by `overrides`."""
|
||||||
|
matrix = self._make_submatrix(**overrides)
|
||||||
|
keys = tuple(matrix.keys())
|
||||||
|
for combination in itertools.product(*matrix.values()):
|
||||||
|
cand = MatrixElement(**dict(zip(keys, combination)))
|
||||||
|
if filter and not filter(cand):
|
||||||
|
continue
|
||||||
|
self._add_combination(cand)
|
||||||
|
|
||||||
|
def sample_combinations(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
rgen: random.Random,
|
||||||
|
min_samples_per_value: int = 1,
|
||||||
|
filter: typing.Callable[[MatrixElement], bool] | None = None,
|
||||||
|
**overrides,
|
||||||
|
):
|
||||||
|
"""Adds samples from the submatrix defined by `overrides`,
|
||||||
|
ensuring each individual value appears at least n times.
|
||||||
|
"""
|
||||||
|
matrix = self._make_submatrix(**overrides)
|
||||||
|
missing: dict[tuple[str, typing.Any], int] = {}
|
||||||
|
for key, options in matrix.items():
|
||||||
|
for value in options:
|
||||||
|
idx = (key, value)
|
||||||
|
missing[idx] = min_samples_per_value - self.per_value_counts.get(idx, 0)
|
||||||
|
while missing:
|
||||||
|
(force_key, force_option), remaining = next(iter(missing.items()))
|
||||||
|
if remaining <= 0:
|
||||||
|
missing.pop((force_key, force_option))
|
||||||
|
continue
|
||||||
|
choice = {}
|
||||||
|
for key, options in matrix.items():
|
||||||
|
choice[key] = force_option if key == force_key else rgen.choice(options)
|
||||||
|
cand = MatrixElement(**choice)
|
||||||
|
if filter and not filter(cand):
|
||||||
|
continue
|
||||||
|
self._add_combination(cand)
|
||||||
|
for idx in choice.items():
|
||||||
|
if missing.pop(idx, 0) <= 0:
|
||||||
|
continue
|
||||||
|
remaining = min_samples_per_value - self.per_value_counts.get(idx, 0)
|
||||||
|
if remaining > 0:
|
||||||
|
missing[idx] = remaining
|
47
.github/workflows/ci-clang-tidy.yml
vendored
47
.github/workflows/ci-clang-tidy.yml
vendored
@@ -34,33 +34,33 @@ on:
|
|||||||
paths-ignore:
|
paths-ignore:
|
||||||
- "docs/**"
|
- "docs/**"
|
||||||
|
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
generate-matrix:
|
||||||
|
name: "Generate build matrix for ${{ github.workflow }}"
|
||||||
|
outputs:
|
||||||
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
- id: set-matrix
|
||||||
|
run: python .github/generate-job-matrix.py --preset clang-tidy --seed 42 --debug combinations counts
|
||||||
build:
|
build:
|
||||||
name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
|
name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
|
||||||
runs-on: ${{ matrix.config.os }}
|
runs-on: ${{ matrix.config.os }}
|
||||||
|
needs: generate-matrix
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
formatting: ["std::format", "fmtlib"]
|
include: ${{fromJson(needs.generate-matrix.outputs.matrix)}}
|
||||||
contracts: ["none", "gsl-lite", "ms-gsl"]
|
|
||||||
std: [20, 23]
|
|
||||||
config:
|
|
||||||
- {
|
|
||||||
name: "Clang-18",
|
|
||||||
os: ubuntu-24.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: CLANG,
|
|
||||||
version: 18,
|
|
||||||
cc: "clang-18",
|
|
||||||
cxx: "clang++-18",
|
|
||||||
},
|
|
||||||
lib: "libc++",
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "True",
|
|
||||||
conan-config: "",
|
|
||||||
}
|
|
||||||
build_type: ["Release", "Debug"]
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.config.compiler.cc }}
|
CC: ${{ matrix.config.compiler.cc }}
|
||||||
@@ -127,8 +127,11 @@ jobs:
|
|||||||
sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.std }}/' ~/.conan2/profiles/default
|
sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.std }}/' ~/.conan2/profiles/default
|
||||||
sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default
|
sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default
|
||||||
conan profile show -pr default
|
conan profile show -pr default
|
||||||
- run: echo "std_format=$([ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
|
- name: Set 'std_format' and 'import_std' environment variables
|
||||||
- run: echo "import_std=$([ "${{ matrix.config.cxx_modules }}" == "True" ] && [ "${{ matrix.config.contracts }}" == "none" ] && [ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "std_format=$([ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
|
||||||
|
echo "import_std=$([ "${{ matrix.std }}" -ge "23" ] && [ "${{ matrix.config.cxx_modules }}" == "True" ] && [ "${{ matrix.config.contracts }}" == "none" ] && [ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
|
||||||
- name: Run clang-tidy
|
- name: Run clang-tidy
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
158
.github/workflows/ci-conan.yml
vendored
158
.github/workflows/ci-conan.yml
vendored
@@ -33,149 +33,35 @@ on:
|
|||||||
env:
|
env:
|
||||||
CHANNEL: ${{ fromJSON('["testing", "stable"]')[github.ref_type == 'tag' && startsWith(github.ref_name, 'v')] }}
|
CHANNEL: ${{ fromJSON('["testing", "stable"]')[github.ref_type == 'tag' && startsWith(github.ref_name, 'v')] }}
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
generate-matrix:
|
||||||
|
name: "Generate build matrix for ${{ github.workflow }}"
|
||||||
|
outputs:
|
||||||
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
- id: set-matrix
|
||||||
|
run: python .github/generate-job-matrix.py --preset conan --seed 42 --debug combinations counts
|
||||||
build:
|
build:
|
||||||
name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
|
name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
|
||||||
runs-on: ${{ matrix.config.os }}
|
runs-on: ${{ matrix.config.os }}
|
||||||
|
needs: generate-matrix
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
formatting: ["std::format", "fmtlib"]
|
include: ${{fromJson(needs.generate-matrix.outputs.matrix)}}
|
||||||
contracts: ["none", "gsl-lite", "ms-gsl"]
|
|
||||||
std: [20, 23]
|
|
||||||
config:
|
|
||||||
- {
|
|
||||||
name: "MSVC 14.4",
|
|
||||||
os: windows-2022,
|
|
||||||
compiler: { type: MSVC, version: 194, cc: "", cxx: "" },
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "True",
|
|
||||||
conan-config: "",
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "GCC-12",
|
|
||||||
os: ubuntu-24.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: GCC,
|
|
||||||
version: 12,
|
|
||||||
cc: "gcc-12",
|
|
||||||
cxx: "g++-12",
|
|
||||||
},
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "False",
|
|
||||||
conan-config: "",
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "GCC-13",
|
|
||||||
os: ubuntu-24.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: GCC,
|
|
||||||
version: 13,
|
|
||||||
cc: "gcc-13",
|
|
||||||
cxx: "g++-13",
|
|
||||||
},
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "True",
|
|
||||||
conan-config: "",
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "GCC-14",
|
|
||||||
os: ubuntu-24.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: GCC,
|
|
||||||
version: 14,
|
|
||||||
cc: "gcc-14",
|
|
||||||
cxx: "g++-14",
|
|
||||||
},
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "True",
|
|
||||||
conan-config: "",
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "Clang-16",
|
|
||||||
os: ubuntu-22.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: CLANG,
|
|
||||||
version: 16,
|
|
||||||
cc: "clang-16",
|
|
||||||
cxx: "clang++-16",
|
|
||||||
},
|
|
||||||
lib: "libc++",
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "False",
|
|
||||||
conan-config: "",
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "Clang-17",
|
|
||||||
os: ubuntu-24.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: CLANG,
|
|
||||||
version: 17,
|
|
||||||
cc: "clang-17",
|
|
||||||
cxx: "clang++-17",
|
|
||||||
},
|
|
||||||
lib: "libc++",
|
|
||||||
cxx_modules: "True",
|
|
||||||
std_format_support: "True",
|
|
||||||
conan-config: "",
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "Clang-18",
|
|
||||||
os: ubuntu-24.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: CLANG,
|
|
||||||
version: 18,
|
|
||||||
cc: "clang-18",
|
|
||||||
cxx: "clang++-18",
|
|
||||||
},
|
|
||||||
lib: "libc++",
|
|
||||||
cxx_modules: "True",
|
|
||||||
std_format_support: "True",
|
|
||||||
conan-config: "",
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "Clang-18 on Apple M1 (arm64)",
|
|
||||||
os: macos-14,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: CLANG,
|
|
||||||
version: 18,
|
|
||||||
cc: "/opt/homebrew/opt/llvm@18/bin/clang-18",
|
|
||||||
cxx: "/opt/homebrew/opt/llvm@18/bin/clang++",
|
|
||||||
},
|
|
||||||
lib: "libc++",
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "True"
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "Apple Clang 15",
|
|
||||||
os: macos-13,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: APPLE_CLANG,
|
|
||||||
version: "15.0",
|
|
||||||
cc: "clang",
|
|
||||||
cxx: "clang++",
|
|
||||||
},
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "False",
|
|
||||||
conan-config: "",
|
|
||||||
}
|
|
||||||
build_type: ["Release", "Debug"]
|
|
||||||
exclude:
|
|
||||||
- formatting: "std::format"
|
|
||||||
config: { std_format_support: "False" }
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.config.compiler.cc }}
|
CC: ${{ matrix.config.compiler.cc }}
|
||||||
CXX: ${{ matrix.config.compiler.cxx }}
|
CXX: ${{ matrix.config.compiler.cxx }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Generate unique cache id
|
- name: Generate unique cache id
|
||||||
@@ -258,21 +144,21 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
echo "std_format=$([ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
|
echo "std_format=$([ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
|
||||||
echo "import_std=$([ "${{ matrix.config.cxx_modules }}" == "True" ] && [ "${{ matrix.config.contracts }}" == "none" ] && [ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
|
echo "import_std=$([ "${{ matrix.std }}" -ge "23" ] && [ "${{ matrix.config.cxx_modules }}" == "True" ] && [ "${{ matrix.config.contracts }}" == "none" ] && [ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
|
||||||
- name: Create Conan package
|
- name: Create Conan package
|
||||||
if: matrix.config.compiler.type != 'MSVC'
|
if: matrix.config.compiler.type != 'MSVC'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \
|
conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \
|
||||||
-b mp-units/* -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -c user.mp-units.build:all=True \
|
-b mp-units/* -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -c user.mp-units.build:all=True \
|
||||||
-o '&:cxx_modules=${{ matrix.config.cxx_modules }}' -o '&:import_std=${{ env.import_std }}' -o '&:std_format=${{ env.std_format }}' -o '&:contracts=${{ matrix.contracts }}' ${{ matrix.config.conan-config }}
|
-o '&:cxx_modules=${{ matrix.config.cxx_modules }}' -o '&:import_std=${{ env.import_std }}' -o '&:std_format=${{ env.std_format }}' -o '&:contracts=${{ matrix.contracts }}' ${{ matrix.config.conan_config }}
|
||||||
- name: Create Conan package
|
- name: Create Conan package
|
||||||
if: matrix.config.compiler.type == 'MSVC'
|
if: matrix.config.compiler.type == 'MSVC'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \
|
conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \
|
||||||
-b mp-units/* -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -c user.mp-units.build:all=False \
|
-b mp-units/* -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -c user.mp-units.build:all=False \
|
||||||
-o '&:cxx_modules=${{ matrix.config.cxx_modules }}' -o '&:import_std=${{ env.import_std }}' -o '&:std_format=${{ env.std_format }}' -o '&:contracts=${{ matrix.contracts }}' ${{ matrix.config.conan-config }}
|
-o '&:cxx_modules=${{ matrix.config.cxx_modules }}' -o '&:import_std=${{ env.import_std }}' -o '&:std_format=${{ env.std_format }}' -o '&:contracts=${{ matrix.contracts }}' ${{ matrix.config.conan_config }}
|
||||||
- name: Obtain package reference
|
- name: Obtain package reference
|
||||||
id: get-package-ref
|
id: get-package-ref
|
||||||
shell: bash
|
shell: bash
|
||||||
|
4
.github/workflows/ci-formatting.yml
vendored
4
.github/workflows/ci-formatting.yml
vendored
@@ -24,6 +24,10 @@ name: Formatting CI
|
|||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
57
.github/workflows/ci-freestanding.yml
vendored
57
.github/workflows/ci-freestanding.yml
vendored
@@ -34,51 +34,32 @@ on:
|
|||||||
paths-ignore:
|
paths-ignore:
|
||||||
- "docs/**"
|
- "docs/**"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
generate-matrix:
|
||||||
|
name: "Generate build matrix for ${{ github.workflow }}"
|
||||||
|
outputs:
|
||||||
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
- id: set-matrix
|
||||||
|
run: python .github/generate-job-matrix.py --preset freestanding --seed 42 --debug combinations counts
|
||||||
build:
|
build:
|
||||||
name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
|
name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
|
||||||
runs-on: ${{ matrix.config.os }}
|
runs-on: ${{ matrix.config.os }}
|
||||||
|
needs: generate-matrix
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
formatting: ["std::format", "fmtlib"]
|
include: ${{fromJson(needs.generate-matrix.outputs.matrix)}}
|
||||||
contracts: ["none"]
|
|
||||||
std: [23]
|
|
||||||
config:
|
|
||||||
- {
|
|
||||||
name: "GCC-14",
|
|
||||||
os: ubuntu-24.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: GCC,
|
|
||||||
version: 14,
|
|
||||||
cc: "gcc-14",
|
|
||||||
cxx: "g++-14",
|
|
||||||
},
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "True",
|
|
||||||
conan-config: "",
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "Clang-18",
|
|
||||||
os: ubuntu-24.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: CLANG,
|
|
||||||
version: 18,
|
|
||||||
cc: "clang-18",
|
|
||||||
cxx: "clang++-18",
|
|
||||||
},
|
|
||||||
lib: "libc++",
|
|
||||||
cxx_modules: "True",
|
|
||||||
std_format_support: "True",
|
|
||||||
conan-config: "",
|
|
||||||
}
|
|
||||||
build_type: ["Release", "Debug"]
|
|
||||||
# TODO For some reason Clang-18 Debug with -ffreestanding does not pass CMakeTestCXXCompiler
|
|
||||||
exclude:
|
|
||||||
- build_type: "Debug"
|
|
||||||
config: { name: "Clang-18" }
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.config.compiler.cc }}
|
CC: ${{ matrix.config.compiler.cc }}
|
||||||
|
146
.github/workflows/ci-test-package-cmake.yml
vendored
146
.github/workflows/ci-test-package-cmake.yml
vendored
@@ -38,136 +38,32 @@ on:
|
|||||||
- "example/**"
|
- "example/**"
|
||||||
- "test/**"
|
- "test/**"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
generate-matrix:
|
||||||
|
name: "Generate build matrix for ${{ github.workflow }}"
|
||||||
|
outputs:
|
||||||
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
- id: set-matrix
|
||||||
|
run: python .github/generate-job-matrix.py --preset conan --seed 42 --debug combinations counts
|
||||||
test_package:
|
test_package:
|
||||||
name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
|
name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
|
||||||
runs-on: ${{ matrix.config.os }}
|
runs-on: ${{ matrix.config.os }}
|
||||||
|
needs: generate-matrix
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
formatting: ["std::format", "fmtlib"]
|
include: ${{fromJson(needs.generate-matrix.outputs.matrix)}}
|
||||||
contracts: ["none", "gsl-lite", "ms-gsl"]
|
|
||||||
std: [20, 23]
|
|
||||||
config:
|
|
||||||
- {
|
|
||||||
name: "MSVC 14.4",
|
|
||||||
os: windows-2022,
|
|
||||||
compiler: { type: MSVC, version: 194, cc: "", cxx: "" },
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "True",
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "GCC-12",
|
|
||||||
os: ubuntu-24.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: GCC,
|
|
||||||
version: 12,
|
|
||||||
cc: "gcc-12",
|
|
||||||
cxx: "g++-12",
|
|
||||||
},
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "False",
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "GCC-13",
|
|
||||||
os: ubuntu-24.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: GCC,
|
|
||||||
version: 13,
|
|
||||||
cc: "gcc-13",
|
|
||||||
cxx: "g++-13",
|
|
||||||
},
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "True",
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "GCC-14",
|
|
||||||
os: ubuntu-24.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: GCC,
|
|
||||||
version: 14,
|
|
||||||
cc: "gcc-14",
|
|
||||||
cxx: "g++-14",
|
|
||||||
},
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "True"
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "Clang-16",
|
|
||||||
os: ubuntu-22.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: CLANG,
|
|
||||||
version: 16,
|
|
||||||
cc: "clang-16",
|
|
||||||
cxx: "clang++-16",
|
|
||||||
},
|
|
||||||
lib: "libc++",
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "False",
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "Clang-17",
|
|
||||||
os: ubuntu-24.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: CLANG,
|
|
||||||
version: 17,
|
|
||||||
cc: "clang-17",
|
|
||||||
cxx: "clang++-17",
|
|
||||||
},
|
|
||||||
lib: "libc++",
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "True",
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "Clang-18",
|
|
||||||
os: ubuntu-24.04,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: CLANG,
|
|
||||||
version: 18,
|
|
||||||
cc: "clang-18",
|
|
||||||
cxx: "clang++-18",
|
|
||||||
},
|
|
||||||
lib: "libc++",
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "True"
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "Clang-18 on Apple M1 (arm64)",
|
|
||||||
os: macos-14,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: CLANG,
|
|
||||||
version: 18,
|
|
||||||
cc: "/opt/homebrew/opt/llvm@18/bin/clang-18",
|
|
||||||
cxx: "/opt/homebrew/opt/llvm@18/bin/clang++",
|
|
||||||
},
|
|
||||||
lib: "libc++",
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "True"
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
name: "Apple Clang 15",
|
|
||||||
os: macos-14,
|
|
||||||
compiler:
|
|
||||||
{
|
|
||||||
type: APPLE_CLANG,
|
|
||||||
version: "15.0",
|
|
||||||
cc: "clang",
|
|
||||||
cxx: "clang++",
|
|
||||||
},
|
|
||||||
cxx_modules: "False",
|
|
||||||
std_format_support: "False",
|
|
||||||
}
|
|
||||||
build_type: ["Release", "Debug"]
|
|
||||||
exclude:
|
|
||||||
- formatting: "std::format"
|
|
||||||
config: { std_format_support: "False" }
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.config.compiler.cc }}
|
CC: ${{ matrix.config.compiler.cc }}
|
||||||
@@ -255,8 +151,8 @@ jobs:
|
|||||||
- name: Set 'std_format' and 'import_std' environment variables
|
- name: Set 'std_format' and 'import_std' environment variables
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
echo "std_format=$([ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
|
echo "std_format=$([ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
|
||||||
echo "import_std=$([ "${{ matrix.config.cxx_modules }}" == "True" ] && [ "${{ matrix.config.contracts }}" == "none" ] && [ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
|
echo "import_std=$([ "${{ matrix.std }}" -ge "23" ] && [ "${{ matrix.config.cxx_modules }}" == "True" ] && [ "${{ matrix.config.contracts }}" == "none" ] && [ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
|
||||||
- name: Install Conan dependencies
|
- name: Install Conan dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
4
.github/workflows/citation.yml
vendored
4
.github/workflows/citation.yml
vendored
@@ -5,6 +5,10 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- CITATION.cff
|
- CITATION.cff
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Validate-CITATION-cff:
|
Validate-CITATION-cff:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -22,6 +22,10 @@ on:
|
|||||||
paths-ignore:
|
paths-ignore:
|
||||||
- "docs/**"
|
- "docs/**"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze
|
name: Analyze
|
||||||
|
5
.github/workflows/documentation.yml
vendored
5
.github/workflows/documentation.yml
vendored
@@ -36,6 +36,11 @@ on:
|
|||||||
- "mkdocs.yml"
|
- "mkdocs.yml"
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@@ -26,6 +26,7 @@ In this series, we will describe:
|
|||||||
- [Part 3 - Modeling ISQ](isq-part-3-modeling-isq.md)
|
- [Part 3 - Modeling ISQ](isq-part-3-modeling-isq.md)
|
||||||
- [Part 4 - Implementing ISQ](isq-part-4-implemeting-isq.md)
|
- [Part 4 - Implementing ISQ](isq-part-4-implemeting-isq.md)
|
||||||
- [Part 5 - Benefits](isq-part-5-benefits.md)
|
- [Part 5 - Benefits](isq-part-5-benefits.md)
|
||||||
|
- [Part 6 - Challenges](isq-part-6-challenges.md)
|
||||||
|
|
||||||
|
|
||||||
## Terms and Definitions
|
## Terms and Definitions
|
||||||
|
@@ -28,6 +28,7 @@ library on just units or dimensions.
|
|||||||
- [Part 3 - Modeling ISQ](isq-part-3-modeling-isq.md)
|
- [Part 3 - Modeling ISQ](isq-part-3-modeling-isq.md)
|
||||||
- [Part 4 - Implementing ISQ](isq-part-4-implemeting-isq.md)
|
- [Part 4 - Implementing ISQ](isq-part-4-implemeting-isq.md)
|
||||||
- [Part 5 - Benefits](isq-part-5-benefits.md)
|
- [Part 5 - Benefits](isq-part-5-benefits.md)
|
||||||
|
- [Part 6 - Challenges](isq-part-6-challenges.md)
|
||||||
|
|
||||||
|
|
||||||
## Limitations of units-only solutions
|
## Limitations of units-only solutions
|
||||||
|
@@ -27,6 +27,7 @@ language.
|
|||||||
- Part 3 - Modeling ISQ
|
- Part 3 - Modeling ISQ
|
||||||
- [Part 4 - Implementing ISQ](isq-part-4-implemeting-isq.md)
|
- [Part 4 - Implementing ISQ](isq-part-4-implemeting-isq.md)
|
||||||
- [Part 5 - Benefits](isq-part-5-benefits.md)
|
- [Part 5 - Benefits](isq-part-5-benefits.md)
|
||||||
|
- [Part 6 - Challenges](isq-part-6-challenges.md)
|
||||||
|
|
||||||
|
|
||||||
## Dimension is not enough to describe a quantity
|
## Dimension is not enough to describe a quantity
|
||||||
|
@@ -28,6 +28,7 @@ Now, it is time to see how we can implement hierarchies of quantities of the sam
|
|||||||
- [Part 3 - Modeling ISQ](isq-part-3-modeling-isq.md)
|
- [Part 3 - Modeling ISQ](isq-part-3-modeling-isq.md)
|
||||||
- Part 4 - Implementing ISQ
|
- Part 4 - Implementing ISQ
|
||||||
- [Part 5 - Benefits](isq-part-5-benefits.md)
|
- [Part 5 - Benefits](isq-part-5-benefits.md)
|
||||||
|
- [Part 6 - Challenges](isq-part-6-challenges.md)
|
||||||
|
|
||||||
|
|
||||||
## Modeling a hierarchy of kind _length_
|
## Modeling a hierarchy of kind _length_
|
||||||
|
@@ -26,6 +26,7 @@ how our ISQ model elegantly addresses the remaining problems.
|
|||||||
- [Part 3 - Modeling ISQ](isq-part-3-modeling-isq.md)
|
- [Part 3 - Modeling ISQ](isq-part-3-modeling-isq.md)
|
||||||
- [Part 4 - Implementing ISQ](isq-part-4-implemeting-isq.md)
|
- [Part 4 - Implementing ISQ](isq-part-4-implemeting-isq.md)
|
||||||
- Part 5 - Benefits
|
- Part 5 - Benefits
|
||||||
|
- [Part 6 - Challenges](isq-part-6-challenges.md)
|
||||||
|
|
||||||
|
|
||||||
## Generic but safe interfaces
|
## Generic but safe interfaces
|
||||||
|
530
docs/blog/posts/isq-part-6-challenges.md
Normal file
530
docs/blog/posts/isq-part-6-challenges.md
Normal file
@@ -0,0 +1,530 @@
|
|||||||
|
---
|
||||||
|
date: 2024-11-11
|
||||||
|
authors:
|
||||||
|
- mpusz
|
||||||
|
categories:
|
||||||
|
- Metrology
|
||||||
|
comments: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# International System of Quantities (ISQ): Part 6 - Challenges
|
||||||
|
|
||||||
|
This article might be the last one from our series. This time, we will discuss the challenges and
|
||||||
|
issues with modeling of the ISQ in software.
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
## Articles from this series
|
||||||
|
|
||||||
|
- [Part 1 - Introduction](isq-part-1-introduction.md)
|
||||||
|
- [Part 2 - Problems when ISQ is not used](isq-part-2-problems-when-isq-is-not-used.md)
|
||||||
|
- [Part 3 - Modeling ISQ](isq-part-3-modeling-isq.md)
|
||||||
|
- [Part 4 - Implementing ISQ](isq-part-4-implemeting-isq.md)
|
||||||
|
- [Part 5 - Benefits](isq-part-5-benefits.md)
|
||||||
|
- Part 6 - Challenges
|
||||||
|
|
||||||
|
|
||||||
|
## Ambiguity
|
||||||
|
|
||||||
|
Some quantity names are ambiguous. It is not a problem of ISQ but of the English language and
|
||||||
|
the way we communicate things. When I say: "Every _width_ is a _length_, but not every _length_
|
||||||
|
is a _width_" most people understand this right away. However, the same people trying to model
|
||||||
|
[our 3D box problem](isq-part-2-problems-when-isq-is-not-used.md#various-quantities-of-the-same-dimension-and-kinds)
|
||||||
|
try to do it as follows:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Box {
|
||||||
|
quantity<isq::length[m]> length_;
|
||||||
|
quantity<isq::width[m]> width_;
|
||||||
|
quantity<isq::height[m]> height_;
|
||||||
|
public:
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
This looks correct at first sight. Only when we think about the sentence mentioned above will
|
||||||
|
we realize that this implementation has a problem. We intended to specify three orthogonal
|
||||||
|
dimensions of the box, each of which will be a strong quantity that is not convertible to others.
|
||||||
|
But we've failed.
|
||||||
|
|
||||||
|
When we look at the
|
||||||
|
[tree of quantities of length](isq-part-4-implemeting-isq.md#modeling-a-hierarchy-of-kind-length)
|
||||||
|
we immediately see that both _width_ and _height_ are special _lengths_ so they are convertible to
|
||||||
|
it.
|
||||||
|
|
||||||
|
To implement our task correctly, we had to define and use a new quantity of kind _length_:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
inline constexpr struct horizontal_length final : quantity_spec<isq::length> {} horizontal_length;
|
||||||
|
```
|
||||||
|
|
||||||
|
We do not propose adding _horizontal length_ to ISO 80000-3. There are probably other similar
|
||||||
|
cases as well, but so far, this was the most common and obvious one we've encountered.
|
||||||
|
|
||||||
|
|
||||||
|
## No common quantities
|
||||||
|
|
||||||
|
ISO 80000-1:2009 explicitly states:
|
||||||
|
|
||||||
|
!!! quote
|
||||||
|
|
||||||
|
Two or more quantities cannot be added or subtracted unless they belong to the same category
|
||||||
|
of mutually comparable quantities.
|
||||||
|
|
||||||
|
This means that we should be able to add and subtract any quantities as long as they belong to
|
||||||
|
the same kind. However, ISO/IEC documents do not provide any rules or even hints about what should
|
||||||
|
be the result of such operations.
|
||||||
|
|
||||||
|
If it is possible to add _radius_ and _distance_, then what quantity should be provided
|
||||||
|
in return? Undoubtedly, the resulting quantity type can't be the same as any of the arguments.
|
||||||
|
It is not a _radius_ or _distance_. It is some closer unspecified _length_, though.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
|
||||||
|
Finding the correct solution took us many months of experimentation and implementation.
|
||||||
|
Based on the hierarchy tree of quantities, we can define
|
||||||
|
[conversion rules](isq-part-3-modeling-isq.md#converting-between-quantities-of-the-same-kind)
|
||||||
|
and what a [common quantity should be](isq-part-3-modeling-isq.md#comparing-adding-and-subtracting-quantities-of-the-same-kind).
|
||||||
|
|
||||||
|
|
||||||
|
## Lack of consistency
|
||||||
|
|
||||||
|
The documents of ISO/IEC 80000 are not 100% consistent, and programming languages do not like
|
||||||
|
inconsistencies.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
- _time_ is mentioned as a base quantity of ISQ in ISO 80000-1 chapter 4.5.
|
||||||
|
- ISO 80000-3 "Space and time", does not define a quantity of _time_. It provides a _duration_
|
||||||
|
quantity (item 3-9) with symbol _t_, and states in the Remarks section:
|
||||||
|
|
||||||
|
!!! quote
|
||||||
|
|
||||||
|
Duration is often just called time.
|
||||||
|
|
||||||
|
- Other parts (e.g., IEC 80000-6 "Electromagnetism") often say:
|
||||||
|
|
||||||
|
!!! quote
|
||||||
|
|
||||||
|
... _t_ is time (ISO 80000-3)
|
||||||
|
|
||||||
|
To be consistent, ISO/IEC should either:
|
||||||
|
|
||||||
|
- change ISO 80000-1 chapter 4.5 and all references in other parts to use _duration_ (unlikely),
|
||||||
|
- or add _time_ as an alias name to _duration_ in the definition 3-9 of ISO 80000-3.
|
||||||
|
|
||||||
|
|
||||||
|
## Lack of definitions
|
||||||
|
|
||||||
|
ISQ defines derived quantities in terms of other quantities provided in the series. However, some
|
||||||
|
definitions mention quantities that are not defined in the ISQ at all.
|
||||||
|
|
||||||
|
For example, _weight_ is defined as $F_\textsf{g} = m\;g$, where $m$ is the _mass_ of the body
|
||||||
|
(item 4-1 of ISO 80000-4 "Mechanics"), and $g$ is the _local acceleration of free fall_ (ISO 80000-3).
|
||||||
|
|
||||||
|
The problem here is that ISO 80000-3 never defines a quantity with a symbol $g$ or named as a
|
||||||
|
_local acceleration of free fall_. The closest one we have is _acceleration_ (item 3-11) with
|
||||||
|
a symbol $a$.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
|
||||||
|
To have a proper definition of _weight_ in **mp-units** that is not defined in terms of just
|
||||||
|
any kind of _acceleration_, we have added `isq::acceleration_of_free_fall` in our definitions
|
||||||
|
as an extension to the original ISQ set of quantities.
|
||||||
|
|
||||||
|
|
||||||
|
## Not engineering-friendly
|
||||||
|
|
||||||
|
Many quantities have proper physical definitions, but they are sometimes not engineering-friendly.
|
||||||
|
|
||||||
|
For example, _velocity_ is defined as a rate of change of _position vector_
|
||||||
|
$v = \frac{\textsf{d}r}{\textsf{d}t}$, where $r$ denotes the _position vector_ (item 3‑1.10) and
|
||||||
|
$t$ the _duration_ (item 3‑9).
|
||||||
|
|
||||||
|
Next, a _speed_ quantity is defined as the magnitude of _velocity_. Despite being
|
||||||
|
physically correct, requiring every _speed_ to be derived from the vector quantity of _velocity_
|
||||||
|
in software would be inconvenient. If this was the only case, people would always need to use
|
||||||
|
vector representations of _position vectors_ to talk about _speeds_, which differs from what we
|
||||||
|
do in practice. In practice, we divide any kind of _length_ by _time_ to get some kind of _speed_.
|
||||||
|
|
||||||
|
ISO 80000-3 provides _length_, _height_, _distance_ and other quantities of kind _length_ that when
|
||||||
|
divided by _duration_ can serve really well to calculate _speed_.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
|
||||||
|
This is why in **mp-units**, we decided to divert from the official definition of _speed_ and
|
||||||
|
define it as:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
inline constexpr struct speed : quantity_spec<speed, length / time> {} speed;
|
||||||
|
```
|
||||||
|
|
||||||
|
This allows us to create a quantity of kind _speed_ from any quantity of _length_ divided
|
||||||
|
by _time_.
|
||||||
|
|
||||||
|
Additionally, it is essential to note that for the needs of our library, defining _velocity_
|
||||||
|
as `position_vector / duration` would be wrong. We miss the delta part here. Even though it is
|
||||||
|
not mentioned in ISO 80000-3, the delta of _position vectors_ is actually a _displacement_.
|
||||||
|
This is why our _velocity_ is defined as:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
inline constexpr struct velocity : quantity_spec<speed, displacement / duration> {} velocity;
|
||||||
|
```
|
||||||
|
|
||||||
|
Please also note that _velocity_ is defined as a more specialized quantity of _speed_.
|
||||||
|
|
||||||
|
|
||||||
|
## Affine space agnostic
|
||||||
|
|
||||||
|
[The affine space](../../users_guide/framework_basics/the_affine_space.md) is a powerful
|
||||||
|
abstraction, allowing us to model some problems safer or more accurately. It has two types of
|
||||||
|
entities:
|
||||||
|
|
||||||
|
- point - a position specified with coordinate values (e.g., location, address, etc.),
|
||||||
|
- displacement vector - the difference between two points (e.g., shift, offset, displacement,
|
||||||
|
duration, etc.).
|
||||||
|
|
||||||
|
Vectors support all the arithmetics operations, but points have some limitations. It is not
|
||||||
|
possible to:
|
||||||
|
|
||||||
|
- add two _points_,
|
||||||
|
- subtract a _point_ from a _vector_,
|
||||||
|
- multiply nor divide _points_ with anything else.
|
||||||
|
|
||||||
|
ISO/IEC series does not acknowledge this abstraction even though it would be really useful in
|
||||||
|
some cases. Let's discuss the following two examples.
|
||||||
|
|
||||||
|
What does it mean to add two _altitudes_? It is not meaningful. On the other hand, subtracting
|
||||||
|
those should not result in an _altitude_, but in a quantity of _height_. Adding or
|
||||||
|
subtracting _height_ to/from _altitude_ results in _altitude_. Subtracting _altitude_ from
|
||||||
|
_height_ is meaningless again. Those quantities clearly model affine space. Maybe this is why
|
||||||
|
ISQ defines them as one quantity type _height_/_depth_/_altitude_?
|
||||||
|
|
||||||
|
What does it mean to add two _position vectors_? It is not meaningful again. However, subtracting
|
||||||
|
those results in a _displacement_ as we noted in the previous chapter. Adding or subtracting
|
||||||
|
_displacement_ to/from _position vector_ results in another _position vector_, and subtracting
|
||||||
|
_position vector_ from _displacement_ does not have physical sense. Again, those quantities
|
||||||
|
perfectly model affine space. However, this time, those are defined as separate and independent
|
||||||
|
quantities (i.e., displacement is not modeled as delta _position vector_ or _position vector_
|
||||||
|
is not modeled as a _displacement_ from the origin of a coordinate system).
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
|
||||||
|
Currently, **mp-units** does not enforce the affine space behavior for such quantities.
|
||||||
|
Today, subtracting two _altitudes_ result in an _altitude_ and subtracting two
|
||||||
|
_position vectors_ result in a _position vector_. However, we plan to support automatic
|
||||||
|
conversion to a proper quantity type on subtraction and addition shortly.
|
||||||
|
|
||||||
|
|
||||||
|
## Non-negative quantities
|
||||||
|
|
||||||
|
Some quantities in the ISQ are defined as non-negative. This is a really interesting property that
|
||||||
|
may be checked at runtime to increase safety. However, the number of such quantities is minimal.
|
||||||
|
From a few hundred quantities provided by the ISO/IEC series, only the following have this property
|
||||||
|
mentioned explicitly:
|
||||||
|
|
||||||
|
- _width_/_breadth_,
|
||||||
|
- _thickness_,
|
||||||
|
- _diameter_,
|
||||||
|
- _radius_.
|
||||||
|
|
||||||
|
If _height_ was defined separately from _altitude_, it could probably also join this group.
|
||||||
|
|
||||||
|
Let's think a bit more about this. What does it mean that a quantity is non-negative? Indeed,
|
||||||
|
it is hard to imagine something of a negative _width_ or _radius_. However, if we subtract
|
||||||
|
two _widths_, the second one may be larger. This will result in a negative
|
||||||
|
quantity of _width_, violating our precondition. So, is it non-negative or not?
|
||||||
|
|
||||||
|
Again, we have to talk about the affine space abstractions. Every empirical measurement can be
|
||||||
|
expressed as a point. Such points for some quantities may be non-negative indeed.
|
||||||
|
|
||||||
|
Non-negative quantities do not end on the ones provided above. For example, _speed_ is a good
|
||||||
|
example here as well. In general, all magnitudes of vector quantities will also have this property.
|
||||||
|
|
||||||
|
When subtracting two points, we end up with a delta/displacement type, which may be negative
|
||||||
|
even for quantities listed as non-negative in the ISQ. As stated in the previous chapter,
|
||||||
|
having affine space abstractions acknowledged in ISQ would greatly help here.
|
||||||
|
|
||||||
|
|
||||||
|
## Lack of quantity recipes
|
||||||
|
|
||||||
|
Definition of many derived quantities provides their recipes in the form of
|
||||||
|
[quantity equations](../../appendix/glossary.md#quantity-equation) (e.g., _weight_ equation
|
||||||
|
in the previous chapter). However, some of them do not. Instead, they often provide a very
|
||||||
|
generic description.
|
||||||
|
|
||||||
|
For example, _force_ is defined as:
|
||||||
|
|
||||||
|
!!! quote
|
||||||
|
|
||||||
|
vector (ISO 80000-2) quantity describing interaction between bodies or particles.
|
||||||
|
|
||||||
|
This is not helpful for programming languages that like explicit definitions. Different
|
||||||
|
vendors may interpret the above differently, which will result in different implementations that
|
||||||
|
will not be compatible with each other.
|
||||||
|
|
||||||
|
As the derived quantity of _force_ has to be a vector quantity, it has to be defined in terms of
|
||||||
|
at least one other vector quantity. We have a few to choose from:
|
||||||
|
|
||||||
|
- _displacement_ ($\Delta{r}$),
|
||||||
|
- _velocity_ ($v$),
|
||||||
|
- _acceleration_ ($a$).
|
||||||
|
|
||||||
|
It is not stated explicitly in ISQ which one of those should be used and how.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
|
||||||
|
In **mp-units** we decided to define _force_ as $F = m\;a$.
|
||||||
|
|
||||||
|
|
||||||
|
## Lack of generic quantities and name conflicts
|
||||||
|
|
||||||
|
In the previous chapter, we complained about some definitions needing to be more complex or generic.
|
||||||
|
On the other hand, we also lack some generic quantities in ISQ that could serve as a root for
|
||||||
|
a quantity hierarchy tree.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
- ISO 80000-4 "Mechanics" defines _power <mechanics>_ as $P = F\;v$ (scalar product of force $F$
|
||||||
|
(item 4-9.1) acting to a body and its velocity $v$ (ISO 80000-3)),
|
||||||
|
- ISO 80000-6 "Electromagnetism" defines _power_ as $p = u\;i$ (scalar quantity given by the
|
||||||
|
product of _instantaneous voltage_ $u$ (item 6-11.3) and _instantaneous electric current_ $i$
|
||||||
|
(item 6-1)).
|
||||||
|
|
||||||
|
First, the above definitions have somehow conflicting names which makes it hard for the programming
|
||||||
|
languages to name them consistently by different vendors.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
|
||||||
|
In **mp-units**, we chose `mechanical_power` and `electromagnetism_power` for those.
|
||||||
|
|
||||||
|
Second, we do not have any other more generic definition of _power_ to put above those in the tree.
|
||||||
|
Not having it makes it hard to answer what should be the result of:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
quantity q = isq::mechanical_power(42 * W) + isq::electromagnetism_power(60 * W);
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
|
||||||
|
To solve the above problem, we have added `isq::power` in **mp-units**, that has a really
|
||||||
|
generic definition of:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
inline constexpr struct power : quantity_spec<mass* pow<2>(length) / pow<3>(time)> {} power;
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Invalid definitions order
|
||||||
|
|
||||||
|
_Energy_ is defined a bit better than _power_, but still not without issues.
|
||||||
|
|
||||||
|
The first time ISQ mentions _energy_ is in the ISO 80000-4 "Mechanics". It defines
|
||||||
|
_potential energy_, _kinetic energy_, and a _mechanical energy_ as the sum of the first two.
|
||||||
|
Right after that a _mechanical work/work_ is defined.
|
||||||
|
|
||||||
|
Then ISO 80000-5 "Thermodynamics" defines _energy <thermodynamics>_ as:
|
||||||
|
|
||||||
|
!!! quote
|
||||||
|
|
||||||
|
ability of a system to do work (ISO 80000-4).
|
||||||
|
|
||||||
|
Next, _internal energy/thermodynamic energy_ is defined in terms of the change of heat.
|
||||||
|
|
||||||
|
From the above, it seems that what is called _energy <thermodynamics>_ should actually be
|
||||||
|
the root of our tree and probably be provided in Part 4 before the _mechanical energy_ is defined.
|
||||||
|
|
||||||
|
|
||||||
|
## Hierarchies of derived quantities
|
||||||
|
|
||||||
|
Derived quantities of the same kind are often independently defined in the ISQ. The ISO/IEC 80000
|
||||||
|
series often does not suggest any hierarchy between those. Even more, it states:
|
||||||
|
|
||||||
|
!!! quote "ISO/IEC Guide 99"
|
||||||
|
|
||||||
|
The division of ‘quantity’ according to ‘kind of quantity’ is, to some extent, arbitrary.
|
||||||
|
|
||||||
|
Because of this, it is unknown or ambiguous how to form a hierarchy tree for such quantities.
|
||||||
|
|
||||||
|
To get some sense of the complexity here, let's look again at our tree of quantities of a kind
|
||||||
|
_energy_:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
energy["<b>energy</b><br><i>(mass * length<sup>2</sup> / time<sup>2</sup>)</i><br>[J]"]
|
||||||
|
energy --- mechanical_energy["<b>mechanical_energy</b>"]
|
||||||
|
mechanical_energy --- potential_energy["<b>potential_energy</b>"]
|
||||||
|
mechanical_energy --- kinetic_energy["<b>kinetic_energy</b>"]
|
||||||
|
energy --- enthalpy["<b>enthalpy</b>"]
|
||||||
|
enthalpy --- internal_energy["<b>internal_energy</b> / <b>thermodynamic_energy</b>"]
|
||||||
|
internal_energy --- Helmholtz_energy["<b>Helmholtz_energy</b> / <b>Helmholtz_function</b>"]
|
||||||
|
enthalpy --- Gibbs_energy["<b>Gibbs_energy</b> / <b>Gibbs_function</b>"]
|
||||||
|
energy --- active_energy["<b>active_energy</b>"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Not being exact means that every vendor may implement it differently. This will result in:
|
||||||
|
|
||||||
|
- different convertibility rules among quantities:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static_assert(implicitly_convertible(isq::potential_energy, isq::mechanical_energy));
|
||||||
|
static_assert(explicitly_convertible(isq::mechanical_energy, isq::potential_energy));
|
||||||
|
```
|
||||||
|
|
||||||
|
- different common quantities resulting from the arithmetics on various quantities of the same
|
||||||
|
kind:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static_assert((isq::potential_energy(1 * J) + isq::kinetic_energy(1 * J)).quantity_spec == isq::mechanical_energy);
|
||||||
|
```
|
||||||
|
|
||||||
|
It would be great if ISQ could provide specific division of quantities into kinds and more
|
||||||
|
information about the position of each quantity within the hierarchy of quantities of
|
||||||
|
the same kind.
|
||||||
|
|
||||||
|
!!! important
|
||||||
|
|
||||||
|
We can try to do this by ourselves, but it is tough. Probably no one, for sure we are
|
||||||
|
not, is an expert in all the fields of ISO/IEC 80000 applicability.
|
||||||
|
|
||||||
|
We need the help of subject matter experts who will help us build those trees for their domains
|
||||||
|
and then verify that everything works as expected.
|
||||||
|
|
||||||
|
|
||||||
|
## The same or a different kind?
|
||||||
|
|
||||||
|
Some quantities are more complicated than others. For example, _power_ has:
|
||||||
|
|
||||||
|
- scalar quantities expressed in:
|
||||||
|
- W (watts) (e.g., _mechanical power_, _active power_),
|
||||||
|
- VA (volt-ampere) (e.g., _apparent power_),
|
||||||
|
- var (e.g., _reactive power_),
|
||||||
|
- complex quantities expressed in VA (volt-ampere) (e.g., _complex power_).
|
||||||
|
|
||||||
|
How should we model this? Maybe those should be two or three independent trees of quantities, each
|
||||||
|
having its own unit?
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
power["<b>power</b><br><i>(mass * length<sup>2</sup> / time<sup>3</sup>)</i><br>[W]"]
|
||||||
|
power --- mechanical_power["<b>mechanical_power</b><br><i>(scalar_product(force, velocity))</i>"]
|
||||||
|
power --- electromagnetism_power["<b>electromagnetism_power</b> | <b>instantaneous_power</b><br><i>(instantaneous_voltage * instantaneous_electric_current)</i>"]
|
||||||
|
power --- active_power["<b>active_power</b><br><i>(1 / period * instantaneous_power * time)<br>(re(complex_power))</i>"]
|
||||||
|
|
||||||
|
nonactive_power["<b>nonactive_power</b><br><i>(mass * length<sup>2</sup> / time<sup>3</sup>)</i><br>[VA]"]
|
||||||
|
nonactive_power --- reactive_power["<b>reactive_power</b><br><i>(im(complex_power))</i><br>[var]"]
|
||||||
|
|
||||||
|
complex_power["<b>complex_power</b><br>{complex}<br><i>(voltage_phasor * electric_current_phasor)<br>(active_power + j * reactive_power)</i><br>[VA]"]
|
||||||
|
complex_power --- apparent_power["<b>apparent_power</b><br><i>(voltage * electric_current)<br>(mod(complex_power))</i>"]
|
||||||
|
```
|
||||||
|
|
||||||
|
This will mean that we will not be able to add or compare _active power_, _reactive power_, and
|
||||||
|
_apparent power_, which probably makes a lot of sense. However, it also means that the following
|
||||||
|
will fail to compile:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
quantity apparent = isq::apparent_power(100 * VA);
|
||||||
|
quantity active = isq::active_power(60 * W);
|
||||||
|
quantity<isq::nonactive_power[VA]> q = sqrt(pow<2>(apparent) - pow<2>(active)); // Compile-time error
|
||||||
|
```
|
||||||
|
|
||||||
|
Also the following will not work:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
quantity active = isq::active_power(60 * W);
|
||||||
|
quantity reactive = isq::reactive_power(40 * var);
|
||||||
|
quantity<isq::apparent_power[VA]> q = sqrt(pow<2>(active) + pow<2>(reactive)); // Compile-time error
|
||||||
|
```
|
||||||
|
|
||||||
|
If we want the above to work maybe we need to implement the tree as follows?
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
power["<b>power</b><br><i>(mass * length<sup>2</sup> / time<sup>3</sup>)</i><br>[W]"]
|
||||||
|
power --- mechanical_power["<b>mechanical_power</b><br><i>(scalar_product(force, velocity))</i>"]
|
||||||
|
power --- electromagnetism_power["<b>electromagnetism_power</b> | <b>instantaneous_power</b><br><i>(instantaneous_voltage * instantaneous_electric_current)</i>"]
|
||||||
|
power --- apparent_power["<b>apparent_power</b><br><i>(voltage * electric_current)<br>(mod(complex_power))</i><br>[VA]"]
|
||||||
|
apparent_power --- active_power["<b>active_power</b><br><i>(1 / period * instantaneous_power * time)<br>(re(complex_power))</i>"]
|
||||||
|
apparent_power --- nonactive_power["<b>nonactive_power</b><br><i>(sqrt(apparent_power<sup>2</sup> - active_power<sup>2</sup>))</i><br>"]
|
||||||
|
nonactive_power --- reactive_power["<b>reactive_power</b><br><i>(im(complex_power))</i><br>[var]"]
|
||||||
|
apparent_power --- complex_power["<b>complex_power</b><br>{complex}<br><i>(voltage_phasor * electric_current_phasor)<br>(active_power + j * reactive_power)</i>"]
|
||||||
|
```
|
||||||
|
|
||||||
|
However, the above allows direct addition and comparison of _active power_ and _nonactive power_,
|
||||||
|
and also will not complain if someone will try to use watt (W) as a unit of _apparent power_ or
|
||||||
|
_reactive power_.
|
||||||
|
|
||||||
|
Again, ISQ does not provide a direct answer here.
|
||||||
|
|
||||||
|
|
||||||
|
## More base quantities?
|
||||||
|
|
||||||
|
Is ISQ really based on only seven base quantities? Let's look at the definition of
|
||||||
|
_traffic intensity_ in IEC 80000-13 "Information science and technology":
|
||||||
|
|
||||||
|
!!! quote
|
||||||
|
|
||||||
|
number of simultaneously busy resources in a particular pool of resources.
|
||||||
|
|
||||||
|
It looks like a definition of a specialized dimensionless quantity or, more correctly, a quantity
|
||||||
|
of dimension one. This would not be the only such case. Even in the same Part 13, we can find
|
||||||
|
quantities like _storage capacity_ with a similar property.
|
||||||
|
|
||||||
|
Only when we look closer do we start to see differences. All dimensionless quantities, even if they
|
||||||
|
have their own dedicated units, can also be measured in a unit of one (1). This is true for
|
||||||
|
_storage capacity_ (also measured in bits), _angular measure_ (also measured in radians),
|
||||||
|
_solid angular measure (also measured in steradians), and more.
|
||||||
|
|
||||||
|
However, _traffic intensity_ can only be measured in erlangs (E), not in a unit one (1).
|
||||||
|
Does it mean that it is a "hidden" 8-th base quantity in ISQ? If so, should it have its own
|
||||||
|
dimension as well?
|
||||||
|
|
||||||
|
Angular quantities are another interesting case here. Scientists have written petitions and papers
|
||||||
|
for years to make them an additional dimension in ISQ and SI. More about this can be found in
|
||||||
|
our documentation's [Strong Angular System](../../users_guide/systems/strong_angular_system.md)
|
||||||
|
chapter.
|
||||||
|
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
ISQ is tremendous and solves many problems we had in modeling various subjects for years in
|
||||||
|
software. As a result, we have more powerful tools in our hands that allow us to deliver safer
|
||||||
|
products.
|
||||||
|
|
||||||
|
Unfortunately, ISQ, contrarily to SI, is not widely recognized, and no libraries besides
|
||||||
|
**mp-units** model it in any programming language. Keeping it behind a paywall does not help
|
||||||
|
either. We hope that posts from this series will spread in the community, raise awareness
|
||||||
|
of ISQ and its benefits, and encourage authors of other libraries to implement it in their
|
||||||
|
products.
|
||||||
|
|
||||||
|
Despite all the benefits, it is essential to realize that ISQ has many problems. International
|
||||||
|
standards should be specified in such a way that there is no room for ambiguity in their
|
||||||
|
interpretation by different parties trying to use them. As described above, this is not the case
|
||||||
|
here.
|
||||||
|
|
||||||
|
ISQ is not ready to be unambiguously modeled in software by various vendors. Here are the most
|
||||||
|
important problems to solve to allow this:
|
||||||
|
|
||||||
|
1. ISQ needs to define basic operations on quantities:
|
||||||
|
|
||||||
|
- what the result of addition and subtraction should be when arguments differ,
|
||||||
|
- convertibility rules.
|
||||||
|
|
||||||
|
2. The exact quantity equation recipe needs to be included for many derived quantities.
|
||||||
|
3. Many ISQ quantities do not provide their exact relation versus other quantities of the same
|
||||||
|
kind (no strict hierarchy).
|
||||||
|
4. Some missing quantities need to be included. Others would benefit from corrected names.
|
||||||
|
|
||||||
|
Additionally:
|
||||||
|
|
||||||
|
- extending ISQ with affine space abstractions,
|
||||||
|
- specifying more quantities as non-negative,
|
||||||
|
- adding more base quantities (i.e., _angle_)
|
||||||
|
|
||||||
|
could improve the safety of our programs and products that people depend on with their lives on
|
||||||
|
a daily basis.
|
||||||
|
|
||||||
|
I hope you enjoyed following this series and learned more about the International System
|
||||||
|
of Quantities. Please try it out in your domain and share feedback with us. We always love to
|
||||||
|
hear about the projects in which our library is being used and about use cases it helps
|
||||||
|
address.
|
@@ -16,19 +16,11 @@ work in practice.
|
|||||||
--8<-- "example/si_constants.cpp:28:40"
|
--8<-- "example/si_constants.cpp:28:40"
|
||||||
```
|
```
|
||||||
|
|
||||||
As always, we start with the inclusion of all the needed header files. After that, for
|
As always, we start with the inclusion of all the needed header files.
|
||||||
the simplicity of this example, we
|
|
||||||
[hack the character of quantities](../framework_basics/character_of_a_quantity.md#hacking-the-character)
|
|
||||||
to be able to express vector quantities with simple scalar types.
|
|
||||||
|
|
||||||
```cpp title="si_constants.cpp" linenums="14"
|
|
||||||
--8<-- "example/si_constants.cpp:42:44"
|
|
||||||
```
|
|
||||||
|
|
||||||
The main part of the example prints all of the SI-defining constants:
|
The main part of the example prints all of the SI-defining constants:
|
||||||
|
|
||||||
```cpp title="si_constants.cpp" linenums="17"
|
```cpp title="si_constants.cpp" linenums="14"
|
||||||
--8<-- "example/si_constants.cpp:45:"
|
--8<-- "example/si_constants.cpp:42:"
|
||||||
```
|
```
|
||||||
|
|
||||||
While analyzing the output of this program (provided below), we can easily notice that a direct
|
While analyzing the output of this program (provided below), we can easily notice that a direct
|
||||||
|
@@ -129,8 +129,8 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
|
|||||||
template<typename T, typename Validator, typename Char>
|
template<typename T, typename Validator, typename Char>
|
||||||
struct MP_UNITS_STD_FMT::formatter<validated_type<T, Validator>, Char> : formatter<T, Char> {
|
struct MP_UNITS_STD_FMT::formatter<validated_type<T, Validator>, Char> : formatter<T, Char> {
|
||||||
template<typename FormatContext>
|
template<typename FormatContext>
|
||||||
auto format(const validated_type<T, Validator>& v, FormatContext& ctx) const -> decltype(ctx.out())
|
auto format(const validated_type<T, Validator>& val, FormatContext& ctx) const -> decltype(ctx.out())
|
||||||
{
|
{
|
||||||
return formatter<T, Char>::format(v.value(), ctx);
|
return formatter<T, Char>::format(val.value(), ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -39,10 +39,6 @@ import mp_units;
|
|||||||
#include <mp-units/systems/si.h>
|
#include <mp-units/systems/si.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<class T>
|
|
||||||
requires mp_units::is_scalar<T>
|
|
||||||
constexpr bool mp_units::is_vector<T> = true;
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
using namespace mp_units;
|
using namespace mp_units;
|
||||||
|
@@ -91,6 +91,7 @@ if(NOT ${projectPrefix}API_FREESTANDING)
|
|||||||
include/mp-units/bits/fmt.h
|
include/mp-units/bits/fmt.h
|
||||||
include/mp-units/bits/requires_hosted.h
|
include/mp-units/bits/requires_hosted.h
|
||||||
include/mp-units/ext/format.h
|
include/mp-units/ext/format.h
|
||||||
|
include/mp-units/cartesian_vector.h
|
||||||
include/mp-units/complex.h
|
include/mp-units/complex.h
|
||||||
include/mp-units/format.h
|
include/mp-units/format.h
|
||||||
include/mp-units/math.h
|
include/mp-units/math.h
|
||||||
|
230
src/core/include/mp-units/cartesian_vector.h
Normal file
230
src/core/include/mp-units/cartesian_vector.h
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
// 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/requires_hosted.h>
|
||||||
|
//
|
||||||
|
#include <mp-units/bits/module_macros.h>
|
||||||
|
#include <mp-units/framework/customization_points.h>
|
||||||
|
|
||||||
|
#if MP_UNITS_HOSTED
|
||||||
|
#include <mp-units/bits/fmt.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MP_UNITS_IN_MODULE_INTERFACE
|
||||||
|
#ifdef MP_UNITS_IMPORT_STD
|
||||||
|
import std;
|
||||||
|
#else
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <type_traits>
|
||||||
|
#if MP_UNITS_HOSTED
|
||||||
|
#include <ostream>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace mp_units {
|
||||||
|
|
||||||
|
MP_UNITS_EXPORT template<typename T = double>
|
||||||
|
class cartesian_vector {
|
||||||
|
public:
|
||||||
|
// public members required to satisfy structural type requirements :-(
|
||||||
|
T _coordinates_[3];
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
cartesian_vector() = default;
|
||||||
|
cartesian_vector(const cartesian_vector&) = default;
|
||||||
|
cartesian_vector(cartesian_vector&&) = default;
|
||||||
|
cartesian_vector& operator=(const cartesian_vector&) = default;
|
||||||
|
cartesian_vector& operator=(cartesian_vector&&) = default;
|
||||||
|
|
||||||
|
template<std::convertible_to<T> Arg1, std::convertible_to<T>... Args>
|
||||||
|
constexpr cartesian_vector(Arg1&& arg1, Args&&... args) :
|
||||||
|
_coordinates_(std::forward<Arg1>(arg1), std::forward<Args>(args)...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::convertible_to<T> U>
|
||||||
|
constexpr cartesian_vector(const cartesian_vector<U>& other) : _coordinates_{other[0], other[1], other[2]}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::convertible_to<T> U>
|
||||||
|
constexpr cartesian_vector(cartesian_vector<U>&& other) :
|
||||||
|
_coordinates_{std::move(other[0]), std::move(other[1]), std::move(other[2])}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::convertible_to<T> U>
|
||||||
|
constexpr cartesian_vector& operator=(const cartesian_vector<U>& other)
|
||||||
|
{
|
||||||
|
_coordinates_[0] = other[0];
|
||||||
|
_coordinates_[1] = other[1];
|
||||||
|
_coordinates_[2] = other[2];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::convertible_to<T> U>
|
||||||
|
constexpr cartesian_vector& operator=(cartesian_vector<U>&& other)
|
||||||
|
{
|
||||||
|
_coordinates_[0] = std::move(other[0]);
|
||||||
|
_coordinates_[1] = std::move(other[1]);
|
||||||
|
_coordinates_[2] = std::move(other[2]);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr T magnitude() const
|
||||||
|
requires treat_as_floating_point<T>
|
||||||
|
{
|
||||||
|
return std::hypot(_coordinates_[0], _coordinates_[1], _coordinates_[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr cartesian_vector unit() const
|
||||||
|
requires treat_as_floating_point<T>
|
||||||
|
{
|
||||||
|
return *this / magnitude();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr T& operator[](std::size_t i) { return _coordinates_[i]; }
|
||||||
|
[[nodiscard]] constexpr const T& operator[](std::size_t i) const { return _coordinates_[i]; }
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr cartesian_vector operator+() const { return *this; }
|
||||||
|
[[nodiscard]] constexpr cartesian_vector operator-() const
|
||||||
|
{
|
||||||
|
return {-_coordinates_[0], -_coordinates_[1], -_coordinates_[2]};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::same_as<T> U, typename V>
|
||||||
|
requires requires(U u, V v) { u + v; }
|
||||||
|
[[nodiscard]] friend constexpr auto operator+(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
|
||||||
|
{
|
||||||
|
return ::mp_units::cartesian_vector{lhs._coordinates_[0] + rhs._coordinates_[0],
|
||||||
|
lhs._coordinates_[1] + rhs._coordinates_[1],
|
||||||
|
lhs._coordinates_[2] + rhs._coordinates_[2]};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::same_as<T> U, typename V>
|
||||||
|
requires requires(U u, V v) { u - v; }
|
||||||
|
[[nodiscard]] friend constexpr auto operator-(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
|
||||||
|
{
|
||||||
|
return ::mp_units::cartesian_vector{lhs._coordinates_[0] - rhs._coordinates_[0],
|
||||||
|
lhs._coordinates_[1] - rhs._coordinates_[1],
|
||||||
|
lhs._coordinates_[2] - rhs._coordinates_[2]};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::same_as<T> U>
|
||||||
|
requires requires(U u, T t) { u* t; }
|
||||||
|
[[nodiscard]] friend constexpr auto operator*(const cartesian_vector<U>& lhs, const T& rhs)
|
||||||
|
{
|
||||||
|
return ::mp_units::cartesian_vector{lhs._coordinates_[0] * rhs, lhs._coordinates_[1] * rhs,
|
||||||
|
lhs._coordinates_[2] * rhs};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::same_as<T> U>
|
||||||
|
requires requires(T t, U u) { t* u; }
|
||||||
|
[[nodiscard]] friend constexpr auto operator*(const T& lhs, const cartesian_vector<U>& rhs)
|
||||||
|
{
|
||||||
|
return rhs * lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::same_as<T> U>
|
||||||
|
requires requires(U u, T t) { u / t; }
|
||||||
|
[[nodiscard]] friend constexpr auto operator/(const cartesian_vector<U>& lhs, const T& rhs)
|
||||||
|
{
|
||||||
|
return ::mp_units::cartesian_vector{lhs._coordinates_[0] / rhs, lhs._coordinates_[1] / rhs,
|
||||||
|
lhs._coordinates_[2] / rhs};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::same_as<T> U, std::equality_comparable_with<U> V>
|
||||||
|
[[nodiscard]] friend constexpr bool operator==(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
|
||||||
|
{
|
||||||
|
return lhs._coordinates_[0] == rhs._coordinates_[0] && lhs._coordinates_[1] == rhs._coordinates_[1] &&
|
||||||
|
lhs._coordinates_[2] == rhs._coordinates_[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr T norm(const cartesian_vector& vec)
|
||||||
|
requires treat_as_floating_point<T>
|
||||||
|
{
|
||||||
|
return vec.magnitude();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr cartesian_vector unit_vector(const cartesian_vector& vec)
|
||||||
|
requires treat_as_floating_point<T>
|
||||||
|
{
|
||||||
|
return vec.unit();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::same_as<T> U, typename V>
|
||||||
|
requires requires(U u, V v, decltype(u * v) t) {
|
||||||
|
u* v;
|
||||||
|
t + t;
|
||||||
|
}
|
||||||
|
[[nodiscard]] friend constexpr auto scalar_product(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
|
||||||
|
{
|
||||||
|
return lhs._coordinates_[0] * rhs._coordinates_[0] + lhs._coordinates_[1] * rhs._coordinates_[1] +
|
||||||
|
lhs._coordinates_[2] * rhs._coordinates_[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::same_as<T> U, typename V>
|
||||||
|
requires requires(U u, V v, decltype(u * v) t) {
|
||||||
|
u* v;
|
||||||
|
t - t;
|
||||||
|
}
|
||||||
|
[[nodiscard]] friend constexpr auto vector_product(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
|
||||||
|
{
|
||||||
|
return ::mp_units::cartesian_vector{
|
||||||
|
lhs._coordinates_[1] * rhs._coordinates_[2] - lhs._coordinates_[2] * rhs._coordinates_[1],
|
||||||
|
lhs._coordinates_[2] * rhs._coordinates_[0] - lhs._coordinates_[0] * rhs._coordinates_[2],
|
||||||
|
lhs._coordinates_[0] * rhs._coordinates_[1] - lhs._coordinates_[1] * rhs._coordinates_[0]};
|
||||||
|
}
|
||||||
|
|
||||||
|
#if MP_UNITS_HOSTED
|
||||||
|
friend constexpr std::ostream& operator<<(std::ostream& os, const cartesian_vector& vec)
|
||||||
|
{
|
||||||
|
return os << '[' << vec[0] << ", " << vec[1] << ", " << vec[2] << ']';
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Arg, typename... Args>
|
||||||
|
requires(sizeof...(Args) <= 2) && requires { typename std::common_type_t<Arg, Args...>; }
|
||||||
|
cartesian_vector(Arg, Args...) -> cartesian_vector<std::common_type_t<Arg, Args...>>;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
constexpr bool is_vector<cartesian_vector<T>> = true;
|
||||||
|
|
||||||
|
} // namespace mp_units
|
||||||
|
|
||||||
|
#if MP_UNITS_HOSTED
|
||||||
|
// TODO use parse and use formatter for the underlying type
|
||||||
|
template<typename T, typename Char>
|
||||||
|
struct MP_UNITS_STD_FMT::formatter<mp_units::cartesian_vector<T>, Char> :
|
||||||
|
formatter<std::basic_string_view<Char>, Char> {
|
||||||
|
template<typename FormatContext>
|
||||||
|
auto format(const mp_units::cartesian_vector<T>& vec, FormatContext& ctx) const
|
||||||
|
{
|
||||||
|
return format_to(ctx.out(), "[{}, {}, {}]", vec[0], vec[1], vec[2]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
@@ -28,6 +28,7 @@
|
|||||||
#include <mp-units/framework.h>
|
#include <mp-units/framework.h>
|
||||||
|
|
||||||
#if MP_UNITS_HOSTED
|
#if MP_UNITS_HOSTED
|
||||||
|
#include <mp-units/cartesian_vector.h>
|
||||||
#include <mp-units/complex.h>
|
#include <mp-units/complex.h>
|
||||||
#include <mp-units/format.h>
|
#include <mp-units/format.h>
|
||||||
#include <mp-units/math.h>
|
#include <mp-units/math.h>
|
||||||
|
@@ -90,16 +90,16 @@ public:
|
|||||||
constexpr T* data() noexcept { return data_; }
|
constexpr T* data() noexcept { return data_; }
|
||||||
constexpr const T* data() const noexcept { return data_; }
|
constexpr const T* data() const noexcept { return data_; }
|
||||||
|
|
||||||
constexpr reference push_back(const T& v)
|
constexpr reference push_back(const T& val)
|
||||||
requires std::constructible_from<T, const T&>
|
requires std::constructible_from<T, const T&>
|
||||||
{
|
{
|
||||||
return emplace_back(v);
|
return emplace_back(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr reference push_back(T&& v)
|
constexpr reference push_back(T&& val)
|
||||||
requires std::constructible_from<T, T&&>
|
requires std::constructible_from<T, T&&>
|
||||||
{
|
{
|
||||||
return emplace_back(std::forward<T&&>(v));
|
return emplace_back(std::forward<T&&>(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
|
@@ -174,21 +174,21 @@ public:
|
|||||||
|
|
||||||
template<typename FwdValue, Reference R2>
|
template<typename FwdValue, Reference R2>
|
||||||
requires detail::SameValueAs<R2{}, R, std::remove_cvref_t<FwdValue>, Rep>
|
requires detail::SameValueAs<R2{}, R, std::remove_cvref_t<FwdValue>, Rep>
|
||||||
constexpr quantity(FwdValue&& v, R2) : numerical_value_is_an_implementation_detail_(std::forward<FwdValue>(v))
|
constexpr quantity(FwdValue&& val, R2) : numerical_value_is_an_implementation_detail_(std::forward<FwdValue>(val))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FwdValue, Reference R2, typename Value = std::remove_cvref_t<FwdValue>>
|
template<typename FwdValue, Reference R2, typename Value = std::remove_cvref_t<FwdValue>>
|
||||||
requires(!detail::SameValueAs<R2{}, R, Value, Rep>) &&
|
requires(!detail::SameValueAs<R2{}, R, Value, Rep>) &&
|
||||||
detail::QuantityConvertibleTo<quantity<R2{}, Value>, quantity>
|
detail::QuantityConvertibleTo<quantity<R2{}, Value>, quantity>
|
||||||
constexpr quantity(FwdValue&& v, R2) : quantity(quantity<R2{}, Value>{std::forward<FwdValue>(v), R2{}})
|
constexpr quantity(FwdValue&& val, R2) : quantity(quantity<R2{}, Value>{std::forward<FwdValue>(val), R2{}})
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<detail::ValuePreservingTo<Rep> FwdValue>
|
template<detail::ValuePreservingTo<Rep> FwdValue>
|
||||||
requires(unit == ::mp_units::one)
|
requires(unit == ::mp_units::one)
|
||||||
constexpr explicit(false) quantity(FwdValue&& v) :
|
constexpr explicit(false) quantity(FwdValue&& val) :
|
||||||
numerical_value_is_an_implementation_detail_(std::forward<FwdValue>(v))
|
numerical_value_is_an_implementation_detail_(std::forward<FwdValue>(val))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,9 +214,9 @@ public:
|
|||||||
|
|
||||||
template<detail::ValuePreservingTo<Rep> FwdValue>
|
template<detail::ValuePreservingTo<Rep> FwdValue>
|
||||||
requires(unit == ::mp_units::one)
|
requires(unit == ::mp_units::one)
|
||||||
constexpr quantity& operator=(FwdValue&& v)
|
constexpr quantity& operator=(FwdValue&& val)
|
||||||
{
|
{
|
||||||
numerical_value_is_an_implementation_detail_ = std::forward<FwdValue>(v);
|
numerical_value_is_an_implementation_detail_ = std::forward<FwdValue>(val);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,11 +422,11 @@ public:
|
|||||||
requires(!Quantity<Value>) && requires(rep a, Value b) {
|
requires(!Quantity<Value>) && requires(rep a, Value b) {
|
||||||
{ a *= b } -> std::same_as<rep&>;
|
{ a *= b } -> std::same_as<rep&>;
|
||||||
}
|
}
|
||||||
friend constexpr decltype(auto) operator*=(Q&& lhs, const Value& v)
|
friend constexpr decltype(auto) operator*=(Q&& lhs, const Value& val)
|
||||||
{
|
{
|
||||||
// TODO use *= when compiler bug is resolved:
|
// TODO use *= when compiler bug is resolved:
|
||||||
// https://developercommunity.visualstudio.com/t/Discrepancy-in-Behavior-of-operator-an/10732445
|
// https://developercommunity.visualstudio.com/t/Discrepancy-in-Behavior-of-operator-an/10732445
|
||||||
lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ * v;
|
lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ * val;
|
||||||
return std::forward<Q>(lhs);
|
return std::forward<Q>(lhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,12 +444,12 @@ public:
|
|||||||
requires(!Quantity<Value>) && requires(rep a, Value b) {
|
requires(!Quantity<Value>) && requires(rep a, Value b) {
|
||||||
{ a /= b } -> std::same_as<rep&>;
|
{ a /= b } -> std::same_as<rep&>;
|
||||||
}
|
}
|
||||||
friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& v)
|
friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& val)
|
||||||
{
|
{
|
||||||
MP_UNITS_EXPECTS_DEBUG(v != quantity_values<Value>::zero());
|
MP_UNITS_EXPECTS_DEBUG(val != quantity_values<Value>::zero());
|
||||||
// TODO use /= when compiler bug is resolved:
|
// TODO use /= when compiler bug is resolved:
|
||||||
// https://developercommunity.visualstudio.com/t/Discrepancy-in-Behavior-of-operator-an/10732445
|
// https://developercommunity.visualstudio.com/t/Discrepancy-in-Behavior-of-operator-an/10732445
|
||||||
lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ / v;
|
lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ / val;
|
||||||
return std::forward<Q>(lhs);
|
return std::forward<Q>(lhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,17 +551,17 @@ public:
|
|||||||
template<std::derived_from<quantity> Q, typename Value>
|
template<std::derived_from<quantity> Q, typename Value>
|
||||||
requires(!Quantity<Value>) &&
|
requires(!Quantity<Value>) &&
|
||||||
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::multiplies<>, Rep, const Value&>
|
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::multiplies<>, Rep, const Value&>
|
||||||
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator*(const Q& q, const Value& v)
|
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator*(const Q& q, const Value& val)
|
||||||
{
|
{
|
||||||
return ::mp_units::quantity{q.numerical_value_ref_in(unit) * v, R};
|
return ::mp_units::quantity{q.numerical_value_ref_in(unit) * val, R};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Value, std::derived_from<quantity> Q>
|
template<typename Value, std::derived_from<quantity> Q>
|
||||||
requires(!Quantity<Value>) &&
|
requires(!Quantity<Value>) &&
|
||||||
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::multiplies<>, const Value&, Rep>
|
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::multiplies<>, const Value&, Rep>
|
||||||
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator*(const Value& v, const Q& q)
|
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator*(const Value& val, const Q& q)
|
||||||
{
|
{
|
||||||
return ::mp_units::quantity{v * q.numerical_value_ref_in(unit), R};
|
return ::mp_units::quantity{val * q.numerical_value_ref_in(unit), R};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::derived_from<quantity> Q, auto R2, typename Rep2>
|
template<std::derived_from<quantity> Q, auto R2, typename Rep2>
|
||||||
@@ -575,18 +575,18 @@ public:
|
|||||||
template<std::derived_from<quantity> Q, typename Value>
|
template<std::derived_from<quantity> Q, typename Value>
|
||||||
requires(!Quantity<Value>) &&
|
requires(!Quantity<Value>) &&
|
||||||
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::divides<>, Rep, const Value&>
|
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::divides<>, Rep, const Value&>
|
||||||
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator/(const Q& q, const Value& v)
|
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator/(const Q& q, const Value& val)
|
||||||
{
|
{
|
||||||
MP_UNITS_EXPECTS_DEBUG(v != quantity_values<Value>::zero());
|
MP_UNITS_EXPECTS_DEBUG(val != quantity_values<Value>::zero());
|
||||||
return ::mp_units::quantity{q.numerical_value_ref_in(unit) / v, R};
|
return ::mp_units::quantity{q.numerical_value_ref_in(unit) / val, R};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Value, std::derived_from<quantity> Q>
|
template<typename Value, std::derived_from<quantity> Q>
|
||||||
requires(!Quantity<Value>) &&
|
requires(!Quantity<Value>) &&
|
||||||
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::divides<>, const Value&, Rep>
|
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::divides<>, const Value&, Rep>
|
||||||
[[nodiscard]] friend constexpr QuantityOf<inverse(quantity_spec)> auto operator/(const Value& v, const Q& q)
|
[[nodiscard]] friend constexpr QuantityOf<inverse(quantity_spec)> auto operator/(const Value& val, const Q& q)
|
||||||
{
|
{
|
||||||
return ::mp_units::quantity{v / q.numerical_value_ref_in(unit), ::mp_units::one / R};
|
return ::mp_units::quantity{val / q.numerical_value_ref_in(unit), ::mp_units::one / R};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::derived_from<quantity> Q, auto R2, typename Rep2>
|
template<std::derived_from<quantity> Q, auto R2, typename Rep2>
|
||||||
|
@@ -59,7 +59,7 @@ template<QuantitySpec QS, Unit U>
|
|||||||
requires(!AssociatedUnit<U>) || UnitOf<U, QS{}>
|
requires(!AssociatedUnit<U>) || UnitOf<U, QS{}>
|
||||||
[[nodiscard]] consteval Reference auto make_reference(QS, U u)
|
[[nodiscard]] consteval Reference auto make_reference(QS, U u)
|
||||||
{
|
{
|
||||||
if constexpr (QuantityKindSpec<QS>)
|
if constexpr (requires { requires(get_quantity_spec(U{}) == QS{}); })
|
||||||
return u;
|
return u;
|
||||||
else
|
else
|
||||||
return reference<QS, U>{};
|
return reference<QS, U>{};
|
||||||
|
@@ -118,12 +118,11 @@ concept ComplexRepresentation = Complex<T> && WeaklyRegular<T> && requires(T a,
|
|||||||
{ a - b } -> Complex;
|
{ a - b } -> Complex;
|
||||||
{ a* b } -> Complex;
|
{ a* b } -> Complex;
|
||||||
{ a / b } -> Complex;
|
{ a / b } -> Complex;
|
||||||
// TBD
|
{ real(a) } -> Scalar;
|
||||||
// { re(a) } -> Scalar;
|
{ imag(a) } -> Scalar;
|
||||||
// { im(a) } -> Scalar;
|
{ abs(a) } -> Scalar;
|
||||||
// { mod(a) } -> Scalar;
|
{ arg(a) } -> Scalar;
|
||||||
// { arg(a) } -> Scalar;
|
{ conj(a) } -> Complex;
|
||||||
// { conj(a) } -> Complex;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO how to check for a complex(Scalar, Scalar) -> Complex?
|
// TODO how to check for a complex(Scalar, Scalar) -> Complex?
|
||||||
|
@@ -68,8 +68,8 @@ void to_stream_impl(std::basic_ostream<CharT, Traits>& os, const quantity<R, Rep
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename CharT, class Traits, typename T>
|
template<typename CharT, class Traits, typename T>
|
||||||
std::basic_ostream<CharT, Traits>& to_stream(std::basic_ostream<CharT, Traits>& os, const T& v)
|
std::basic_ostream<CharT, Traits>& to_stream(std::basic_ostream<CharT, Traits>& os, const T& val)
|
||||||
requires requires { detail::to_stream_impl(os, v); }
|
requires requires { detail::to_stream_impl(os, val); }
|
||||||
{
|
{
|
||||||
if (os.width()) {
|
if (os.width()) {
|
||||||
// std::setw() applies to the whole output so it has to be first put into std::string
|
// std::setw() applies to the whole output so it has to be first put into std::string
|
||||||
@@ -77,11 +77,11 @@ std::basic_ostream<CharT, Traits>& to_stream(std::basic_ostream<CharT, Traits>&
|
|||||||
oss.flags(os.flags());
|
oss.flags(os.flags());
|
||||||
oss.imbue(os.getloc());
|
oss.imbue(os.getloc());
|
||||||
oss.precision(os.precision());
|
oss.precision(os.precision());
|
||||||
detail::to_stream_impl(oss, v);
|
detail::to_stream_impl(oss, val);
|
||||||
return os << std::move(oss).str();
|
return os << std::move(oss).str();
|
||||||
}
|
}
|
||||||
|
|
||||||
detail::to_stream_impl(os, v);
|
detail::to_stream_impl(os, val);
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,10 +93,10 @@ constexpr bool is_mp_units_stream = requires(OStream os, T v) { detail::to_strea
|
|||||||
MP_UNITS_EXPORT_BEGIN
|
MP_UNITS_EXPORT_BEGIN
|
||||||
|
|
||||||
template<typename CharT, typename Traits, typename T>
|
template<typename CharT, typename Traits, typename T>
|
||||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const T& v)
|
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const T& val)
|
||||||
requires detail::is_mp_units_stream<std::basic_ostream<CharT, Traits>, T>
|
requires detail::is_mp_units_stream<std::basic_ostream<CharT, Traits>, T>
|
||||||
{
|
{
|
||||||
return detail::to_stream(os, v);
|
return detail::to_stream(os, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
MP_UNITS_EXPORT_END
|
MP_UNITS_EXPORT_END
|
||||||
|
@@ -83,10 +83,10 @@ struct quantity_like_traits<std::chrono::duration<Rep, Period>> {
|
|||||||
return q.count();
|
return q.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] static constexpr T from_numerical_value(const rep& v) noexcept(
|
[[nodiscard]] static constexpr T from_numerical_value(const rep& val) noexcept(
|
||||||
std::is_nothrow_copy_constructible_v<rep>)
|
std::is_nothrow_copy_constructible_v<rep>)
|
||||||
{
|
{
|
||||||
return T(v);
|
return T(val);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -113,10 +113,10 @@ struct quantity_point_like_traits<std::chrono::time_point<C, std::chrono::durati
|
|||||||
return tp.time_since_epoch().count();
|
return tp.time_since_epoch().count();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] static constexpr T from_numerical_value(const rep& v) noexcept(
|
[[nodiscard]] static constexpr T from_numerical_value(const rep& val) noexcept(
|
||||||
std::is_nothrow_copy_constructible_v<rep>)
|
std::is_nothrow_copy_constructible_v<rep>)
|
||||||
{
|
{
|
||||||
return T(std::chrono::duration<Rep, Period>(v));
|
return T(std::chrono::duration<Rep, Period>(val));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -24,13 +24,14 @@ find_package(Catch2 3 REQUIRED)
|
|||||||
|
|
||||||
add_executable(
|
add_executable(
|
||||||
unit_tests_runtime
|
unit_tests_runtime
|
||||||
|
atomic_test.cpp
|
||||||
|
cartesian_vector_test.cpp
|
||||||
distribution_test.cpp
|
distribution_test.cpp
|
||||||
fixed_string_test.cpp
|
fixed_string_test.cpp
|
||||||
fmt_test.cpp
|
fmt_test.cpp
|
||||||
math_test.cpp
|
math_test.cpp
|
||||||
atomic_test.cpp
|
|
||||||
truncation_test.cpp
|
|
||||||
quantity_test.cpp
|
quantity_test.cpp
|
||||||
|
truncation_test.cpp
|
||||||
)
|
)
|
||||||
if(${projectPrefix}BUILD_CXX_MODULES)
|
if(${projectPrefix}BUILD_CXX_MODULES)
|
||||||
target_compile_definitions(unit_tests_runtime PUBLIC ${projectPrefix}MODULES)
|
target_compile_definitions(unit_tests_runtime PUBLIC ${projectPrefix}MODULES)
|
||||||
|
381
test/runtime/cartesian_vector_test.cpp
Normal file
381
test/runtime/cartesian_vector_test.cpp
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
// 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 "almost_equals.h"
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||||
|
#include <mp-units/compat_macros.h>
|
||||||
|
#include <mp-units/ext/format.h>
|
||||||
|
#ifdef MP_UNITS_IMPORT_STD
|
||||||
|
import std;
|
||||||
|
#else
|
||||||
|
#include <sstream>
|
||||||
|
#endif
|
||||||
|
#ifdef MP_UNITS_MODULES
|
||||||
|
import mp_units;
|
||||||
|
#else
|
||||||
|
#include <mp-units/cartesian_vector.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace mp_units;
|
||||||
|
using namespace Catch::Matchers;
|
||||||
|
|
||||||
|
TEST_CASE("cartesian_vector operations", "[vector]")
|
||||||
|
{
|
||||||
|
SECTION("cartesian_vector initialization and access")
|
||||||
|
{
|
||||||
|
SECTION("one argument")
|
||||||
|
{
|
||||||
|
cartesian_vector v{1.0};
|
||||||
|
REQUIRE(v[0] == 1.0);
|
||||||
|
REQUIRE(v[1] == 0);
|
||||||
|
REQUIRE(v[2] == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("two arguments")
|
||||||
|
{
|
||||||
|
cartesian_vector v{1.0, 2.0};
|
||||||
|
REQUIRE(v[0] == 1.0);
|
||||||
|
REQUIRE(v[1] == 2.0);
|
||||||
|
REQUIRE(v[2] == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("all arguments")
|
||||||
|
{
|
||||||
|
cartesian_vector v{1.0, 2.0, 3.0};
|
||||||
|
REQUIRE(v[0] == 1.0);
|
||||||
|
REQUIRE(v[1] == 2.0);
|
||||||
|
REQUIRE(v[2] == 3.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("convertibility")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{1, 2, 3};
|
||||||
|
|
||||||
|
SECTION("construction")
|
||||||
|
{
|
||||||
|
cartesian_vector v2 = v1;
|
||||||
|
REQUIRE(v2[0] == 1.0);
|
||||||
|
REQUIRE(v2[1] == 2.0);
|
||||||
|
REQUIRE(v2[2] == 3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("assignment")
|
||||||
|
{
|
||||||
|
cartesian_vector v2{3.0, 2.0, 1.0};
|
||||||
|
v2 = v1;
|
||||||
|
REQUIRE(v2[0] == 1.0);
|
||||||
|
REQUIRE(v2[1] == 2.0);
|
||||||
|
REQUIRE(v2[2] == 3.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("cartesian_vector addition")
|
||||||
|
{
|
||||||
|
SECTION("double + double")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{1.0, 2.0, 3.0};
|
||||||
|
cartesian_vector v2{4.0, 5.0, 6.0};
|
||||||
|
cartesian_vector result = v1 + v2;
|
||||||
|
REQUIRE(result[0] == 5.0);
|
||||||
|
REQUIRE(result[1] == 7.0);
|
||||||
|
REQUIRE(result[2] == 9.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("double + int")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{1.0, 2.0, 3.0};
|
||||||
|
cartesian_vector v2{4, 5, 6};
|
||||||
|
cartesian_vector result = v1 + v2;
|
||||||
|
REQUIRE(result[0] == 5.0);
|
||||||
|
REQUIRE(result[1] == 7.0);
|
||||||
|
REQUIRE(result[2] == 9.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("int + double")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{1, 2, 3};
|
||||||
|
cartesian_vector v2{4.0, 5.0, 6.0};
|
||||||
|
cartesian_vector result = v1 + v2;
|
||||||
|
REQUIRE(result[0] == 5.0);
|
||||||
|
REQUIRE(result[1] == 7.0);
|
||||||
|
REQUIRE(result[2] == 9.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("int + int")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{1, 2, 3};
|
||||||
|
cartesian_vector v2{4, 5, 6};
|
||||||
|
cartesian_vector result = v1 + v2;
|
||||||
|
REQUIRE(result[0] == 5);
|
||||||
|
REQUIRE(result[1] == 7);
|
||||||
|
REQUIRE(result[2] == 9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("cartesian_vector subtraction")
|
||||||
|
{
|
||||||
|
SECTION("double - double")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{4.0, 5.0, 6.0};
|
||||||
|
cartesian_vector v2{1.0, 2.0, 3.0};
|
||||||
|
cartesian_vector result = v1 - v2;
|
||||||
|
REQUIRE(result[0] == 3.0);
|
||||||
|
REQUIRE(result[1] == 3.0);
|
||||||
|
REQUIRE(result[2] == 3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("double - int")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{4.0, 5.0, 6.0};
|
||||||
|
cartesian_vector v2{1, 2, 3};
|
||||||
|
cartesian_vector result = v1 - v2;
|
||||||
|
REQUIRE(result[0] == 3.0);
|
||||||
|
REQUIRE(result[1] == 3.0);
|
||||||
|
REQUIRE(result[2] == 3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("int - double")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{4, 5, 6};
|
||||||
|
cartesian_vector v2{1.0, 2.0, 3.0};
|
||||||
|
cartesian_vector result = v1 - v2;
|
||||||
|
REQUIRE(result[0] == 3.0);
|
||||||
|
REQUIRE(result[1] == 3.0);
|
||||||
|
REQUIRE(result[2] == 3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("int - int")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{4, 5, 6};
|
||||||
|
cartesian_vector v2{1, 2, 3};
|
||||||
|
cartesian_vector result = v1 - v2;
|
||||||
|
REQUIRE(result[0] == 3);
|
||||||
|
REQUIRE(result[1] == 3);
|
||||||
|
REQUIRE(result[2] == 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("cartesian_vector scalar multiplication")
|
||||||
|
{
|
||||||
|
SECTION("double * double")
|
||||||
|
{
|
||||||
|
cartesian_vector v{1.0, 2.0, 3.0};
|
||||||
|
cartesian_vector result = v * 2.0;
|
||||||
|
REQUIRE(result[0] == 2.0);
|
||||||
|
REQUIRE(result[1] == 4.0);
|
||||||
|
REQUIRE(result[2] == 6.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("double * int")
|
||||||
|
{
|
||||||
|
cartesian_vector v{1.0, 2.0, 3.0};
|
||||||
|
cartesian_vector result = v * 2;
|
||||||
|
REQUIRE(result[0] == 2.0);
|
||||||
|
REQUIRE(result[1] == 4.0);
|
||||||
|
REQUIRE(result[2] == 6.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("int * double")
|
||||||
|
{
|
||||||
|
cartesian_vector v{1, 2, 3};
|
||||||
|
cartesian_vector result = v * 2.0;
|
||||||
|
REQUIRE(result[0] == 2.0);
|
||||||
|
REQUIRE(result[1] == 4.0);
|
||||||
|
REQUIRE(result[2] == 6.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("int * int")
|
||||||
|
{
|
||||||
|
cartesian_vector v{1, 2, 3};
|
||||||
|
cartesian_vector result = v * 2;
|
||||||
|
REQUIRE(result[0] == 2);
|
||||||
|
REQUIRE(result[1] == 4);
|
||||||
|
REQUIRE(result[2] == 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("cartesian_vector scalar division")
|
||||||
|
{
|
||||||
|
SECTION("double / double")
|
||||||
|
{
|
||||||
|
cartesian_vector v{2.0, 4.0, 6.0};
|
||||||
|
cartesian_vector result = v / 2.0;
|
||||||
|
REQUIRE(result[0] == 1.0);
|
||||||
|
REQUIRE(result[1] == 2.0);
|
||||||
|
REQUIRE(result[2] == 3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("double / int")
|
||||||
|
{
|
||||||
|
cartesian_vector v{2.0, 4.0, 6.0};
|
||||||
|
cartesian_vector result = v / 2;
|
||||||
|
REQUIRE(result[0] == 1.0);
|
||||||
|
REQUIRE(result[1] == 2.0);
|
||||||
|
REQUIRE(result[2] == 3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("int / double")
|
||||||
|
{
|
||||||
|
cartesian_vector v{2, 4, 6};
|
||||||
|
cartesian_vector result = v / 2.0;
|
||||||
|
REQUIRE(result[0] == 1.0);
|
||||||
|
REQUIRE(result[1] == 2.0);
|
||||||
|
REQUIRE(result[2] == 3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("int / int")
|
||||||
|
{
|
||||||
|
cartesian_vector v{2, 4, 6};
|
||||||
|
cartesian_vector result = v / 2;
|
||||||
|
REQUIRE(result[0] == 1);
|
||||||
|
REQUIRE(result[1] == 2);
|
||||||
|
REQUIRE(result[2] == 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("cartesian_vector magnitude")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{3.0, 4.0, 0.0};
|
||||||
|
cartesian_vector v2{2.0, 3.0, 6.0};
|
||||||
|
REQUIRE(v1.magnitude() == 5.0);
|
||||||
|
REQUIRE(v2.magnitude() == 7.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("cartesian_vector unit vector")
|
||||||
|
{
|
||||||
|
cartesian_vector v{3.0, 4.0, 0.0};
|
||||||
|
cartesian_vector unit_v = v.unit();
|
||||||
|
REQUIRE_THAT(unit_v.magnitude(), WithinULP(1.0, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("cartesian_vector equality")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{1.0, 2.0, 3.0};
|
||||||
|
cartesian_vector v2{1, 2, 3};
|
||||||
|
cartesian_vector v3{1.1, 2.0, 3.0};
|
||||||
|
cartesian_vector v4{1.0, 2.1, 3.0};
|
||||||
|
cartesian_vector v5{1.0, 2.0, 3.1};
|
||||||
|
REQUIRE(v1 == v2);
|
||||||
|
REQUIRE(v1 != v3);
|
||||||
|
REQUIRE(v1 != v4);
|
||||||
|
REQUIRE(v1 != v5);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("cartesian_vector scalar product")
|
||||||
|
{
|
||||||
|
SECTION("double * double")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{1.0, 2.0, 3.0};
|
||||||
|
cartesian_vector v2{4.0, 5.0, 6.0};
|
||||||
|
REQUIRE(scalar_product(v1, v2) == 32.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("double * int")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{1.0, 2.0, 3.0};
|
||||||
|
cartesian_vector v2{4, 5, 6};
|
||||||
|
REQUIRE(scalar_product(v1, v2) == 32.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("int * double")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{1, 2, 3};
|
||||||
|
cartesian_vector v2{4.0, 5.0, 6.0};
|
||||||
|
REQUIRE(scalar_product(v1, v2) == 32.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("int * int")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{1, 2, 3};
|
||||||
|
cartesian_vector v2{4, 5, 6};
|
||||||
|
REQUIRE(scalar_product(v1, v2) == 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("cartesian_vector vector product")
|
||||||
|
{
|
||||||
|
SECTION("double * double")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{1.0, 2.0, 3.0};
|
||||||
|
cartesian_vector v2{4.0, 5.0, 6.0};
|
||||||
|
cartesian_vector result = vector_product(v1, v2);
|
||||||
|
REQUIRE(result[0] == -3.0);
|
||||||
|
REQUIRE(result[1] == 6.0);
|
||||||
|
REQUIRE(result[2] == -3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("double * int")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{1.0, 2.0, 3.0};
|
||||||
|
cartesian_vector v2{4, 5, 6};
|
||||||
|
cartesian_vector result = vector_product(v1, v2);
|
||||||
|
REQUIRE(result[0] == -3.0);
|
||||||
|
REQUIRE(result[1] == 6.0);
|
||||||
|
REQUIRE(result[2] == -3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("int * double")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{1, 2, 3};
|
||||||
|
cartesian_vector v2{4.0, 5.0, 6.0};
|
||||||
|
cartesian_vector result = vector_product(v1, v2);
|
||||||
|
REQUIRE(result[0] == -3.0);
|
||||||
|
REQUIRE(result[1] == 6.0);
|
||||||
|
REQUIRE(result[2] == -3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("int * int")
|
||||||
|
{
|
||||||
|
cartesian_vector v1{1, 2, 3};
|
||||||
|
cartesian_vector v2{4, 5, 6};
|
||||||
|
cartesian_vector result = vector_product(v1, v2);
|
||||||
|
REQUIRE(result[0] == -3);
|
||||||
|
REQUIRE(result[1] == 6);
|
||||||
|
REQUIRE(result[2] == -3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("cartesian_vector text output", "[vector][fmt][ostream]")
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
SECTION("integral representation")
|
||||||
|
{
|
||||||
|
cartesian_vector v{1, 2, 3};
|
||||||
|
os << v;
|
||||||
|
|
||||||
|
SECTION("iostream") { CHECK(os.str() == "[1, 2, 3]"); }
|
||||||
|
SECTION("fmt with default format {}") { CHECK(MP_UNITS_STD_FMT::format("{}", v) == os.str()); }
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("floating-point representation")
|
||||||
|
{
|
||||||
|
cartesian_vector v{1.2, 2.3, 3.4};
|
||||||
|
os << v;
|
||||||
|
|
||||||
|
SECTION("iostream") { CHECK(os.str() == "[1.2, 2.3, 3.4]"); }
|
||||||
|
SECTION("fmt with default format {}") { CHECK(MP_UNITS_STD_FMT::format("{}", v) == os.str()); }
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@@ -38,19 +38,22 @@ import mp_units;
|
|||||||
|
|
||||||
using namespace mp_units;
|
using namespace mp_units;
|
||||||
|
|
||||||
TEST_CASE("fixed_string::at", "[fixed_string]")
|
TEST_CASE("fixed_string operations", "[fixed_string]")
|
||||||
{
|
{
|
||||||
basic_fixed_string txt = "abc";
|
SECTION("fixed_string::at")
|
||||||
SECTION("in range")
|
|
||||||
{
|
{
|
||||||
CHECK(txt.at(0) == 'a');
|
basic_fixed_string txt = "abc";
|
||||||
CHECK(txt.at(1) == 'b');
|
SECTION("in range")
|
||||||
CHECK(txt.at(2) == 'c');
|
{
|
||||||
}
|
CHECK(txt.at(0) == 'a');
|
||||||
SECTION("out of range")
|
CHECK(txt.at(1) == 'b');
|
||||||
{
|
CHECK(txt.at(2) == 'c');
|
||||||
REQUIRE_THROWS_MATCHES(txt.at(3), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
|
}
|
||||||
REQUIRE_THROWS_MATCHES(txt.at(1024), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
|
SECTION("out of range")
|
||||||
|
{
|
||||||
|
REQUIRE_THROWS_MATCHES(txt.at(3), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
|
||||||
|
REQUIRE_THROWS_MATCHES(txt.at(1024), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -43,486 +43,488 @@ using namespace mp_units::si::unit_symbols;
|
|||||||
|
|
||||||
// classical
|
// classical
|
||||||
|
|
||||||
TEST_CASE("'pow<N>()' on quantity changes the value and the dimension accordingly", "[math][pow]")
|
TEST_CASE("math operations", "[math]")
|
||||||
{
|
{
|
||||||
SECTION("'pow<0>(q)' returns '1'") { CHECK(pow<0>(2 * isq::length[m]) == 1 * one); }
|
SECTION("'pow<N>()' on quantity changes the value and the dimension accordingly")
|
||||||
|
|
||||||
SECTION("'pow<1>(q)' returns 'q'") { CHECK(pow<1>(2 * isq::length[m]) == 2 * isq::length[m]); }
|
|
||||||
|
|
||||||
SECTION("'pow<2>(q)' squares both the value and a dimension")
|
|
||||||
{
|
{
|
||||||
CHECK(pow<2>(2 * isq::length[m]) == 4 * isq::area[m2]);
|
SECTION("'pow<0>(q)' returns '1'") { CHECK(pow<0>(2 * isq::length[m]) == 1 * one); }
|
||||||
|
|
||||||
|
SECTION("'pow<1>(q)' returns 'q'") { CHECK(pow<1>(2 * isq::length[m]) == 2 * isq::length[m]); }
|
||||||
|
|
||||||
|
SECTION("'pow<2>(q)' squares both the value and a dimension")
|
||||||
|
{
|
||||||
|
CHECK(pow<2>(2 * isq::length[m]) == 4 * isq::area[m2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("'pow<3>(q)' cubes both the value and a dimension")
|
||||||
|
{
|
||||||
|
CHECK(pow<3>(2 * isq::length[m]) == 8 * isq::volume[m3]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("'pow<3>(q)' cubes both the value and a dimension")
|
SECTION("'sqrt()' on quantity changes the value and the dimension accordingly")
|
||||||
{
|
{
|
||||||
CHECK(pow<3>(2 * isq::length[m]) == 8 * isq::volume[m3]);
|
REQUIRE(sqrt(4 * isq::area[m2]) == 2 * isq::length[m]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
SECTION("'cbrt()' on quantity changes the value and the dimension accordingly")
|
||||||
TEST_CASE("'sqrt()' on quantity changes the value and the dimension accordingly", "[math][sqrt]")
|
{
|
||||||
{
|
REQUIRE(cbrt(8 * isq::volume[m3]) == 2 * isq::length[m]);
|
||||||
REQUIRE(sqrt(4 * isq::area[m2]) == 2 * isq::length[m]);
|
}
|
||||||
}
|
|
||||||
|
SECTION("'fma()' on quantity changes the value and the dimension accordingly")
|
||||||
TEST_CASE("'cbrt()' on quantity changes the value and the dimension accordingly", "[math][cbrt]")
|
{
|
||||||
{
|
REQUIRE(fma(1.0 * isq::length[m], 2.0 * one, 2.0 * isq::length[m]) == 4.0 * isq::length[m]);
|
||||||
REQUIRE(cbrt(8 * isq::volume[m3]) == 2 * isq::length[m]);
|
REQUIRE(fma(isq::speed(10.0 * m / s), isq::time(2.0 * s), isq::height(42.0 * m)) == isq::length(62.0 * m));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("'fma()' on quantity changes the value and the dimension accordingly", "[math][fma]")
|
SECTION("fmod functions")
|
||||||
{
|
{
|
||||||
REQUIRE(fma(1.0 * isq::length[m], 2.0 * one, 2.0 * isq::length[m]) == 4.0 * isq::length[m]);
|
SECTION("fmod should work on the same quantities")
|
||||||
REQUIRE(fma(isq::speed(10.0 * m / s), isq::time(2.0 * s), isq::height(42.0 * m)) == isq::length(62.0 * m));
|
{
|
||||||
}
|
REQUIRE(fmod(4. * isq::length[km], 3. * isq::length[km]) == 1. * isq::length[km]);
|
||||||
|
REQUIRE(fmod(-9. * isq::length[km], 3. * isq::length[km]) == -0. * isq::length[km]);
|
||||||
TEST_CASE("fmod functions", "[math][fmod]")
|
REQUIRE(fmod(3 * isq::length[km], 2 * isq::length[km]) == 1 * isq::length[km]);
|
||||||
{
|
REQUIRE(fmod(4 * isq::length[km], 2.5f * isq::length[km]) == 1.5 * isq::length[km]);
|
||||||
SECTION("fmod should work on the same quantities")
|
}
|
||||||
{
|
SECTION("fmod should work with different units of the same dimension")
|
||||||
REQUIRE(fmod(4. * isq::length[km], 3. * isq::length[km]) == 1. * isq::length[km]);
|
{
|
||||||
REQUIRE(fmod(-9. * isq::length[km], 3. * isq::length[km]) == -0. * isq::length[km]);
|
REQUIRE(fmod(4. * isq::length[km], 3000. * isq::length[m]) == 1000. * isq::length[m]);
|
||||||
REQUIRE(fmod(3 * isq::length[km], 2 * isq::length[km]) == 1 * isq::length[km]);
|
REQUIRE(fmod(-9. * isq::length[km], 3000. * isq::length[m]) == -0. * isq::length[m]);
|
||||||
REQUIRE(fmod(4 * isq::length[km], 2.5f * isq::length[km]) == 1.5 * isq::length[km]);
|
REQUIRE(fmod(3. * isq::length[km], 2000. * isq::length[m]) == 1000 * isq::length[m]);
|
||||||
}
|
REQUIRE(fmod(4 * isq::length[km], 2500 * isq::length[m]) == 1500 * isq::length[m]);
|
||||||
SECTION("fmod should work with different units of the same dimension")
|
}
|
||||||
{
|
}
|
||||||
REQUIRE(fmod(4. * isq::length[km], 3000. * isq::length[m]) == 1000. * isq::length[m]);
|
|
||||||
REQUIRE(fmod(-9. * isq::length[km], 3000. * isq::length[m]) == -0. * isq::length[m]);
|
SECTION("remainder functions")
|
||||||
REQUIRE(fmod(3. * isq::length[km], 2000. * isq::length[m]) == 1000 * isq::length[m]);
|
{
|
||||||
REQUIRE(fmod(4 * isq::length[km], 2500 * isq::length[m]) == 1500 * isq::length[m]);
|
SECTION("remainder should work on the same quantities")
|
||||||
}
|
{
|
||||||
}
|
REQUIRE(remainder(4. * isq::length[km], 3. * isq::length[km]) == 1. * isq::length[km]);
|
||||||
|
REQUIRE(remainder(-9. * isq::length[km], 3. * isq::length[km]) == -0. * isq::length[km]);
|
||||||
TEST_CASE("remainder functions", "[math][remainder]")
|
REQUIRE(remainder(3 * isq::length[km], 2 * isq::length[km]) == -1 * isq::length[km]);
|
||||||
{
|
REQUIRE(remainder(4 * isq::length[km], 2.75f * isq::length[km]) == 1.25 * isq::length[km]);
|
||||||
SECTION("remainder should work on the same quantities")
|
}
|
||||||
{
|
SECTION("remainder should work with different units of the same dimension")
|
||||||
REQUIRE(remainder(4. * isq::length[km], 3. * isq::length[km]) == 1. * isq::length[km]);
|
{
|
||||||
REQUIRE(remainder(-9. * isq::length[km], 3. * isq::length[km]) == -0. * isq::length[km]);
|
REQUIRE(remainder(4. * isq::length[km], 3000. * isq::length[m]) == 1000. * isq::length[m]);
|
||||||
REQUIRE(remainder(3 * isq::length[km], 2 * isq::length[km]) == -1 * isq::length[km]);
|
REQUIRE(remainder(-9. * isq::length[km], 3000. * isq::length[m]) == -0. * isq::length[m]);
|
||||||
REQUIRE(remainder(4 * isq::length[km], 2.75f * isq::length[km]) == 1.25 * isq::length[km]);
|
REQUIRE(remainder(3. * isq::length[km], 2000. * isq::length[m]) == -1000 * isq::length[m]);
|
||||||
}
|
REQUIRE(remainder(4 * isq::length[km], 2750 * isq::length[m]) == 1250 * isq::length[m]);
|
||||||
SECTION("remainder should work with different units of the same dimension")
|
}
|
||||||
{
|
}
|
||||||
REQUIRE(remainder(4. * isq::length[km], 3000. * isq::length[m]) == 1000. * isq::length[m]);
|
|
||||||
REQUIRE(remainder(-9. * isq::length[km], 3000. * isq::length[m]) == -0. * isq::length[m]);
|
SECTION("'isfinite()' accepts dimensioned arguments") { REQUIRE(isfinite(4.0 * isq::length[m])); }
|
||||||
REQUIRE(remainder(3. * isq::length[km], 2000. * isq::length[m]) == -1000 * isq::length[m]);
|
|
||||||
REQUIRE(remainder(4 * isq::length[km], 2750 * isq::length[m]) == 1250 * isq::length[m]);
|
SECTION("'isinf()' accepts dimensioned arguments") { REQUIRE(!isinf(4.0 * isq::length[m])); }
|
||||||
}
|
|
||||||
}
|
SECTION("'isnan()' accepts dimensioned arguments") { REQUIRE(!isnan(4.0 * isq::length[m])); }
|
||||||
|
|
||||||
TEST_CASE("'isfinite()' accepts dimensioned arguments", "[math][isfinite]") { REQUIRE(isfinite(4.0 * isq::length[m])); }
|
|
||||||
|
SECTION("'pow<Num, Den>()' on quantity changes the value and the dimension accordingly")
|
||||||
TEST_CASE("'isinf()' accepts dimensioned arguments", "[math][isinf]") { REQUIRE(!isinf(4.0 * isq::length[m])); }
|
{
|
||||||
|
REQUIRE(pow<1, 4>(16 * isq::area[m2]) == sqrt(4 * isq::length[m]));
|
||||||
TEST_CASE("'isnan()' accepts dimensioned arguments", "[math][isnan]") { REQUIRE(!isnan(4.0 * isq::length[m])); }
|
}
|
||||||
|
|
||||||
|
// TODO add tests for exp()
|
||||||
TEST_CASE("'pow<Num, Den>()' on quantity changes the value and the dimension accordingly", "[math][pow]")
|
|
||||||
{
|
SECTION("absolute functions on quantity returns the absolute value")
|
||||||
REQUIRE(pow<1, 4>(16 * isq::area[m2]) == sqrt(4 * isq::length[m]));
|
{
|
||||||
}
|
SECTION("'abs()' on a negative quantity returns the abs")
|
||||||
|
{
|
||||||
// TODO add tests for exp()
|
SECTION("integral representation") { REQUIRE(abs(-1 * isq::length[m]) == 1 * isq::length[m]); }
|
||||||
|
|
||||||
TEST_CASE("absolute functions on quantity returns the absolute value", "[math][abs][fabs]")
|
SECTION("floating-point representation") { REQUIRE(abs(-1. * isq::length[m]) == 1 * isq::length[m]); }
|
||||||
{
|
}
|
||||||
SECTION("'abs()' on a negative quantity returns the abs")
|
|
||||||
{
|
SECTION("'abs()' on a positive quantity returns the abs")
|
||||||
SECTION("integral representation") { REQUIRE(abs(-1 * isq::length[m]) == 1 * isq::length[m]); }
|
{
|
||||||
|
SECTION("integral representation") { REQUIRE(abs(1 * isq::length[m]) == 1 * isq::length[m]); }
|
||||||
SECTION("floating-point representation") { REQUIRE(abs(-1. * isq::length[m]) == 1 * isq::length[m]); }
|
|
||||||
}
|
SECTION("floating-point representation") { REQUIRE(abs(1. * isq::length[m]) == 1 * isq::length[m]); }
|
||||||
|
}
|
||||||
SECTION("'abs()' on a positive quantity returns the abs")
|
}
|
||||||
{
|
|
||||||
SECTION("integral representation") { REQUIRE(abs(1 * isq::length[m]) == 1 * isq::length[m]); }
|
SECTION("numeric_limits functions")
|
||||||
|
{
|
||||||
SECTION("floating-point representation") { REQUIRE(abs(1. * isq::length[m]) == 1 * isq::length[m]); }
|
SECTION("'epsilon' works as expected using default floating type")
|
||||||
}
|
{
|
||||||
}
|
REQUIRE(epsilon<double>(isq::length[m]).numerical_value_in(m) ==
|
||||||
|
std::numeric_limits<decltype(1. * isq::length[m])::rep>::epsilon());
|
||||||
TEST_CASE("numeric_limits functions", "[limits]")
|
}
|
||||||
{
|
SECTION("'epsilon' works as expected using integers")
|
||||||
SECTION("'epsilon' works as expected using default floating type")
|
{
|
||||||
{
|
REQUIRE(epsilon<int>(isq::length[m]).numerical_value_in(m) ==
|
||||||
REQUIRE(epsilon<double>(isq::length[m]).numerical_value_in(m) ==
|
std::numeric_limits<decltype(1 * isq::length[m])::rep>::epsilon());
|
||||||
std::numeric_limits<decltype(1. * isq::length[m])::rep>::epsilon());
|
}
|
||||||
}
|
}
|
||||||
SECTION("'epsilon' works as expected using integers")
|
|
||||||
{
|
SECTION("floor functions")
|
||||||
REQUIRE(epsilon<int>(isq::length[m]).numerical_value_in(m) ==
|
{
|
||||||
std::numeric_limits<decltype(1 * isq::length[m])::rep>::epsilon());
|
SECTION("floor 1 second with target unit second should be 1 second")
|
||||||
}
|
{
|
||||||
}
|
REQUIRE(floor<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
|
||||||
|
}
|
||||||
TEST_CASE("floor functions", "[floor]")
|
SECTION("floor 1000 milliseconds with target unit second should be 1 second")
|
||||||
{
|
{
|
||||||
SECTION("floor 1 second with target unit second should be 1 second")
|
REQUIRE(floor<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(floor<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
|
SECTION("floor 1001 milliseconds with target unit second should be 1 second")
|
||||||
}
|
{
|
||||||
SECTION("floor 1000 milliseconds with target unit second should be 1 second")
|
REQUIRE(floor<si::second>(1001 * isq::time[ms]) == 1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(floor<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
|
SECTION("floor 1999 milliseconds with target unit second should be 1 second")
|
||||||
}
|
{
|
||||||
SECTION("floor 1001 milliseconds with target unit second should be 1 second")
|
REQUIRE(floor<si::second>(1999 * isq::time[ms]) == 1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(floor<si::second>(1001 * isq::time[ms]) == 1 * isq::time[s]);
|
SECTION("floor -1000 milliseconds with target unit second should be -1 second")
|
||||||
}
|
{
|
||||||
SECTION("floor 1999 milliseconds with target unit second should be 1 second")
|
REQUIRE(floor<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(floor<si::second>(1999 * isq::time[ms]) == 1 * isq::time[s]);
|
SECTION("floor -999 milliseconds with target unit second should be -1 second")
|
||||||
}
|
{
|
||||||
SECTION("floor -1000 milliseconds with target unit second should be -1 second")
|
REQUIRE(floor<si::second>(-999 * isq::time[ms]) == -1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(floor<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
|
SECTION("floor 1.3 seconds with target unit second should be 1 second")
|
||||||
}
|
{
|
||||||
SECTION("floor -999 milliseconds with target unit second should be -1 second")
|
REQUIRE(floor<si::second>(1.3 * isq::time[s]) == 1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(floor<si::second>(-999 * isq::time[ms]) == -1 * isq::time[s]);
|
SECTION("floor -1.3 seconds with target unit second should be -2 seconds")
|
||||||
}
|
{
|
||||||
SECTION("floor 1.3 seconds with target unit second should be 1 second")
|
REQUIRE(floor<si::second>(-1.3 * isq::time[s]) == -2 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(floor<si::second>(1.3 * isq::time[s]) == 1 * isq::time[s]);
|
SECTION("floor 1001. milliseconds with target unit second should be 1 second")
|
||||||
}
|
{
|
||||||
SECTION("floor -1.3 seconds with target unit second should be -2 seconds")
|
REQUIRE(floor<si::second>(1001. * isq::time[ms]) == 1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(floor<si::second>(-1.3 * isq::time[s]) == -2 * isq::time[s]);
|
SECTION("floor 1999. milliseconds with target unit second should be 1 second")
|
||||||
}
|
{
|
||||||
SECTION("floor 1001. milliseconds with target unit second should be 1 second")
|
REQUIRE(floor<si::second>(1999. * isq::time[ms]) == 1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(floor<si::second>(1001. * isq::time[ms]) == 1 * isq::time[s]);
|
SECTION("floor -1000. milliseconds with target unit second should be -1 second")
|
||||||
}
|
{
|
||||||
SECTION("floor 1999. milliseconds with target unit second should be 1 second")
|
REQUIRE(floor<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(floor<si::second>(1999. * isq::time[ms]) == 1 * isq::time[s]);
|
SECTION("floor -999. milliseconds with target unit second should be -1 second")
|
||||||
}
|
{
|
||||||
SECTION("floor -1000. milliseconds with target unit second should be -1 second")
|
REQUIRE(floor<si::second>(-999. * isq::time[ms]) == -1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(floor<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
|
|
||||||
}
|
// TODO Add tests for `N`, `kN` and `kg * m / s2` i `kg * km / s2`
|
||||||
SECTION("floor -999. milliseconds with target unit second should be -1 second")
|
}
|
||||||
{
|
|
||||||
REQUIRE(floor<si::second>(-999. * isq::time[ms]) == -1 * isq::time[s]);
|
SECTION("ceil functions")
|
||||||
}
|
{
|
||||||
|
SECTION("ceil 1 second with target unit second should be 1 second")
|
||||||
// TODO Add tests for `N`, `kN` and `kg * m / s2` i `kg * km / s2`
|
{
|
||||||
}
|
REQUIRE(ceil<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
|
||||||
|
}
|
||||||
TEST_CASE("ceil functions", "[ceil]")
|
SECTION("ceil 1000 milliseconds with target unit second should be 1 second")
|
||||||
{
|
{
|
||||||
SECTION("ceil 1 second with target unit second should be 1 second")
|
REQUIRE(ceil<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(ceil<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
|
SECTION("ceil 1001 milliseconds with target unit second should be 2 seconds")
|
||||||
}
|
{
|
||||||
SECTION("ceil 1000 milliseconds with target unit second should be 1 second")
|
REQUIRE(ceil<si::second>(1001 * isq::time[ms]) == 2 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(ceil<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
|
SECTION("ceil 1999 milliseconds with target unit second should be 2 seconds")
|
||||||
}
|
{
|
||||||
SECTION("ceil 1001 milliseconds with target unit second should be 2 seconds")
|
REQUIRE(ceil<si::second>(1999 * isq::time[ms]) == 2 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(ceil<si::second>(1001 * isq::time[ms]) == 2 * isq::time[s]);
|
SECTION("ceil -1000 milliseconds with target unit second should be -1 second")
|
||||||
}
|
{
|
||||||
SECTION("ceil 1999 milliseconds with target unit second should be 2 seconds")
|
REQUIRE(ceil<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(ceil<si::second>(1999 * isq::time[ms]) == 2 * isq::time[s]);
|
SECTION("ceil -999 milliseconds with target unit second should be 0 seconds")
|
||||||
}
|
{
|
||||||
SECTION("ceil -1000 milliseconds with target unit second should be -1 second")
|
REQUIRE(ceil<si::second>(-999 * isq::time[ms]) == 0 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(ceil<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
|
SECTION("ceil 1.3 seconds with target unit second should be 2 seconds")
|
||||||
}
|
{
|
||||||
SECTION("ceil -999 milliseconds with target unit second should be 0 seconds")
|
REQUIRE(ceil<si::second>(1.3 * isq::time[s]) == 2 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(ceil<si::second>(-999 * isq::time[ms]) == 0 * isq::time[s]);
|
SECTION("ceil -1.3 seconds with target unit second should be -1 second")
|
||||||
}
|
{
|
||||||
SECTION("ceil 1.3 seconds with target unit second should be 2 seconds")
|
REQUIRE(ceil<si::second>(-1.3 * isq::time[s]) == -1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(ceil<si::second>(1.3 * isq::time[s]) == 2 * isq::time[s]);
|
SECTION("ceil 1001. milliseconds with target unit second should be 2 seconds")
|
||||||
}
|
{
|
||||||
SECTION("ceil -1.3 seconds with target unit second should be -1 second")
|
REQUIRE(ceil<si::second>(1001. * isq::time[ms]) == 2 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(ceil<si::second>(-1.3 * isq::time[s]) == -1 * isq::time[s]);
|
SECTION("ceil 1999. milliseconds with target unit second should be 2 seconds")
|
||||||
}
|
{
|
||||||
SECTION("ceil 1001. milliseconds with target unit second should be 2 seconds")
|
REQUIRE(ceil<si::second>(1999. * isq::time[ms]) == 2 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(ceil<si::second>(1001. * isq::time[ms]) == 2 * isq::time[s]);
|
SECTION("ceil -1000. milliseconds with target unit second should be -1 second")
|
||||||
}
|
{
|
||||||
SECTION("ceil 1999. milliseconds with target unit second should be 2 seconds")
|
REQUIRE(ceil<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(ceil<si::second>(1999. * isq::time[ms]) == 2 * isq::time[s]);
|
SECTION("ceil -999. milliseconds with target unit second should be 0 seconds")
|
||||||
}
|
{
|
||||||
SECTION("ceil -1000. milliseconds with target unit second should be -1 second")
|
REQUIRE(ceil<si::second>(-999. * isq::time[ms]) == 0 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(ceil<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
|
}
|
||||||
}
|
|
||||||
SECTION("ceil -999. milliseconds with target unit second should be 0 seconds")
|
SECTION("round functions")
|
||||||
{
|
{
|
||||||
REQUIRE(ceil<si::second>(-999. * isq::time[ms]) == 0 * isq::time[s]);
|
SECTION("round 1 second with target unit second should be 1 second")
|
||||||
}
|
{
|
||||||
}
|
REQUIRE(round<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
|
||||||
|
}
|
||||||
TEST_CASE("round functions", "[round]")
|
SECTION("round 1000 milliseconds with target unit second should be 1 second")
|
||||||
{
|
{
|
||||||
SECTION("round 1 second with target unit second should be 1 second")
|
REQUIRE(round<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
|
SECTION("round 1001 milliseconds with target unit second should be 1 second")
|
||||||
}
|
{
|
||||||
SECTION("round 1000 milliseconds with target unit second should be 1 second")
|
REQUIRE(round<si::second>(1001 * isq::time[ms]) == 1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
|
SECTION("round 1499 milliseconds with target unit second should be 1 second")
|
||||||
}
|
{
|
||||||
SECTION("round 1001 milliseconds with target unit second should be 1 second")
|
REQUIRE(round<si::second>(1499 * isq::time[ms]) == 1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(1001 * isq::time[ms]) == 1 * isq::time[s]);
|
SECTION("round 1500 milliseconds with target unit second should be 2 seconds")
|
||||||
}
|
{
|
||||||
SECTION("round 1499 milliseconds with target unit second should be 1 second")
|
REQUIRE(round<si::second>(1500 * isq::time[ms]) == 2 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(1499 * isq::time[ms]) == 1 * isq::time[s]);
|
SECTION("round 1999 milliseconds with target unit second should be 2 seconds")
|
||||||
}
|
{
|
||||||
SECTION("round 1500 milliseconds with target unit second should be 2 seconds")
|
REQUIRE(round<si::second>(1999 * isq::time[ms]) == 2 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(1500 * isq::time[ms]) == 2 * isq::time[s]);
|
SECTION("round -1000 milliseconds with target unit second should be -1 second")
|
||||||
}
|
{
|
||||||
SECTION("round 1999 milliseconds with target unit second should be 2 seconds")
|
REQUIRE(round<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(1999 * isq::time[ms]) == 2 * isq::time[s]);
|
SECTION("round -1001 milliseconds with target unit second should be -1 second")
|
||||||
}
|
{
|
||||||
SECTION("round -1000 milliseconds with target unit second should be -1 second")
|
REQUIRE(round<si::second>(-1001 * isq::time[ms]) == -1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
|
SECTION("round -1499 milliseconds with target unit second should be -1 second")
|
||||||
}
|
{
|
||||||
SECTION("round -1001 milliseconds with target unit second should be -1 second")
|
REQUIRE(round<si::second>(-1499 * isq::time[ms]) == -1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(-1001 * isq::time[ms]) == -1 * isq::time[s]);
|
SECTION("round -1500 milliseconds with target unit second should be -2 seconds")
|
||||||
}
|
{
|
||||||
SECTION("round -1499 milliseconds with target unit second should be -1 second")
|
REQUIRE(round<si::second>(-1500 * isq::time[ms]) == -2 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(-1499 * isq::time[ms]) == -1 * isq::time[s]);
|
SECTION("round -1999 milliseconds with target unit second should be -2 seconds")
|
||||||
}
|
{
|
||||||
SECTION("round -1500 milliseconds with target unit second should be -2 seconds")
|
REQUIRE(round<si::second>(-1999 * isq::time[ms]) == -2 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(-1500 * isq::time[ms]) == -2 * isq::time[s]);
|
SECTION("round 1000. milliseconds with target unit second should be 1 second")
|
||||||
}
|
{
|
||||||
SECTION("round -1999 milliseconds with target unit second should be -2 seconds")
|
REQUIRE(round<si::second>(1000. * isq::time[ms]) == 1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(-1999 * isq::time[ms]) == -2 * isq::time[s]);
|
SECTION("round 1001. milliseconds with target unit second should be 1 second")
|
||||||
}
|
{
|
||||||
SECTION("round 1000. milliseconds with target unit second should be 1 second")
|
REQUIRE(round<si::second>(1001. * isq::time[ms]) == 1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(1000. * isq::time[ms]) == 1 * isq::time[s]);
|
SECTION("round 1499. milliseconds with target unit second should be 1 second")
|
||||||
}
|
{
|
||||||
SECTION("round 1001. milliseconds with target unit second should be 1 second")
|
REQUIRE(round<si::second>(1499. * isq::time[ms]) == 1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(1001. * isq::time[ms]) == 1 * isq::time[s]);
|
SECTION("round 1500. milliseconds with target unit second should be 2 seconds")
|
||||||
}
|
{
|
||||||
SECTION("round 1499. milliseconds with target unit second should be 1 second")
|
REQUIRE(round<si::second>(1500. * isq::time[ms]) == 2 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(1499. * isq::time[ms]) == 1 * isq::time[s]);
|
SECTION("round 1999. milliseconds with target unit second should be 2 seconds")
|
||||||
}
|
{
|
||||||
SECTION("round 1500. milliseconds with target unit second should be 2 seconds")
|
REQUIRE(round<si::second>(1999. * isq::time[ms]) == 2 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(1500. * isq::time[ms]) == 2 * isq::time[s]);
|
SECTION("round -1000. milliseconds with target unit second should be -1 second")
|
||||||
}
|
{
|
||||||
SECTION("round 1999. milliseconds with target unit second should be 2 seconds")
|
REQUIRE(round<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(1999. * isq::time[ms]) == 2 * isq::time[s]);
|
SECTION("round -1001. milliseconds with target unit second should be -1 second")
|
||||||
}
|
{
|
||||||
SECTION("round -1000. milliseconds with target unit second should be -1 second")
|
REQUIRE(round<si::second>(-1001. * isq::time[ms]) == -1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
|
SECTION("round -1499. milliseconds with target unit second should be -1 second")
|
||||||
}
|
{
|
||||||
SECTION("round -1001. milliseconds with target unit second should be -1 second")
|
REQUIRE(round<si::second>(-1499. * isq::time[ms]) == -1 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(-1001. * isq::time[ms]) == -1 * isq::time[s]);
|
SECTION("round -1500. milliseconds with target unit second should be -2 seconds")
|
||||||
}
|
{
|
||||||
SECTION("round -1499. milliseconds with target unit second should be -1 second")
|
REQUIRE(round<si::second>(-1500. * isq::time[ms]) == -2 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(-1499. * isq::time[ms]) == -1 * isq::time[s]);
|
SECTION("round -1999. milliseconds with target unit second should be -2 seconds")
|
||||||
}
|
{
|
||||||
SECTION("round -1500. milliseconds with target unit second should be -2 seconds")
|
REQUIRE(round<si::second>(-1999. * isq::time[ms]) == -2 * isq::time[s]);
|
||||||
{
|
}
|
||||||
REQUIRE(round<si::second>(-1500. * isq::time[ms]) == -2 * isq::time[s]);
|
}
|
||||||
}
|
|
||||||
SECTION("round -1999. milliseconds with target unit second should be -2 seconds")
|
SECTION("hypot functions")
|
||||||
{
|
{
|
||||||
REQUIRE(round<si::second>(-1999. * isq::time[ms]) == -2 * isq::time[s]);
|
SECTION("hypot should work on the same quantities")
|
||||||
}
|
{
|
||||||
}
|
REQUIRE(hypot(3. * isq::length[km], 4. * isq::length[km]) == 5. * isq::length[km]);
|
||||||
|
REQUIRE(hypot(2. * isq::length[km], 3. * isq::length[km], 6. * isq::length[km]) == 7. * isq::length[km]);
|
||||||
TEST_CASE("hypot functions", "[hypot]")
|
}
|
||||||
{
|
SECTION("hypot should work with different units of the same dimension")
|
||||||
SECTION("hypot should work on the same quantities")
|
{
|
||||||
{
|
REQUIRE(hypot(3. * isq::length[km], 4000. * isq::length[m]) == 5. * isq::length[km]);
|
||||||
REQUIRE(hypot(3. * isq::length[km], 4. * isq::length[km]) == 5. * isq::length[km]);
|
REQUIRE(hypot(2. * isq::length[km], 3000. * isq::length[m], 6. * isq::length[km]) == 7. * isq::length[km]);
|
||||||
REQUIRE(hypot(2. * isq::length[km], 3. * isq::length[km], 6. * isq::length[km]) == 7. * isq::length[km]);
|
}
|
||||||
}
|
}
|
||||||
SECTION("hypot should work with different units of the same dimension")
|
|
||||||
{
|
SECTION("SI trigonometric functions")
|
||||||
REQUIRE(hypot(3. * isq::length[km], 4000. * isq::length[m]) == 5. * isq::length[km]);
|
{
|
||||||
REQUIRE(hypot(2. * isq::length[km], 3000. * isq::length[m], 6. * isq::length[km]) == 7. * isq::length[km]);
|
SECTION("sin")
|
||||||
}
|
{
|
||||||
}
|
REQUIRE_THAT(si::sin(0 * deg), AlmostEquals(0. * one));
|
||||||
|
REQUIRE_THAT(si::sin(90 * deg), AlmostEquals(1. * one));
|
||||||
TEST_CASE("SI trigonometric functions", "[trig][si]")
|
REQUIRE_THAT(si::sin(180 * deg), AlmostEquals(0. * one));
|
||||||
{
|
REQUIRE_THAT(si::sin(270 * deg), AlmostEquals(-1. * one));
|
||||||
SECTION("sin")
|
}
|
||||||
{
|
|
||||||
REQUIRE_THAT(si::sin(0 * deg), AlmostEquals(0. * one));
|
SECTION("cos")
|
||||||
REQUIRE_THAT(si::sin(90 * deg), AlmostEquals(1. * one));
|
{
|
||||||
REQUIRE_THAT(si::sin(180 * deg), AlmostEquals(0. * one));
|
REQUIRE_THAT(si::cos(0 * deg), AlmostEquals(1. * one));
|
||||||
REQUIRE_THAT(si::sin(270 * deg), AlmostEquals(-1. * one));
|
REQUIRE_THAT(si::cos(90 * deg), AlmostEquals(0. * one));
|
||||||
}
|
REQUIRE_THAT(si::cos(180 * deg), AlmostEquals(-1. * one));
|
||||||
|
REQUIRE_THAT(si::cos(270 * deg), AlmostEquals(0. * one));
|
||||||
SECTION("cos")
|
}
|
||||||
{
|
|
||||||
REQUIRE_THAT(si::cos(0 * deg), AlmostEquals(1. * one));
|
SECTION("tan")
|
||||||
REQUIRE_THAT(si::cos(90 * deg), AlmostEquals(0. * one));
|
{
|
||||||
REQUIRE_THAT(si::cos(180 * deg), AlmostEquals(-1. * one));
|
REQUIRE_THAT(si::tan(0 * deg), AlmostEquals(0. * one));
|
||||||
REQUIRE_THAT(si::cos(270 * deg), AlmostEquals(0. * one));
|
REQUIRE_THAT(si::tan(45. * deg), AlmostEquals(1. * one));
|
||||||
}
|
REQUIRE_THAT(si::tan(135. * deg), AlmostEquals(-1. * one));
|
||||||
|
REQUIRE_THAT(si::tan(180. * deg), AlmostEquals(0. * one));
|
||||||
SECTION("tan")
|
}
|
||||||
{
|
}
|
||||||
REQUIRE_THAT(si::tan(0 * deg), AlmostEquals(0. * one));
|
|
||||||
REQUIRE_THAT(si::tan(45. * deg), AlmostEquals(1. * one));
|
SECTION("SI inverse trigonometric functions")
|
||||||
REQUIRE_THAT(si::tan(135. * deg), AlmostEquals(-1. * one));
|
{
|
||||||
REQUIRE_THAT(si::tan(180. * deg), AlmostEquals(0. * one));
|
SECTION("asin")
|
||||||
}
|
{
|
||||||
}
|
REQUIRE_THAT(si::asin(-1 * one), AlmostEquals(-90. * deg));
|
||||||
|
REQUIRE_THAT(si::asin(0 * one), AlmostEquals(0. * deg));
|
||||||
TEST_CASE("SI inverse trigonometric functions", "[inv trig][si]")
|
REQUIRE_THAT(si::asin(1 * one), AlmostEquals(90. * deg));
|
||||||
{
|
}
|
||||||
SECTION("asin")
|
|
||||||
{
|
SECTION("acos")
|
||||||
REQUIRE_THAT(si::asin(-1 * one), AlmostEquals(-90. * deg));
|
{
|
||||||
REQUIRE_THAT(si::asin(0 * one), AlmostEquals(0. * deg));
|
REQUIRE_THAT(si::asin(-1 * one), AlmostEquals(-90. * deg));
|
||||||
REQUIRE_THAT(si::asin(1 * one), AlmostEquals(90. * deg));
|
REQUIRE_THAT(si::asin(0 * one), AlmostEquals(0. * deg));
|
||||||
}
|
REQUIRE_THAT(si::asin(1 * one), AlmostEquals(90. * deg));
|
||||||
|
}
|
||||||
SECTION("acos")
|
|
||||||
{
|
SECTION("atan")
|
||||||
REQUIRE_THAT(si::asin(-1 * one), AlmostEquals(-90. * deg));
|
{
|
||||||
REQUIRE_THAT(si::asin(0 * one), AlmostEquals(0. * deg));
|
REQUIRE_THAT(si::atan(-1 * one), AlmostEquals(-45. * deg));
|
||||||
REQUIRE_THAT(si::asin(1 * one), AlmostEquals(90. * deg));
|
REQUIRE_THAT(si::atan(0 * one), AlmostEquals(0. * deg));
|
||||||
}
|
REQUIRE_THAT(si::atan(1 * one), AlmostEquals(45. * deg));
|
||||||
|
}
|
||||||
SECTION("atan")
|
}
|
||||||
{
|
|
||||||
REQUIRE_THAT(si::atan(-1 * one), AlmostEquals(-45. * deg));
|
SECTION("SI atan2 functions")
|
||||||
REQUIRE_THAT(si::atan(0 * one), AlmostEquals(0. * deg));
|
{
|
||||||
REQUIRE_THAT(si::atan(1 * one), AlmostEquals(45. * deg));
|
SECTION("atan2 should work on the same quantities")
|
||||||
}
|
{
|
||||||
}
|
REQUIRE_THAT(si::atan2(-1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(-45. * deg));
|
||||||
|
REQUIRE_THAT(si::atan2(0. * isq::length[km], 1. * isq::length[km]), AlmostEquals(0. * deg));
|
||||||
TEST_CASE("SI atan2 functions", "[atan2][si]")
|
REQUIRE_THAT(si::atan2(1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(45. * deg));
|
||||||
{
|
}
|
||||||
SECTION("atan2 should work on the same quantities")
|
SECTION("atan2 should work with different units of the same dimension")
|
||||||
{
|
{
|
||||||
REQUIRE_THAT(si::atan2(-1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(-45. * deg));
|
REQUIRE_THAT(si::atan2(-1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(-45. * deg));
|
||||||
REQUIRE_THAT(si::atan2(0. * isq::length[km], 1. * isq::length[km]), AlmostEquals(0. * deg));
|
REQUIRE_THAT(si::atan2(0. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(0. * deg));
|
||||||
REQUIRE_THAT(si::atan2(1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(45. * deg));
|
REQUIRE_THAT(si::atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * deg));
|
||||||
}
|
}
|
||||||
SECTION("atan2 should work with different units of the same dimension")
|
}
|
||||||
{
|
|
||||||
REQUIRE_THAT(si::atan2(-1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(-45. * deg));
|
SECTION("Angle trigonometric functions")
|
||||||
REQUIRE_THAT(si::atan2(0. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(0. * deg));
|
{
|
||||||
REQUIRE_THAT(si::atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * deg));
|
using namespace mp_units::angular;
|
||||||
}
|
using namespace mp_units::angular::unit_symbols;
|
||||||
}
|
using mp_units::angular::unit_symbols::deg;
|
||||||
|
|
||||||
|
SECTION("sin")
|
||||||
TEST_CASE("Angle trigonometric functions", "[trig][angle]")
|
{
|
||||||
{
|
REQUIRE_THAT(sin(0 * angle[deg]), AlmostEquals(0. * one));
|
||||||
using namespace mp_units::angular;
|
REQUIRE_THAT(sin(90 * angle[deg]), AlmostEquals(1. * one));
|
||||||
using namespace mp_units::angular::unit_symbols;
|
REQUIRE_THAT(sin(180 * angle[deg]), AlmostEquals(0. * one));
|
||||||
using mp_units::angular::unit_symbols::deg;
|
REQUIRE_THAT(sin(270 * angle[deg]), AlmostEquals(-1. * one));
|
||||||
|
|
||||||
SECTION("sin")
|
REQUIRE_THAT(sin(0 * angle[grad]), AlmostEquals(0. * one));
|
||||||
{
|
REQUIRE_THAT(sin(100 * angle[grad]), AlmostEquals(1. * one));
|
||||||
REQUIRE_THAT(sin(0 * angle[deg]), AlmostEquals(0. * one));
|
REQUIRE_THAT(sin(200 * angle[grad]), AlmostEquals(0. * one, 2));
|
||||||
REQUIRE_THAT(sin(90 * angle[deg]), AlmostEquals(1. * one));
|
REQUIRE_THAT(sin(300 * angle[grad]), AlmostEquals(-1. * one));
|
||||||
REQUIRE_THAT(sin(180 * angle[deg]), AlmostEquals(0. * one));
|
}
|
||||||
REQUIRE_THAT(sin(270 * angle[deg]), AlmostEquals(-1. * one));
|
|
||||||
|
SECTION("cos")
|
||||||
REQUIRE_THAT(sin(0 * angle[grad]), AlmostEquals(0. * one));
|
{
|
||||||
REQUIRE_THAT(sin(100 * angle[grad]), AlmostEquals(1. * one));
|
REQUIRE_THAT(cos(0 * angle[deg]), AlmostEquals(1. * one));
|
||||||
REQUIRE_THAT(sin(200 * angle[grad]), AlmostEquals(0. * one, 2));
|
REQUIRE_THAT(cos(90 * angle[deg]), AlmostEquals(0. * one));
|
||||||
REQUIRE_THAT(sin(300 * angle[grad]), AlmostEquals(-1. * one));
|
REQUIRE_THAT(cos(180 * angle[deg]), AlmostEquals(-1. * one));
|
||||||
}
|
REQUIRE_THAT(cos(270 * angle[deg]), AlmostEquals(0. * one));
|
||||||
|
|
||||||
SECTION("cos")
|
REQUIRE_THAT(cos(0 * angle[grad]), AlmostEquals(1. * one));
|
||||||
{
|
REQUIRE_THAT(cos(100 * angle[grad]), AlmostEquals(0. * one));
|
||||||
REQUIRE_THAT(cos(0 * angle[deg]), AlmostEquals(1. * one));
|
REQUIRE_THAT(cos(200 * angle[grad]), AlmostEquals(-1. * one));
|
||||||
REQUIRE_THAT(cos(90 * angle[deg]), AlmostEquals(0. * one));
|
REQUIRE_THAT(cos(300 * angle[grad]), AlmostEquals(0. * one));
|
||||||
REQUIRE_THAT(cos(180 * angle[deg]), AlmostEquals(-1. * one));
|
}
|
||||||
REQUIRE_THAT(cos(270 * angle[deg]), AlmostEquals(0. * one));
|
|
||||||
|
SECTION("tan")
|
||||||
REQUIRE_THAT(cos(0 * angle[grad]), AlmostEquals(1. * one));
|
{
|
||||||
REQUIRE_THAT(cos(100 * angle[grad]), AlmostEquals(0. * one));
|
REQUIRE_THAT(tan(0 * angle[deg]), AlmostEquals(0. * one));
|
||||||
REQUIRE_THAT(cos(200 * angle[grad]), AlmostEquals(-1. * one));
|
REQUIRE_THAT(tan(45 * angle[deg]), AlmostEquals(1. * one));
|
||||||
REQUIRE_THAT(cos(300 * angle[grad]), AlmostEquals(0. * one));
|
REQUIRE_THAT(tan(135 * angle[deg]), AlmostEquals(-1. * one));
|
||||||
}
|
REQUIRE_THAT(tan(180 * angle[deg]), AlmostEquals(0. * one));
|
||||||
|
|
||||||
SECTION("tan")
|
REQUIRE_THAT(tan(0 * angle[grad]), AlmostEquals(0. * one));
|
||||||
{
|
REQUIRE_THAT(tan(50 * angle[grad]), AlmostEquals(1. * one));
|
||||||
REQUIRE_THAT(tan(0 * angle[deg]), AlmostEquals(0. * one));
|
REQUIRE_THAT(tan(150 * angle[grad]), AlmostEquals(-1. * one));
|
||||||
REQUIRE_THAT(tan(45 * angle[deg]), AlmostEquals(1. * one));
|
REQUIRE_THAT(tan(200 * angle[grad]), AlmostEquals(0. * one, 2));
|
||||||
REQUIRE_THAT(tan(135 * angle[deg]), AlmostEquals(-1. * one));
|
}
|
||||||
REQUIRE_THAT(tan(180 * angle[deg]), AlmostEquals(0. * one));
|
}
|
||||||
|
|
||||||
REQUIRE_THAT(tan(0 * angle[grad]), AlmostEquals(0. * one));
|
SECTION("Angle inverse trigonometric functions")
|
||||||
REQUIRE_THAT(tan(50 * angle[grad]), AlmostEquals(1. * one));
|
{
|
||||||
REQUIRE_THAT(tan(150 * angle[grad]), AlmostEquals(-1. * one));
|
using namespace mp_units::angular;
|
||||||
REQUIRE_THAT(tan(200 * angle[grad]), AlmostEquals(0. * one, 2));
|
using namespace mp_units::angular::unit_symbols;
|
||||||
}
|
using mp_units::angular::unit_symbols::deg;
|
||||||
}
|
|
||||||
|
SECTION("asin")
|
||||||
TEST_CASE("Angle inverse trigonometric functions", "[inv trig][angle]")
|
{
|
||||||
{
|
REQUIRE_THAT(asin(-1 * one), AlmostEquals(-90. * angle[deg]));
|
||||||
using namespace mp_units::angular;
|
REQUIRE_THAT(asin(0 * one), AlmostEquals(0. * angle[deg]));
|
||||||
using namespace mp_units::angular::unit_symbols;
|
REQUIRE_THAT(asin(1 * one), AlmostEquals(90. * angle[deg]));
|
||||||
using mp_units::angular::unit_symbols::deg;
|
}
|
||||||
|
|
||||||
SECTION("asin")
|
SECTION("acos")
|
||||||
{
|
{
|
||||||
REQUIRE_THAT(asin(-1 * one), AlmostEquals(-90. * angle[deg]));
|
REQUIRE_THAT(asin(-1 * one), AlmostEquals(-90. * angle[deg]));
|
||||||
REQUIRE_THAT(asin(0 * one), AlmostEquals(0. * angle[deg]));
|
REQUIRE_THAT(asin(0 * one), AlmostEquals(0. * angle[deg]));
|
||||||
REQUIRE_THAT(asin(1 * one), AlmostEquals(90. * angle[deg]));
|
REQUIRE_THAT(asin(1 * one), AlmostEquals(90. * angle[deg]));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("acos")
|
SECTION("atan")
|
||||||
{
|
{
|
||||||
REQUIRE_THAT(asin(-1 * one), AlmostEquals(-90. * angle[deg]));
|
REQUIRE_THAT(atan(-1 * one), AlmostEquals(-45. * angle[deg]));
|
||||||
REQUIRE_THAT(asin(0 * one), AlmostEquals(0. * angle[deg]));
|
REQUIRE_THAT(atan(0 * one), AlmostEquals(0. * angle[deg]));
|
||||||
REQUIRE_THAT(asin(1 * one), AlmostEquals(90. * angle[deg]));
|
REQUIRE_THAT(atan(1 * one), AlmostEquals(45. * angle[deg]));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
SECTION("atan")
|
|
||||||
{
|
SECTION("Angle atan2 functions")
|
||||||
REQUIRE_THAT(atan(-1 * one), AlmostEquals(-45. * angle[deg]));
|
{
|
||||||
REQUIRE_THAT(atan(0 * one), AlmostEquals(0. * angle[deg]));
|
using namespace mp_units::angular;
|
||||||
REQUIRE_THAT(atan(1 * one), AlmostEquals(45. * angle[deg]));
|
using namespace mp_units::angular::unit_symbols;
|
||||||
}
|
using mp_units::angular::unit_symbols::deg;
|
||||||
}
|
|
||||||
|
SECTION("atan2 should work on the same quantities")
|
||||||
TEST_CASE("Angle atan2 functions", "[atan2][angle]")
|
{
|
||||||
{
|
REQUIRE_THAT(atan2(-1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(-45. * angle[deg]));
|
||||||
using namespace mp_units::angular;
|
REQUIRE_THAT(atan2(0. * isq::length[km], 1. * isq::length[km]), AlmostEquals(0. * angle[deg]));
|
||||||
using namespace mp_units::angular::unit_symbols;
|
REQUIRE_THAT(atan2(1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(45. * angle[deg]));
|
||||||
using mp_units::angular::unit_symbols::deg;
|
}
|
||||||
|
SECTION("atan2 should work with different units of the same dimension")
|
||||||
SECTION("atan2 should work on the same quantities")
|
{
|
||||||
{
|
REQUIRE_THAT(atan2(-1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(-45. * angle[deg]));
|
||||||
REQUIRE_THAT(atan2(-1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(-45. * angle[deg]));
|
REQUIRE_THAT(atan2(0. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(0. * angle[deg]));
|
||||||
REQUIRE_THAT(atan2(0. * isq::length[km], 1. * isq::length[km]), AlmostEquals(0. * angle[deg]));
|
REQUIRE_THAT(atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * angle[deg]));
|
||||||
REQUIRE_THAT(atan2(1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(45. * angle[deg]));
|
}
|
||||||
}
|
|
||||||
SECTION("atan2 should work with different units of the same dimension")
|
|
||||||
{
|
|
||||||
REQUIRE_THAT(atan2(-1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(-45. * angle[deg]));
|
|
||||||
REQUIRE_THAT(atan2(0. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(0. * angle[deg]));
|
|
||||||
REQUIRE_THAT(atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * angle[deg]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -60,22 +60,25 @@ constexpr bool within_4_ulps(T a, T b)
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// conversion requiring radical magnitudes
|
TEST_CASE("quantity operations", "[quantity]")
|
||||||
TEST_CASE("unit conversions support radical magnitudes", "[conversion][radical]")
|
|
||||||
{
|
{
|
||||||
REQUIRE(within_4_ulps(sqrt((1.0 * m) * (1.0 * km)).numerical_value_in(m), sqrt(1000.0)));
|
// conversion requiring radical magnitudes
|
||||||
}
|
SECTION("unit conversions support radical magnitudes")
|
||||||
|
{
|
||||||
|
REQUIRE(within_4_ulps(sqrt((1.0 * m) * (1.0 * km)).numerical_value_in(m), sqrt(1000.0)));
|
||||||
|
}
|
||||||
|
|
||||||
// Reproducing issue #474 exactly:
|
// Reproducing issue #474 exactly:
|
||||||
TEST_CASE("Issue 474 is fixed", "[conversion][radical]")
|
SECTION("Issue 474 is fixed")
|
||||||
{
|
{
|
||||||
constexpr auto val_issue_474 = 8.0 * si::si2019::boltzmann_constant * 1000.0 * K / (std::numbers::pi * 10 * Da);
|
constexpr auto val_issue_474 = 8.0 * si::si2019::boltzmann_constant * 1000.0 * K / (std::numbers::pi * 10 * Da);
|
||||||
REQUIRE(within_4_ulps(sqrt(val_issue_474).numerical_value_in(m / s),
|
REQUIRE(within_4_ulps(sqrt(val_issue_474).numerical_value_in(m / s),
|
||||||
sqrt(val_issue_474.numerical_value_in(m * m / s / s))));
|
sqrt(val_issue_474.numerical_value_in(m * m / s / s))));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Volatile representation type", "[volatile]")
|
SECTION("Volatile representation type")
|
||||||
{
|
{
|
||||||
volatile std::int16_t vint = 123;
|
volatile std::int16_t vint = 123;
|
||||||
REQUIRE(quantity(vint * m).numerical_value_in(m) == 123);
|
REQUIRE(quantity(vint * m).numerical_value_in(m) == 123);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,24 +25,28 @@
|
|||||||
#include <mp-units/systems/isq/mechanics.h>
|
#include <mp-units/systems/isq/mechanics.h>
|
||||||
#include <mp-units/systems/isq/space_and_time.h>
|
#include <mp-units/systems/isq/space_and_time.h>
|
||||||
#include <mp-units/systems/si/units.h>
|
#include <mp-units/systems/si/units.h>
|
||||||
|
#if MP_UNITS_HOSTED
|
||||||
template<class T>
|
#include <mp-units/cartesian_vector.h>
|
||||||
requires mp_units::is_scalar<T>
|
#endif
|
||||||
constexpr bool mp_units::is_vector<T> = true;
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using namespace mp_units;
|
using namespace mp_units;
|
||||||
using namespace mp_units::cgs;
|
using namespace mp_units::cgs;
|
||||||
using namespace mp_units::cgs::unit_symbols;
|
using namespace mp_units::cgs::unit_symbols;
|
||||||
|
#if MP_UNITS_HOSTED
|
||||||
|
using v = cartesian_vector<double>;
|
||||||
|
#endif
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/Centimetre%E2%80%93gram%E2%80%93second_system_of_units#Definitions_and_conversion_factors_of_CGS_units_in_mechanics
|
// https://en.wikipedia.org/wiki/Centimetre%E2%80%93gram%E2%80%93second_system_of_units#Definitions_and_conversion_factors_of_CGS_units_in_mechanics
|
||||||
static_assert(isq::length(100 * cm) == isq::length(1 * si::metre));
|
static_assert(isq::length(100 * cm) == isq::length(1 * si::metre));
|
||||||
static_assert(isq::mass(1000 * g) == isq::mass(1 * si::kilogram));
|
static_assert(isq::mass(1000 * g) == isq::mass(1 * si::kilogram));
|
||||||
static_assert(isq::time(1 * s) == isq::time(1 * si::second));
|
static_assert(isq::time(1 * s) == isq::time(1 * si::second));
|
||||||
static_assert(isq::speed(100 * cm / s) == isq::speed(1 * si::metre / si::second));
|
static_assert(isq::speed(100 * cm / s) == isq::speed(1 * si::metre / si::second));
|
||||||
static_assert(isq::acceleration(100 * Gal) == isq::acceleration(1 * si::metre / square(si::second)));
|
#if MP_UNITS_HOSTED
|
||||||
static_assert(isq::force(100'000 * dyn) == isq::force(1 * si::newton));
|
static_assert(isq::acceleration(v{100} * Gal) == isq::acceleration(v{1} * si::metre / square(si::second)));
|
||||||
|
static_assert(isq::force(v{100'000} * dyn) == isq::force(v{1} * si::newton));
|
||||||
|
#endif
|
||||||
static_assert(isq::energy(10'000'000 * erg) == isq::energy(1 * si::joule));
|
static_assert(isq::energy(10'000'000 * erg) == isq::energy(1 * si::joule));
|
||||||
static_assert(isq::power(10'000'000 * erg / s) == isq::power(1 * si::watt));
|
static_assert(isq::power(10'000'000 * erg / s) == isq::power(1 * si::watt));
|
||||||
static_assert(isq::pressure(10 * Ba) == isq::pressure(1 * si::pascal));
|
static_assert(isq::pressure(10 * Ba) == isq::pressure(1 * si::pascal));
|
||||||
|
@@ -237,17 +237,17 @@ static_assert(compare(round<si::second>(-1999. * isq::time[ms]), -2. * isq::time
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// non-truncating
|
// non-truncating
|
||||||
static_assert(compare(inverse<us>(250 * Hz), 4000 * us));
|
static_assert(compare(kind_of<isq::time>(inverse<us>(250 * Hz)), 4000 * us));
|
||||||
static_assert(compare(inverse<us>(250 * kHz), 4 * us));
|
static_assert(compare(kind_of<isq::time>(inverse<us>(250 * kHz)), 4 * us));
|
||||||
static_assert(compare(inverse<ks>(250 * uHz), 4 * ks));
|
static_assert(compare(kind_of<isq::time>(inverse<ks>(250 * uHz)), 4 * ks));
|
||||||
|
|
||||||
// truncating
|
// truncating
|
||||||
static_assert(compare(inverse<s>(1 * kHz), 0 * s));
|
static_assert(compare(kind_of<isq::time>(inverse<s>(1 * kHz)), 0 * s));
|
||||||
|
|
||||||
// floating-point representation does not truncate
|
// floating-point representation does not truncate
|
||||||
static_assert(compare(inverse<s>(1. * kHz), 0.001 * s));
|
static_assert(compare(kind_of<isq::time>(inverse<s>(1. * kHz)), 0.001 * s));
|
||||||
|
|
||||||
// check if constraints work properly for a derived unit of a narrowed kind
|
// check if constraints work properly for a derived unit of a narrowed kind
|
||||||
static_assert(compare(inverse<Hz>(1 * s), 1 * Hz));
|
static_assert(compare(kind_of<isq::frequency>(inverse<Hz>(1 * s)), 1 * Hz));
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@@ -378,4 +378,14 @@ static_assert(invalid_comparison<frequency(1 * hertz), activity(1 * becquerel)>)
|
|||||||
static_assert(invalid_comparison<angular_measure(1 * radian), solid_angular_measure(1 * steradian)>);
|
static_assert(invalid_comparison<angular_measure(1 * radian), solid_angular_measure(1 * steradian)>);
|
||||||
static_assert(invalid_comparison<angular_measure(1 * radian), storage_capacity(1 * bit)>);
|
static_assert(invalid_comparison<angular_measure(1 * radian), storage_capacity(1 * bit)>);
|
||||||
|
|
||||||
|
// make_reference
|
||||||
|
static_assert(is_of_type<make_reference(length, metre), reference<length_, metre_>>);
|
||||||
|
static_assert(is_of_type<make_reference(width, metre), reference<width_, metre_>>);
|
||||||
|
static_assert(is_of_type<make_reference(kind_of<length>, metre), metre_>);
|
||||||
|
static_assert(is_of_type<make_reference(get_quantity_spec(metre), metre), metre_>);
|
||||||
|
static_assert(is_of_type<make_reference(get_quantity_spec(hertz), hertz), hertz_>);
|
||||||
|
static_assert(is_of_type<make_reference(kind_of<frequency>, hertz), hertz_>);
|
||||||
|
static_assert(is_of_type<make_reference(get_quantity_spec(watt), watt), watt_>);
|
||||||
|
static_assert(is_of_type<make_reference(kind_of<power>, watt), reference<kind_of_<power_>, watt_>>);
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
Reference in New Issue
Block a user