Add 'xo-ratio/' from commit 'd18e9afc1d'
git-subtree-dir: xo-ratio git-subtree-mainline:b4c2b98a88git-subtree-split:d18e9afc1d
This commit is contained in:
commit
92df7ca236
31 changed files with 5283 additions and 0 deletions
221
xo-ratio/.github/workflows/ubuntu-main.yml
vendored
Normal file
221
xo-ratio/.github/workflows/ubuntu-main.yml
vendored
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
name: build xo-ratio + dependencies
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
|
||||
# You can convert this to a matrix build if you need cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: checkout source
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
# install catch2, doxygen. see
|
||||
# [[https://stackoverflow.com/questions/57982945/how-to-apt-get-install-in-a-github-actions-workflow]]
|
||||
|
||||
echo "::group::install catch2"
|
||||
sudo apt-get install -y catch2
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::install doxygen"
|
||||
sudo apt-get install -y doxygen
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::install sphinx"
|
||||
sudo apt-get install -y python3-sphinx
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::install sphinx readthedocs theme"
|
||||
sudo apt-get install -y python3-sphinx-rtd-theme
|
||||
echo "::endgroup"
|
||||
|
||||
#echo "::group::install pybind11"
|
||||
#sudo apt-get install -y pybind11-dev
|
||||
#echo "::endgroup"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: clone xo-cmake
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Rconybea/xo-cmake
|
||||
path: repo/xo-cmake
|
||||
|
||||
- name: build xo-cmake
|
||||
run: |
|
||||
XONAME=xo-cmake
|
||||
XOSRC=repo/${XONAME}
|
||||
BUILDDIR=${{github.workspace}}/build_${XONAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
echo "::group::configure ${XONAME}"
|
||||
cmake -B ${BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} ${XOSRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XONAME}"
|
||||
cmake --build ${BUILDDIR} --config ${{env.BUILD_TYPE}}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XONAME}"
|
||||
cmake --install ${BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: clone xo-indentlog
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Rconybea/indentlog
|
||||
path: repo/xo-indentlog
|
||||
|
||||
- name: build xo-indentlog
|
||||
run: |
|
||||
XONAME=xo-indentlog
|
||||
XOSRC=repo/${XONAME}
|
||||
BUILDDIR=${{github.workspace}}/build_${XONAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
echo "::group::repo dir tree"
|
||||
tree -L 2 repo
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XONAME}"
|
||||
cmake -B ${BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} ${XOSRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XONAME}"
|
||||
cmake --build ${BUILDDIR} --config ${{env.BUILD_TYPE}} -j
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XONAME}"
|
||||
cmake --install ${BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree -L 3 ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: clone xo-randomgen
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Rconybea/randomgen
|
||||
path: repo/xo-randomgen
|
||||
|
||||
- name: build xo-randomgen
|
||||
run: |
|
||||
XONAME=xo-randomgen
|
||||
XOSRC=repo/${XONAME}
|
||||
BUILDDIR=${{github.workspace}}/build_${XONAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
echo "::group::repo dir tree"
|
||||
tree -L 2 repo
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XONAME}"
|
||||
cmake -B ${BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} ${XOSRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XONAME}"
|
||||
cmake --build ${BUILDDIR} --config ${{env.BUILD_TYPE}} -j
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XONAME}"
|
||||
cmake --install ${BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree -L 3 ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: clone xo-flatstring
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Rconybea/xo-flatstring
|
||||
path: repo/xo-flatstring
|
||||
|
||||
- name: build xo-flatstring
|
||||
run: |
|
||||
XONAME=xo-flatstring
|
||||
XOSRC=repo/${XONAME}
|
||||
BUILDDIR=${{github.workspace}}/build_${XONAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
echo "::group::repo dir tree"
|
||||
tree -L 2 repo
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XONAME}"
|
||||
cmake -B ${BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} ${XOSRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XONAME}"
|
||||
cmake --build ${BUILDDIR} --config ${{env.BUILD_TYPE}} -j
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XONAME}"
|
||||
cmake --install ${BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree -L 3 ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: build self (xo-ratio)
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: |
|
||||
XONAME=xo-ratio
|
||||
BUILDDIR=${{github.workspace}}/build_${XONAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
echo "::group::repo dir tree"
|
||||
tree -L 2 repo
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XONAME}"
|
||||
cmake -B ${BUILDDIR} -DCMAKE_MODULE_PATH=${PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${PREFIX} -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XONAME}"
|
||||
cmake --build ${BUILDDIR} --config ${{env.BUILD_TYPE}}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::run unit tests ${XONAME}"
|
||||
cmake --build ${BUILDDIR} -- test
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XONAME}"
|
||||
cmake --install ${BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree -L 3 ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
# Execute tests defined by the CMake configuration.
|
||||
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
(cd ${BUILDDIR} && ctest -C ${{env.BUILD_TYPE}})
|
||||
212
xo-ratio/.github/workflows/xo-cpp-main.yml
vendored
Normal file
212
xo-ratio/.github/workflows/xo-cpp-main.yml
vendored
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
name: XO ratio builder
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
|
||||
# You can convert this to a matrix build if you need cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
# custom docker image. see github.com:rconybea/docker-xo-builder for definition
|
||||
image: ghcr.io/rconybea/docker-xo-builder:v1
|
||||
|
||||
steps:
|
||||
- name: xo-cmake
|
||||
run: |
|
||||
# treat github.com as known host to prevent shtoopid SSL errors
|
||||
mkdir -p ~/.ssh
|
||||
ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts
|
||||
cat ~/.ssh/known_hosts
|
||||
|
||||
git config --global http.sslVerify false
|
||||
|
||||
XO_NAME=xo-cmake
|
||||
XO_SRC=repo/${XO_NAME}
|
||||
XO_BUILDDIR=${{github.workspace}}/build_${XO_NAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
XO_REPO=https://github.com/rconybea/xo-cmake.git
|
||||
|
||||
mkdir -p ${XO_SRC}
|
||||
mkdir -p ${XO_BUILDDIR}
|
||||
|
||||
echo "::group::clone ${XO_NAME}"
|
||||
export GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no'
|
||||
export GIT_SSL_NOVERIFY=true
|
||||
git clone ${XO_REPO} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XO_NAME}"
|
||||
cmake -B ${XO_BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XO_NAME}"
|
||||
cmake --build ${XO_BUILDDIR} -j
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XO_NAME}"
|
||||
cmake --install ${XO_BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: xo-indentlog
|
||||
run: |
|
||||
XO_NAME=xo-indentlog
|
||||
XO_SRC=repo/${XO_NAME}
|
||||
XO_BUILDDIR=${{github.workspace}}/build_${XO_NAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
XO_REPO=https://github.com/rconybea/indentlog.git
|
||||
|
||||
mkdir -p ${XO_SRC}
|
||||
mkdir -p ${XO_BUILDDIR}
|
||||
|
||||
echo "::group::clone ${XO_NAME}"
|
||||
export GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no'
|
||||
export GIT_SSL_NOVERIFY=true
|
||||
git clone ${XO_REPO} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XO_NAME}"
|
||||
cmake -B ${XO_BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XO_NAME}"
|
||||
cmake --build ${XO_BUILDDIR} -j
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XO_NAME}"
|
||||
cmake --install ${XO_BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: xo-flatstring
|
||||
run: |
|
||||
XO_NAME=xo-flatstring
|
||||
XO_SRC=repo/${XO_NAME}
|
||||
XO_BUILDDIR=${{github.workspace}}/build_${XO_NAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
XO_REPO=https://github.com/rconybea/${XO_NAME}.git
|
||||
|
||||
mkdir -p ${XO_SRC}
|
||||
mkdir -p ${XO_BUILDDIR}
|
||||
|
||||
echo "::group::clone ${XO_NAME}"
|
||||
export GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no'
|
||||
export GIT_SSL_NOVERIFY=true
|
||||
git clone ${XO_REPO} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XO_NAME}"
|
||||
cmake -B ${XO_BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XO_NAME}"
|
||||
cmake --build ${XO_BUILDDIR} -j
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XO_NAME}"
|
||||
cmake --install ${XO_BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: xo-randomgen
|
||||
run: |
|
||||
XO_NAME=xo-randomgen
|
||||
XO_SRC=repo/${XO_NAME}
|
||||
XO_BUILDDIR=${{github.workspace}}/build_${XO_NAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
XO_REPO=https://github.com/rconybea/randomgen.git
|
||||
|
||||
mkdir -p ${XO_SRC}
|
||||
mkdir -p ${XO_BUILDDIR}
|
||||
|
||||
echo "::group::clone ${XO_NAME}"
|
||||
export GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no'
|
||||
export GIT_SSL_NOVERIFY=true
|
||||
git clone ${XO_REPO} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XO_NAME}"
|
||||
cmake -B ${XO_BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XO_NAME}"
|
||||
cmake --build ${XO_BUILDDIR} -j
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XO_NAME}"
|
||||
cmake --install ${XO_BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: xo-ratio
|
||||
run: |
|
||||
XO_NAME=xo-ratio
|
||||
XO_SRC=repo/${XO_NAME}
|
||||
XO_BUILDDIR=${{github.workspace}}/build_${XO_NAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
XO_REPO=https://github.com/rconybea/${XO_NAME}.git
|
||||
|
||||
mkdir -p ${XO_SRC}
|
||||
mkdir -p ${XO_BUILDDIR}
|
||||
|
||||
echo "::group::clone ${XO_NAME}"
|
||||
export GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no'
|
||||
export GIT_SSL_NOVERIFY=true
|
||||
git clone ${XO_REPO} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XO_NAME}"
|
||||
cmake -B ${XO_BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XO_NAME}"
|
||||
cmake --build ${XO_BUILDDIR} -j
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::test ${XO_NAME}"
|
||||
cmake --build ${XO_BUILDDIR} -- test
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XO_NAME}"
|
||||
cmake --install ${XO_BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree ${PREFIX}
|
||||
echo "::endgroup"
|
||||
8
xo-ratio/.gitignore
vendored
Normal file
8
xo-ratio/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# emacs project control file
|
||||
.projectile
|
||||
# clangd working space (see emacs+lsp)
|
||||
.cache
|
||||
# typical cmake build directory (source-tree-nephew)
|
||||
.build*
|
||||
# symlink to builddir/compile_commands.json; should be set manually in dev sandbox
|
||||
compile_commands.json
|
||||
47
xo-ratio/CMakeLists.txt
Normal file
47
xo-ratio/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# xo-ratio/CMakeLists.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(xo_ratio VERSION 1.0)
|
||||
enable_language(CXX)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(cmake/xo-bootstrap-macros.cmake)
|
||||
|
||||
xo_cxx_toplevel_options3()
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# c++ settings
|
||||
|
||||
# one-time project-specific c++ flags. usually empty
|
||||
set(PROJECT_CXX_FLAGS "")
|
||||
#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2")
|
||||
add_definitions(${PROJECT_CXX_FLAGS})
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
add_subdirectory(example)
|
||||
add_subdirectory(utest)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# provide find_package() support for projects using this library
|
||||
|
||||
set(SELF_LIB xo_ratio)
|
||||
xo_add_headeronly_library(${SELF_LIB})
|
||||
xo_install_library4(${SELF_LIB} ${PROJECT_NAME}Targets)
|
||||
xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# docs targets depend on all the other library/utest targets
|
||||
#
|
||||
add_subdirectory(docs)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# dependencies
|
||||
|
||||
xo_headeronly_dependency(${SELF_LIB} xo_reflectutil)
|
||||
xo_headeronly_dependency(${SELF_LIB} xo_flatstring)
|
||||
#xo_headeronly_dependency(${SELF_LIB} randomgen)
|
||||
# etc..
|
||||
|
||||
# end CMakeLists.txt
|
||||
29
xo-ratio/LICENSE
Normal file
29
xo-ratio/LICENSE
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
Copyright (c) 2024 Roland Conybeare <git3ub@nym.hush.com>, All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Please also refer to the file .github/CONTRIBUTING.md, which clarifies licensing of
|
||||
external contributions to this project including patches, pull requests, etc.
|
||||
85
xo-ratio/README.md
Normal file
85
xo-ratio/README.md
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# ratio library
|
||||
|
||||
Header-only, constexpr library providing exact representation for rational numbers.
|
||||
|
||||
Relative to `std::ratio`:
|
||||
1. Uses `constexpr` instead of creating new types.
|
||||
This means it can be used seamlessly at runtime.
|
||||
2. Supports a few more arithmetic operations,
|
||||
for example exponentiation to integer powers.
|
||||
3. Provides constexpr conversion to fixed-capacity strings (using xo-flatstring)
|
||||
3. Provides concept support (with c++20)
|
||||
4. Requires modern (c++17) support to achieve this
|
||||
|
||||
Relative to `boost::ratio`:
|
||||
1. Streamlined, assumes modern compiler support
|
||||
|
||||
## Documentation
|
||||
|
||||
- xo-ratio documentation [under construction]: [documentation](https://rconybea.github.io/web/xo-ratio/html/index.html)
|
||||
- unit test coverage here: [coverage](https://rconybea.github.io/web/xo-ratio/ccov/html/index.html)
|
||||
|
||||
## Getting Started
|
||||
|
||||
### install dependencies
|
||||
|
||||
- [github/Rconybea/xo-cmake](https://github.com/Rconybea/xo-cmake) cmake macros
|
||||
- [github/Rconybea/xo-flatstring](https://github.com/Rconybea/xo-flatstring) fixed-capacity strings
|
||||
- [github/rconybea/xo-indentlog](https://github.com/Rconybea/xo-indentlog) logging (used by unit tests)
|
||||
- [github/rconybea/xo-randomgen](https://github.com/Rconybea/xo-randomgen) rng (used by unit tests)
|
||||
|
||||
### copy repository locally
|
||||
|
||||
Using `xo-build` (provided by `xo-cmake`):
|
||||
```
|
||||
$ xo-build --clone xo-ratio`
|
||||
```
|
||||
|
||||
or equivalently:
|
||||
```
|
||||
$ cd ~/proj # for example
|
||||
$ git clone https://github.com/Rconybea/xo-ratio
|
||||
```
|
||||
|
||||
### build + install
|
||||
|
||||
Using `xo-build`:
|
||||
```
|
||||
$ xo-build --configure --build --install xo-ratio
|
||||
```
|
||||
|
||||
or equivalently:
|
||||
```
|
||||
$ PREFIX=/usr/local # for example
|
||||
$ BUILDDIR=.build # for example
|
||||
$ mkdir xo-ratio/${BUILDDIR}
|
||||
$ cmake -DCMAKE_INSTALL_PREFIX=${PREFIX} -S xo-ratio -B xo-ratio/${BUILDDIR}
|
||||
$ cmake --build xo-ratio/${BUILDDIR}
|
||||
$ cmake --install xo-ratio/${BUILDDIR}
|
||||
```
|
||||
|
||||
### build with unit test coverage
|
||||
```
|
||||
$ cd xo-ratio
|
||||
$ mkdir .build-ccov
|
||||
$ cmake -DCMAKE_BUILD_TYPE=coverage -B .build-ccov
|
||||
$ cmake --build .build-ccov
|
||||
```
|
||||
|
||||
run coverage-enabled unit tests
|
||||
```
|
||||
$ cmake --build .build-ccov -- test
|
||||
```
|
||||
|
||||
generate html+text coverage report
|
||||
```
|
||||
$ cmake --build .build-ccov -- ccov
|
||||
```
|
||||
|
||||
browse to `.build-ccov/ccov/html/index.html`
|
||||
|
||||
### LSP support
|
||||
```
|
||||
$ cd xo-ratio
|
||||
$ ln -s .build/compile_commands.json
|
||||
```
|
||||
33
xo-ratio/cmake/xo-bootstrap-macros.cmake
Normal file
33
xo-ratio/cmake/xo-bootstrap-macros.cmake
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# ----------------------------------------------------------------
|
||||
# for example:
|
||||
# $ PREFIX=/usr/local # for example
|
||||
# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build
|
||||
#
|
||||
# will get
|
||||
# CMAKE_MODULE_PATH
|
||||
# from xo-cmake-config --cmake-module-path
|
||||
#
|
||||
# and expect .cmake macros in
|
||||
# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED)
|
||||
|
||||
if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL "prefix"))
|
||||
message(FATAL "could not find xo-cmake-config executable")
|
||||
endif()
|
||||
|
||||
if (NOT XO_SUBMODULE_BUILD)
|
||||
if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix))
|
||||
# default to typical install location for xo-project-macros
|
||||
execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH)
|
||||
message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# needs to have been installed somewhere on CMAKE_MODULE_PATH,
|
||||
# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX)
|
||||
#
|
||||
include(xo_macros/xo_cxx)
|
||||
|
||||
xo_cxx_bootstrap_message()
|
||||
17
xo-ratio/cmake/xo_ratioConfig.cmake.in
Normal file
17
xo-ratio/cmake/xo_ratioConfig.cmake.in
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
# note: changes to find_dependency() calls here
|
||||
# must coordinate with xo_dependency() calls
|
||||
# in xo-reactor/src/reactor/CMakeLists.txt
|
||||
#
|
||||
find_dependency(xo_flatstring)
|
||||
#find_dependency(subsys)
|
||||
#find_dependency(Eigen3)
|
||||
#find_dependency(webutil)
|
||||
#find_dependency(printjson)
|
||||
#find_dependency(callback)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
5
xo-ratio/docs/CMakeLists.txt
Normal file
5
xo-ratio/docs/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# xo-flatstring/docs/CMakeLists.txt
|
||||
|
||||
xo_doxygen_collect_deps()
|
||||
xo_docdir_doxygen_config()
|
||||
xo_docdir_sphinx_config(index.rst install.rst ratio-reference.rst ratio-class.rst ratio-functions.rst)
|
||||
2816
xo-ratio/docs/Doxyfile.in
Normal file
2816
xo-ratio/docs/Doxyfile.in
Normal file
File diff suppressed because it is too large
Load diff
BIN
xo-ratio/docs/_static/img/favicon.ico
vendored
Normal file
BIN
xo-ratio/docs/_static/img/favicon.ico
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 302 KiB |
77
xo-ratio/docs/_static/img/icon.svg
vendored
Normal file
77
xo-ratio/docs/_static/img/icon.svg
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="108.32474mm"
|
||||
height="108.39381mm"
|
||||
viewBox="0 0 108.32474 108.39382"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
sodipodi:docname="icon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="157"
|
||||
inkscape:cy="322.5"
|
||||
inkscape:window-width="1607"
|
||||
inkscape:window-height="1085"
|
||||
inkscape:window-x="26"
|
||||
inkscape:window-y="23"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="layer1"
|
||||
fit-margin-top="9.8"
|
||||
fit-margin-left="8"
|
||||
fit-margin-right="10"
|
||||
fit-margin-bottom="9.8" />
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-47.453609,-27.112369)">
|
||||
<circle
|
||||
style="fill:#ff0000;fill-rule:evenodd;stroke-width:0.264583"
|
||||
id="path31"
|
||||
cx="68.211342"
|
||||
cy="87.603088"
|
||||
r="12.757732" />
|
||||
<circle
|
||||
style="fill:#ffcf00;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
|
||||
id="path31-3"
|
||||
cx="122.47422"
|
||||
cy="49.670101"
|
||||
r="12.757732" />
|
||||
<circle
|
||||
style="fill:#6b6bff;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
|
||||
id="path31-3-6"
|
||||
cx="133.02061"
|
||||
cy="112.94845"
|
||||
r="12.757732" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 73.144329,75.525772 C 85.051544,48.309278 109.54639,49.670102 109.54639,49.670102"
|
||||
id="path589" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2.44548;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 76.544292,96.598799 C 95.429921,116.11457 120.4897,113.37017 120.4897,113.37017"
|
||||
id="path593" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 132,58.175257 c 17.01031,17.350515 7.14433,43.886603 7.14433,43.886603"
|
||||
id="path788" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
77
xo-ratio/docs/_static/img/xo-icon.svg
vendored
Normal file
77
xo-ratio/docs/_static/img/xo-icon.svg
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="108.32474mm"
|
||||
height="108.39381mm"
|
||||
viewBox="0 0 108.32474 108.39382"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
sodipodi:docname="xo-icon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="157"
|
||||
inkscape:cy="322.5"
|
||||
inkscape:window-width="1607"
|
||||
inkscape:window-height="1085"
|
||||
inkscape:window-x="26"
|
||||
inkscape:window-y="23"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="layer1"
|
||||
fit-margin-top="9.8"
|
||||
fit-margin-left="8"
|
||||
fit-margin-right="10"
|
||||
fit-margin-bottom="9.8" />
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-47.453609,-27.112369)">
|
||||
<circle
|
||||
style="fill:#ff0000;fill-rule:evenodd;stroke-width:0.264583"
|
||||
id="path31"
|
||||
cx="68.211342"
|
||||
cy="87.603088"
|
||||
r="12.757732" />
|
||||
<circle
|
||||
style="fill:#ffcf00;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
|
||||
id="path31-3"
|
||||
cx="122.47422"
|
||||
cy="49.670101"
|
||||
r="12.757732" />
|
||||
<circle
|
||||
style="fill:#6b6bff;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
|
||||
id="path31-3-6"
|
||||
cx="133.02061"
|
||||
cy="112.94845"
|
||||
r="12.757732" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 73.144329,75.525772 C 85.051544,48.309278 109.54639,49.670102 109.54639,49.670102"
|
||||
id="path589" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2.44548;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 76.544292,96.598799 C 95.429921,116.11457 120.4897,113.37017 120.4897,113.37017"
|
||||
id="path593" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 132,58.175257 c 17.01031,17.350515 7.14433,43.886603 7.14433,43.886603"
|
||||
id="path788" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
36
xo-ratio/docs/conf.py
Normal file
36
xo-ratio/docs/conf.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
project = 'xo ratio documentation'
|
||||
copyright = '2024, Roland Conybeare'
|
||||
author = 'Roland Conybeare'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
#extensions = []
|
||||
extensions = [ "breathe",
|
||||
"sphinx.ext.autodoc" # generate info from docstrings
|
||||
]
|
||||
|
||||
# note: breathe requires doxygen xml output -> must have GENERATE_XML = YES in Doxyfile.in
|
||||
# match project name in Doxyfile.in
|
||||
breathe_default_project = "xodoxxml"
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
#html_theme = 'alabaster'
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_static_path = ['_static']
|
||||
html_favicon = '_static/img/favicon.ico'
|
||||
35
xo-ratio/docs/index.rst
Normal file
35
xo-ratio/docs/index.rst
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
xo-ratio documentation
|
||||
======================
|
||||
|
||||
xo-ratio is a lightweight header-only library that provides a constexpr
|
||||
fixed-size no-allocation ratio implementation.
|
||||
|
||||
Why ``ratio``?
|
||||
|
||||
1. ``ratio`` instances can be used as template arguments. [1]_
|
||||
|
||||
2. ``ratio`` operations (arithmetic, integer powers, string conversion, ...) are ``constexpr``, so can be done at compile time. [2]_
|
||||
|
||||
3. a ``ratio`` expression can occupy both compile-time and runtime roles. [3]_
|
||||
|
||||
.. [1] verified with gcc 13.2.0; clang 18 does not recognize ratio<T> as a structured type for some reason.
|
||||
|
||||
.. [2] Although allocation is permitted in constexpr code, it's subject to several restrictions.
|
||||
it's not yet possible (as of c++23) to use ``std::string`` at compile time.
|
||||
Using ``xo::flatstring`` instead.
|
||||
|
||||
.. [3] contrast with a solution relying on template arguments, which must then be compile-time-only.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: xo-ratio contents:
|
||||
|
||||
install
|
||||
ratio-reference
|
||||
|
||||
Indices and Tables
|
||||
------------------
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`search`
|
||||
96
xo-ratio/docs/install.rst
Normal file
96
xo-ratio/docs/install.rst
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
.. _install:
|
||||
|
||||
.. toctree
|
||||
:maxdepth: 2
|
||||
|
||||
Source
|
||||
======
|
||||
|
||||
Source code lives on github `here`_
|
||||
|
||||
.. _here: https://github.com/rconybea/xo-ratio
|
||||
|
||||
To clone from git:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone https://github.com/rconybea/xo-ratio
|
||||
|
||||
Implementation relies on c++20 features (expanded use of constexpr; class-instances as template arguments).
|
||||
Tested with gcc 13.2
|
||||
|
||||
Install
|
||||
=======
|
||||
|
||||
Since xo-ratio is header-only, can incorporate into another project just by copying the include directories
|
||||
to somewhere convenient.
|
||||
|
||||
Copy includes
|
||||
-------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# For example..
|
||||
cd myproject
|
||||
mkdir -p ext/xo-ratio
|
||||
rsync -a -v path/to/xo-ratio/include/ ext/xo-ratio/
|
||||
|
||||
Include as git submodule
|
||||
------------------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd myproject
|
||||
git submodule add -b main https://github.com/rconybea/xo-ratio ext/xo-ratio
|
||||
git submodule update --init
|
||||
|
||||
This assumes you organize directly-incorporated dependencies under directory ``myproject/ext``.
|
||||
You would then add ``myproject/ext/xo-ratio/include`` to your compiler's include path,
|
||||
and from c++ do something like
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include <xo/ratio/ratio.hpp>
|
||||
|
||||
in c++ source files that rely on xo-ratio
|
||||
|
||||
Supported compilers
|
||||
-------------------
|
||||
|
||||
* developed with gcc 13.2.0; github CI using gcc 11.4.0 (asof April 2024)
|
||||
|
||||
Building from source
|
||||
--------------------
|
||||
|
||||
Although the xo-ratio library is header-only, unit tests have some dependencies.
|
||||
Example instructions (github CI) for build starting from stock ubuntu are in `ubuntu-main.yml`_
|
||||
|
||||
.. _ubuntu-main.yml: https://github.com/Rconybea/xo-ratio/blob/main/.github/workflows/ubuntu-main.yml
|
||||
|
||||
Unit test dependencies:
|
||||
|
||||
* `catch2`_ header-only unit-test framework
|
||||
* `xo-cmake`_ cmake macros
|
||||
* `xo-indentlog`_ logging with call-structure indenting
|
||||
* `xo-randomgen`_ fast random-number generator (xoshiro256ss)
|
||||
|
||||
.. _catch2: https://github.com/catchorg/Catch2
|
||||
.. _xo-cmake: https://github.com/rconybea/xo-cmake
|
||||
.. _xo-indentlog: https://github.com/rconybea/indentlog
|
||||
.. _xo-randomgen: https://github.com/rconybea/randomgen
|
||||
|
||||
To build documentation, will also need:
|
||||
|
||||
* `doxygen`_ c++ documentation
|
||||
* `graphviz`_ graph drawing software, used by doxygen
|
||||
* `sphinx`_ documentation system, ReST (`.rst`) format
|
||||
* `breathe`_ use `doxygen`-generated ingredients in sphinx `.rst` files
|
||||
* `sphinx_rtd_theme`_ css for sphinx html output
|
||||
|
||||
Can install these
|
||||
|
||||
.. _doxygen: https://www.doxygen.nl
|
||||
.. _graphviz: https://graphviz.org
|
||||
.. _sphinx: https://www.sphinx-doc.org/en/master
|
||||
.. _breathe: https://breathe.readthedocs.io/en/latest
|
||||
.. _sphinx_rtd_theme: https://github.com/readthedocs/sphinx_rtd_theme
|
||||
47
xo-ratio/docs/ratio-class.rst
Normal file
47
xo-ratio/docs/ratio-class.rst
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
.. _ratio-class:
|
||||
|
||||
Ratio
|
||||
=====
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <xo/ratio/ratio.hpp>
|
||||
|
||||
A ``ratio`` represents an exact fraction of two integers.
|
||||
|
||||
.. doxygenclass:: xo::ratio::ratio
|
||||
|
||||
Instance Variables
|
||||
------------------
|
||||
|
||||
.. doxygengroup:: ratio-instance-variables
|
||||
|
||||
Types
|
||||
-----
|
||||
|
||||
.. doxygengroup:: ratio-types
|
||||
|
||||
Constructors
|
||||
------------
|
||||
|
||||
.. doxygengroup:: ratio-ctor
|
||||
|
||||
Static Methods
|
||||
--------------
|
||||
|
||||
.. doxygengroup:: ratio-static-methods
|
||||
|
||||
Access Methods
|
||||
--------------
|
||||
|
||||
.. doxygengroup:: ratio-access
|
||||
|
||||
General Methods
|
||||
---------------
|
||||
|
||||
.. doxygengroup:: ratio-methods
|
||||
|
||||
Conversion
|
||||
----------
|
||||
|
||||
.. doxygengroup:: ratio-conversion
|
||||
17
xo-ratio/docs/ratio-functions.rst
Normal file
17
xo-ratio/docs/ratio-functions.rst
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
.. _ratio_functions:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
Ratio Functions
|
||||
===============
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <xo/ratio/ratio.hpp>
|
||||
|
||||
.. doxygenfunction:: xo::ratio::make_ratio
|
||||
|
||||
.. doxygengroup:: ratio-arithmetic
|
||||
|
||||
.. doxygengroup:: ratio-3way-compare
|
||||
11
xo-ratio/docs/ratio-reference.rst
Normal file
11
xo-ratio/docs/ratio-reference.rst
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
.. _ratio-reference:
|
||||
|
||||
Ratio Reference
|
||||
===============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Ratio Reference
|
||||
|
||||
ratio-class
|
||||
ratio-functions
|
||||
3
xo-ratio/example/CMakeLists.txt
Normal file
3
xo-ratio/example/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# xo-ratio/example/CMakeLists.txt
|
||||
|
||||
add_subdirectory(ex1)
|
||||
12
xo-ratio/example/ex1/CMakeLists.txt
Normal file
12
xo-ratio/example/ex1/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# xo-ratio/example/ex1/CMakeLists.txt
|
||||
|
||||
set(SELF_EXE xo_ratio_ex1)
|
||||
set(SELF_SRCS ex1.cpp)
|
||||
|
||||
if (XO_ENABLE_EXAMPLES)
|
||||
add_executable(${SELF_EXE} ${SELF_SRCS})
|
||||
xo_include_options2(${SELF_EXE})
|
||||
xo_self_dependency(${SELF_EXE} xo_ratio)
|
||||
endif()
|
||||
|
||||
# end CMakeLists.txt
|
||||
287
xo-ratio/example/ex1/ex1.cpp
Normal file
287
xo-ratio/example/ex1/ex1.cpp
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/** @file ex1.cpp **/
|
||||
|
||||
#include "xo/ratio/ratio_iostream.hpp"
|
||||
#include "xo/indentlog/scope.hpp"
|
||||
#include "xo/indentlog/print/hex.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace {
|
||||
using xo::xtag;
|
||||
using xo::hex_view;
|
||||
|
||||
#ifdef NOT_USING
|
||||
template <std::size_t N>
|
||||
xo::flatstring<N>
|
||||
flatstring_from_int(int x)
|
||||
{
|
||||
XO_SCOPE(log, always);
|
||||
|
||||
constexpr size_t buf_z = 20;
|
||||
|
||||
bool negative_flag = (x < 0);
|
||||
std::size_t i = buf_z;
|
||||
char buf[buf_z];
|
||||
std::fill_n(buf, N, '\0');
|
||||
|
||||
if (negative_flag)
|
||||
x = -x;
|
||||
|
||||
buf[--i] = '\0';
|
||||
|
||||
while ((i > 0) && (x != 0)) {
|
||||
buf[--i] = ('0' + x % 10);
|
||||
x = x / 10;
|
||||
}
|
||||
|
||||
if ((i > 0) && negative_flag)
|
||||
buf[--i] = '-';
|
||||
|
||||
char retv[N];
|
||||
std::fill_n(retv, N, '\0');
|
||||
std::copy_n(buf + i, buf_z - i, retv);
|
||||
|
||||
log && log(xtag("i",i), xtag("buf[i..]", hex_view(buf+i, buf+buf_z, true)));
|
||||
|
||||
return retv;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename Ratio, std::size_t N>
|
||||
constexpr xo::flatstring<N>
|
||||
ratio_to_str(Ratio x) noexcept
|
||||
{
|
||||
if (x.is_integer()) {
|
||||
return xo::flatstring<N>::from_int(x.num());
|
||||
} else {
|
||||
constexpr auto num_str = xo::flatstring<N>::from_int(x.num());
|
||||
constexpr auto den_str = xo::flatstring<N>::from_int(x.den());
|
||||
|
||||
constexpr auto tmp = flatstring_concat("(", num_str, "/", den_str, ")");
|
||||
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/* verify can use ratio instance as template argument */
|
||||
using ratio64_t = xo::ratio::ratio<int64_t>;
|
||||
|
||||
template<ratio64_t Ratio>
|
||||
constexpr double my_ratio_approx = Ratio.num_ / static_cast<double>(Ratio.den_);
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
main() {
|
||||
using xo::ratio::make_ratio;
|
||||
using xo::ratio::ratio;
|
||||
using xo::ratio::ratio_concept;
|
||||
using xo::flatstring;
|
||||
using namespace std;
|
||||
|
||||
static_assert(my_ratio_approx<ratio64_t(1, 2)> == 0.5);
|
||||
static_assert(my_ratio_approx<ratio64_t{}> == 0.0);
|
||||
|
||||
constexpr auto r1 = make_ratio(2, 3);
|
||||
cerr << "r1=make_ratio(2,3): " << r1 << endl; // output <ratio 2/3>
|
||||
|
||||
static_assert(r1.num() == 2);
|
||||
static_assert(r1.den() == 3);
|
||||
|
||||
static_assert(r1.compare(r1, r1) == 0);
|
||||
static_assert(xo::ratio::detail::op_aux_type<decltype(r1), decltype(r1)>::compare(r1,r1) == 0);
|
||||
|
||||
static_assert(r1 == r1);
|
||||
static_assert(!(r1 != r1));
|
||||
static_assert(r1 != 0);
|
||||
static_assert(r1 != 1);
|
||||
static_assert(r1 != 2);
|
||||
static_assert(r1 != 3);
|
||||
|
||||
static_assert(r1 >= r1);
|
||||
static_assert(r1 <= r1);
|
||||
|
||||
static_assert(r1 > 0);
|
||||
static_assert(r1 >= 0);
|
||||
static_assert(r1 < 1);
|
||||
static_assert(r1 <= 1);
|
||||
|
||||
constexpr auto r2 = make_ratio(2, 4);
|
||||
cerr << "r2=make_ratio(2,4): " << r2 << endl; // output <ratio 1/2>
|
||||
|
||||
static_assert(r2.num() == 1);
|
||||
static_assert(r2.den() == 2);
|
||||
|
||||
static_assert(r2 == r2);
|
||||
static_assert(r2 != r1);
|
||||
static_assert(!(r2 > r1));
|
||||
static_assert(!(r2 >= r1));
|
||||
static_assert(r2 <= r1);
|
||||
static_assert(r2 < r1);
|
||||
static_assert(r1 > r2);
|
||||
static_assert(r1 >= r2);
|
||||
static_assert(!(r1 < r2));
|
||||
static_assert(!(r1 <= r2));
|
||||
|
||||
constexpr auto r3 = make_ratio(2, 3) - make_ratio(1, 2);
|
||||
cerr << "r3=r1-r2: " << r1 - r2 << endl; // output <ratio 1/6>
|
||||
|
||||
static_assert(r3.num() == 1);
|
||||
static_assert(r3.den() == 6);
|
||||
|
||||
static_assert(r3 == r3);
|
||||
static_assert(r3 != 0);
|
||||
static_assert(r3 != 1);
|
||||
static_assert(r3 < r2);
|
||||
static_assert(r3 <= r2);
|
||||
static_assert(r3 < r1);
|
||||
static_assert(r3 <= r1);
|
||||
|
||||
constexpr auto r4 = r1 + r2;
|
||||
cerr << "r4=r1+r2: " << r1 + r2 << endl; // output <ratio 7/6>
|
||||
|
||||
static_assert(r4.num() == 7);
|
||||
static_assert(r4.den() == 6);
|
||||
static_assert(r4 > 1);
|
||||
static_assert(r4 < 2);
|
||||
|
||||
constexpr auto r5 = r1 + 3;
|
||||
cerr << "r5=r1+3: " << r5 << endl; // output <ratio 11/3>
|
||||
|
||||
static_assert(r5.num() == 11);
|
||||
static_assert(r5.den() == 3);
|
||||
|
||||
constexpr auto r6 = 3 + r1;
|
||||
cerr << "r5=3+r1: " << r6 << endl; // output <ratio 11/3>
|
||||
|
||||
static_assert(r6.num() == 11);
|
||||
static_assert(r6.den() == 3);
|
||||
|
||||
static_assert(r5 == r6);
|
||||
static_assert(r5 > 3);
|
||||
static_assert(r5 < 4);
|
||||
static_assert(r5 > r1);
|
||||
|
||||
constexpr auto r7 = r6 - 3;
|
||||
cerr << "r7=r6-3: " << r7 << endl; // output <ratio 2/3>
|
||||
|
||||
static_assert(r7 == r1);
|
||||
static_assert(r7 >= r1);
|
||||
static_assert(r7 <= r1);
|
||||
|
||||
constexpr auto r8 = 3 - r6;
|
||||
cerr << "r8=3-r6: " << r8 << endl; // output <ratio -2/3>
|
||||
|
||||
static_assert(r8 == r8);
|
||||
static_assert(r8 > -1);
|
||||
static_assert(r8 < 0);
|
||||
static_assert(-1 < r8);
|
||||
static_assert(-1 <= r8);
|
||||
static_assert(0 >= r8);
|
||||
|
||||
constexpr auto r9 = r8 * r8;
|
||||
cerr << "r9=r8*r8: " << r9 << endl; // output <ratio 4/9>
|
||||
|
||||
static_assert(r9 == make_ratio(4, 9));
|
||||
|
||||
constexpr auto r10 = r9 * 9;
|
||||
cerr << "r10=r9*9: " << r10 << endl; // output <ratio 4/1>
|
||||
|
||||
static_assert(r10 == 4);
|
||||
static_assert(r10.convert_to<int>() == 4);
|
||||
|
||||
constexpr auto r11 = r9 * 3;
|
||||
cerr << "r11=r9*3: " << r11 << endl; // output <ratio 4/3>
|
||||
|
||||
static_assert(r11 == make_ratio(4, 3));
|
||||
static_assert(r11.convert_to<int>() == 1);
|
||||
|
||||
constexpr auto r12 = 9 * r9;
|
||||
cerr << "r12=9*r9: " << r12 << endl;
|
||||
|
||||
static_assert(r12 == r10);
|
||||
static_assert(r12 == make_ratio(4, 1));
|
||||
static_assert(r12.convert_to<int>() == 4);
|
||||
|
||||
constexpr auto r13 = 3 * r9;
|
||||
cerr << "r13=3*r9: " << r13 << endl; // output <ratio 4/3>
|
||||
|
||||
static_assert(r13 == make_ratio(4, 3));
|
||||
static_assert(r13 == make_ratio(-4, -3));
|
||||
static_assert(r13 == r11);
|
||||
static_assert(r13.convert_to<int>() == 1);
|
||||
|
||||
constexpr auto r14 = r9 / r9;
|
||||
cerr << "r14=r9/r9: " << r14 << endl; // output <ratio 4/3>
|
||||
|
||||
static_assert(r14 == 1);
|
||||
static_assert(r14.convert_to<int>() == 1);
|
||||
|
||||
constexpr auto r15 = r9 / r8;
|
||||
cerr << "r15=r9/r8: " << r15 << endl; // (4/9) / (-2/3) = (4/9) * (3/-2) = 12/-18 = -2/3
|
||||
|
||||
static_assert(r15 == make_ratio(-2, 3));
|
||||
static_assert(r15 == make_ratio(2, -3));
|
||||
|
||||
constexpr auto r16 = r9 / 2;
|
||||
cerr << "r16=r9/2: " << r16 << endl;
|
||||
|
||||
static_assert(r16 == ratio(2, 9));
|
||||
static_assert(!r16.is_integer());
|
||||
|
||||
constexpr auto r17 = 2 / r9;
|
||||
cerr << "r17=2/r9: " << r17 << endl; // 9/2
|
||||
|
||||
static_assert(r17 == ratio(9, 2));
|
||||
static_assert(!r17.is_integer());
|
||||
|
||||
constexpr auto s17_num_str = flatstring<20>::from_int(r17.num());
|
||||
static_assert(s17_num_str == flatstring("9"));
|
||||
constexpr auto s17_den_str = flatstring<20>::from_int(r17.den());
|
||||
static_assert(s17_den_str == flatstring("2"));
|
||||
constexpr auto s17_str = flatstring_concat(flatstring_concat(flatstring("("),
|
||||
s17_num_str),
|
||||
flatstring_concat(flatstring("/"),
|
||||
s17_den_str),
|
||||
flatstring(")"));
|
||||
cerr << "s17_str=" << s17_str << endl;
|
||||
|
||||
//constexpr auto s17 = ratio_to_str<decltype(r17), 20>(r17);
|
||||
|
||||
constexpr auto r18 = r12 / r8;
|
||||
cerr << "r18=r12/r8: " << r12/r8 << endl;
|
||||
|
||||
static_assert(r18.is_integer());
|
||||
|
||||
constexpr auto r19 = r18.power(2);
|
||||
cerr << "r19=r18^2: " << r19 << endl;
|
||||
|
||||
static_assert(r19.is_integer());
|
||||
static_assert(r19 == ratio(36, 1));
|
||||
|
||||
constexpr auto r20 = r17.power(-3);
|
||||
cerr << "r20=r17^-3: " << r20 << endl;
|
||||
|
||||
static_assert(!r20.is_integer());
|
||||
static_assert(r20 == ratio(8, 729));
|
||||
|
||||
//cerr << flatstring_from_int<10>(1) << endl;
|
||||
|
||||
constexpr auto s20 = flatstring<10>::from_int(-123);
|
||||
cerr << "s20=" << s20 << endl;
|
||||
|
||||
static_assert(s20.size() > 0);
|
||||
static_assert(s20 == flatstring("-123"));
|
||||
|
||||
|
||||
/* verify constexpr working */
|
||||
static_assert(ratio<int>(2,3).num() == 2);
|
||||
static_assert(ratio<int>(2,3).den() == 3);
|
||||
static_assert(make_ratio(-1,2).num() == -1);
|
||||
static_assert(make_ratio(-1,2).den() == 2);
|
||||
static_assert(make_ratio(-2,4).num() == -1);
|
||||
static_assert(make_ratio(-2,4).den() == 2);
|
||||
static_assert(make_ratio(1,-2).num() == -1);
|
||||
static_assert(make_ratio(1,-2).den() == 2);
|
||||
static_assert(make_ratio(-4,-6).num() == 2);
|
||||
static_assert(make_ratio(-4,-6).den() == 3);
|
||||
}
|
||||
39
xo-ratio/include/xo/ratio/numeric_concept.hpp
Normal file
39
xo-ratio/include/xo/ratio/numeric_concept.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/** @file numeric_concept.hpp **/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
|
||||
namespace xo {
|
||||
namespace ratio {
|
||||
/** @concept numeric_concept
|
||||
* @brief Concept for values that participate in arithmetic operations (+,-,*,/) and comparisons
|
||||
*
|
||||
* Intended to include at least:
|
||||
* - built-in integral and floating-point types
|
||||
* - big_int<N> from ctbignum
|
||||
* - boost::rational<U>
|
||||
* - std::complex<U>
|
||||
* - xo::unit::quantity<U,R>
|
||||
*
|
||||
* Accepting complex numbers --> we don't require T to be totally ordered,
|
||||
* and don't require (<,<=,>=,>) operators.
|
||||
*
|
||||
* Intend numeric_concept to apply to types T suitable for
|
||||
* xo::ratio::ratio<T>
|
||||
**/
|
||||
template <typename T, typename U = T>
|
||||
concept numeric_concept = requires(T x, U y)
|
||||
{
|
||||
{ -x };
|
||||
{ x - y };
|
||||
{ x + y };
|
||||
{ x * y };
|
||||
{ x / y };
|
||||
{ x == y };
|
||||
{ x != y };
|
||||
};
|
||||
} /*namespace ratio*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end numeric_concept.hpp */
|
||||
674
xo-ratio/include/xo/ratio/ratio.hpp
Normal file
674
xo-ratio/include/xo/ratio/ratio.hpp
Normal file
|
|
@ -0,0 +1,674 @@
|
|||
/** @file ratio.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ratio_concept.hpp"
|
||||
#include "xo/flatstring/flatstring.hpp"
|
||||
#include <numeric>
|
||||
#include <compare>
|
||||
//#include <type_traits>
|
||||
|
||||
namespace xo {
|
||||
namespace ratio {
|
||||
namespace detail {
|
||||
/** @brief converts ratio to lowest terms when feasible
|
||||
*
|
||||
* Falls back to identity function for non-totally-ordered Ratio::component_type
|
||||
*/
|
||||
template <typename Ratio, bool EnabledFlag = std::totally_ordered<typename Ratio::component_type>>
|
||||
struct reducer_type;
|
||||
|
||||
/** @brief promote value to ratio type **/
|
||||
template <typename Ratio, typename FromType, bool FromRatioFlag = ratio_concept<FromType>>
|
||||
struct promoter_type;
|
||||
|
||||
/** @brief convert value to different numeric (or ratio) representation **/
|
||||
template <typename Ratio, typename ToType, bool ToRatioFlag = ratio_concept<ToType>>
|
||||
struct converter_type;
|
||||
}
|
||||
|
||||
/** @class ratio
|
||||
* @brief represent a ratio of two Int values.
|
||||
**/
|
||||
template <typename Int>
|
||||
requires std::totally_ordered<Int>
|
||||
struct ratio
|
||||
{
|
||||
public:
|
||||
/** @defgroup ratio-types **/
|
||||
///@{
|
||||
/** @brief representation for (numerator, denominator) **/
|
||||
using component_type = Int;
|
||||
///@}
|
||||
|
||||
public:
|
||||
/** @defgroup ratio-ctor **/
|
||||
///@{
|
||||
/** @brief construct ratio 0/1 **/
|
||||
constexpr ratio() = default;
|
||||
/** @brief construct ratio with numerator @p n and denominator @p d.
|
||||
*
|
||||
* Ratio need not be normalized
|
||||
**/
|
||||
constexpr ratio(Int n, Int d = 1) : num_{n}, den_{d} {}
|
||||
///@}
|
||||
|
||||
/** @defgroup ratio-static-methods **/
|
||||
///@{
|
||||
/** @brief ratio in lowest commono terms
|
||||
*
|
||||
**/
|
||||
static constexpr ratio reduce(Int n, Int d) {
|
||||
return ratio(n, d).normalize();
|
||||
}
|
||||
|
||||
/** @brief add ratios @p x and @p y
|
||||
*
|
||||
* @post result ratio is normalized
|
||||
**/
|
||||
static constexpr ratio add(const ratio & x,
|
||||
const ratio & y) {
|
||||
/* (a/b) + (c/d)
|
||||
* = a.d / (b.d) + b.c / (b.d)
|
||||
* = (a.d + b.c) / (b.d)
|
||||
*/
|
||||
|
||||
auto a = x.num();
|
||||
auto b = x.den();
|
||||
auto c = y.num();
|
||||
auto d = y.den();
|
||||
|
||||
auto num = a*d + b*c;
|
||||
auto den = b*d;
|
||||
|
||||
return ratio(num, den).maybe_reduce();
|
||||
}
|
||||
|
||||
/** @brief subtract ratio @p y from ratio @p x
|
||||
*
|
||||
* @post result ratio is normalized
|
||||
**/
|
||||
static constexpr ratio subtract(const ratio & x,
|
||||
const ratio & y) {
|
||||
return add(x, y.negate());
|
||||
}
|
||||
|
||||
/** @brief multiply ratios @p x and @p y
|
||||
*
|
||||
* @post result ratio is normalized
|
||||
**/
|
||||
static constexpr ratio multiply(const ratio & x,
|
||||
const ratio & y) {
|
||||
/* (a/b) * (c/d) = a.c / b.d */
|
||||
|
||||
/* if x,y normalized,
|
||||
* opportunity to cancel common factor between (a, d) or (c, b)
|
||||
*
|
||||
* want to do this before multiplying to avoid overflow involving intermediate terms
|
||||
*/
|
||||
|
||||
auto a1 = x.num();
|
||||
auto b1 = x.den();
|
||||
auto c1 = y.num();
|
||||
auto d1 = y.den();
|
||||
|
||||
auto ad_gcf = std::gcd(a1, d1);
|
||||
auto bc_gcf = std::gcd(b1, c1);
|
||||
|
||||
auto a = a1 / ad_gcf;
|
||||
auto b = b1 / bc_gcf;
|
||||
auto c = c1 / bc_gcf;
|
||||
auto d = d1 / ad_gcf;
|
||||
|
||||
auto num = a*c;
|
||||
auto den = b*d;
|
||||
|
||||
return ratio(num, den).maybe_reduce();
|
||||
}
|
||||
|
||||
/** @brief divide ratio @p y into ratio @p x
|
||||
*
|
||||
* @post result ratio is normalized
|
||||
**/
|
||||
static constexpr ratio divide(const ratio & x,
|
||||
const ratio & y)
|
||||
{
|
||||
return multiply(x, y.reciprocal());
|
||||
}
|
||||
|
||||
/** @brief 3-way compare two ratios **/
|
||||
static constexpr auto compare(ratio x, ratio y) {
|
||||
/* ensure minus signs in numerators only */
|
||||
if (x.den() < 0)
|
||||
return compare_aux(ratio(-x.num(), -x.den()), y);
|
||||
if (y.den() < 0)
|
||||
return compare_aux(x, ratio(-y.num(), -y.den()));
|
||||
|
||||
return compare_aux(x, y);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** @defgroup ratio-access **/
|
||||
///@{
|
||||
/** @brief fetch ratio's numerator **/
|
||||
constexpr Int num() const noexcept { return num_; }
|
||||
/** @brief fetch ratio's denominator **/
|
||||
constexpr Int den() const noexcept { return den_; }
|
||||
|
||||
/** @brief true if and only if ratio is equal to zero **/
|
||||
constexpr bool is_zero() const noexcept { return (num_ == 0) && (den_ != 0); }
|
||||
|
||||
/** @brief true if and only if ratio is equal to one **/
|
||||
constexpr bool is_unity() const noexcept { return (num_ != 0) && (num_ == den_); }
|
||||
|
||||
/** @brief true if and only if ratio represents an integer
|
||||
*
|
||||
* (denominator is +/- 1)
|
||||
**/
|
||||
constexpr bool is_integer() const noexcept { return den_ == 1 || den_ == -1; }
|
||||
///@}
|
||||
|
||||
/** @defgroup ratio-methods **/
|
||||
///@{
|
||||
/** @brief r.negate() is the exact ratio representing @c -r **/
|
||||
constexpr ratio negate() const { return ratio(-num_, den_); }
|
||||
/** @brief r.reciprocal() is the eact ratio representing @c 1/r **/
|
||||
constexpr ratio reciprocal() const { return ratio(den_, num_); }
|
||||
|
||||
/** @brief r.floor() is the largest integer x : x <= r **/
|
||||
constexpr Int floor() const { return (num_ / den_); }
|
||||
|
||||
/** @brief r.ceil() is the smallest integer x : r < x. **/
|
||||
constexpr Int ceil() const { return floor() + 1; }
|
||||
|
||||
/** @brief reduce to lowest terms
|
||||
*
|
||||
* @pre @c Int type must be totally ordered
|
||||
**/
|
||||
constexpr ratio normalize() const requires std::totally_ordered<Int> {
|
||||
if (den_ < 0)
|
||||
return ratio(-num_, -den_).normalize();
|
||||
|
||||
auto factor = std::gcd(num_, den_);
|
||||
|
||||
return ratio(num_ / factor,
|
||||
den_ / factor);
|
||||
}
|
||||
|
||||
/** @brief reduce to lowest terms, if Int representation admits
|
||||
*
|
||||
* Otherwise fallback to identity function
|
||||
**/
|
||||
constexpr ratio maybe_reduce() const {
|
||||
return detail::reducer_type<ratio>::attempt_reduce(*this);
|
||||
}
|
||||
|
||||
/** @brief return fractional part of this ratio
|
||||
*
|
||||
* @pre @c Int type must be totally ordered
|
||||
**/
|
||||
constexpr ratio frac() const requires std::totally_ordered<Int> {
|
||||
return ratio::subtract(*this, ratio(this->floor(), 1));
|
||||
}
|
||||
|
||||
/** @brief compute integer exponent @p p of this ratio
|
||||
*
|
||||
* @note time complexity is O(log p)
|
||||
**/
|
||||
constexpr ratio power(int p) const {
|
||||
|
||||
constexpr ratio retval = ratio(1, 1);
|
||||
|
||||
if (p == 0)
|
||||
return ratio(1, 1);
|
||||
|
||||
if (p < 0)
|
||||
return this->reciprocal().power(-p);
|
||||
|
||||
/* inv: x^p = aj.xj^pj */
|
||||
ratio aj = ratio(1, 1);
|
||||
ratio xj = *this;
|
||||
int pj = p;
|
||||
|
||||
while (pj > 0) {
|
||||
if (pj % 2 == 0) {
|
||||
/* a.x^(2q) = a.(x^2)^q */
|
||||
xj = xj * xj;
|
||||
pj = pj / 2;
|
||||
} else {
|
||||
/* a.x^(2q+1) = (a.x).x^(2q) */
|
||||
aj = aj * xj;
|
||||
pj = (pj - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* pj = 0, so: x^p = aj.xj^pj = aj.xj^0 = aj */
|
||||
return aj;
|
||||
}
|
||||
|
||||
/** @brief negate operator **/
|
||||
constexpr ratio operator-() const { return ratio(-num_, den_); }
|
||||
///@}
|
||||
|
||||
/** @defgroup ratio-conversion **/
|
||||
///@{
|
||||
/** @brief convert to non-ratio representation
|
||||
*
|
||||
* For example: to int or double
|
||||
**/
|
||||
template <typename Repr>
|
||||
constexpr Repr convert_to() const noexcept {
|
||||
return detail::converter_type<ratio, Repr>::convert(*this);
|
||||
}
|
||||
|
||||
/** @brief convert to short human-friendly flatstring representation
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* ratio(7,1).to_str<5>(); // "7"
|
||||
* ratio(1,7).to_str<5>(); // "(1/7)"
|
||||
* ratio(-1,7).to_str<10>(); // "(-1/7)"
|
||||
* ratio(-1,7).to_str<5>(); // "(-1/"
|
||||
* @endcode
|
||||
**/
|
||||
template <std::size_t N>
|
||||
constexpr flatstring<N> to_str() const noexcept {
|
||||
if (this->is_integer()) {
|
||||
return flatstring<N>::from_int(num_);
|
||||
} else {
|
||||
auto num_str = flatstring<N>::from_int(num_);
|
||||
auto den_str = flatstring<N>::from_int(den_);
|
||||
|
||||
/* tmp capacity will be about 2N+3 */
|
||||
auto tmp = flatstring_concat(flatstring("("),
|
||||
num_str,
|
||||
flatstring("/"),
|
||||
den_str,
|
||||
flatstring(")"));
|
||||
|
||||
flatstring<N> retval;
|
||||
retval.assign(tmp);
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief convert to representation using different integer types **/
|
||||
template <typename Ratio2>
|
||||
constexpr operator Ratio2 () const noexcept requires ratio_concept<Ratio2> {
|
||||
return Ratio2(num_, den_);
|
||||
}
|
||||
///@}
|
||||
|
||||
private:
|
||||
/** @brief 3-way compare auxiliary function.
|
||||
*
|
||||
* @pre @p x, @p y have non-negative denominator
|
||||
**/
|
||||
static constexpr auto compare_aux(ratio x, ratio y) noexcept {
|
||||
/* control here: b>=0, d>=0 */
|
||||
|
||||
/* (a/b) <=> (c/d)
|
||||
* (a.d/b) <=> c no sign change, since d >= 0
|
||||
* (a.d) <=> (b.c) no sign change, since b >= 0
|
||||
*/
|
||||
auto a = x.num();
|
||||
auto b = x.den();
|
||||
auto c = y.num();
|
||||
auto d = y.den();
|
||||
|
||||
auto lhs = a*d;
|
||||
auto rhs = b*c;
|
||||
|
||||
return lhs <=> rhs;
|
||||
}
|
||||
|
||||
public: /* need public members so that a ratio instance can be a non-type template parameter (a structural type) */
|
||||
|
||||
/** @defgroup ratio-instance-variables **/
|
||||
///@{
|
||||
/** @brief numerator **/
|
||||
Int num_ = 0;
|
||||
/** @brief denominator **/
|
||||
Int den_ = 1;
|
||||
///@}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename Ratio, bool EnabledFlag>
|
||||
struct reducer_type {};
|
||||
|
||||
template <typename Ratio>
|
||||
struct reducer_type<Ratio, true /*EnabledFlag*/> {
|
||||
static constexpr Ratio attempt_reduce(Ratio x) { return x.normalize(); }
|
||||
};
|
||||
|
||||
template <typename Ratio>
|
||||
struct reducer_type<Ratio, false /*!EnabledFlag*/> {
|
||||
static constexpr Ratio attempt_reduce(Ratio x) { return x; }
|
||||
};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename Ratio, typename FromType, bool FromRatioFlag>
|
||||
struct promoter_type;
|
||||
|
||||
template <typename Ratio, typename FromType>
|
||||
struct promoter_type<Ratio, FromType, true /*FromRatioFlag*/> {
|
||||
/* to 'promote' a ratio, rely on its conversion operator */
|
||||
static constexpr Ratio promote(FromType x) { return x; }
|
||||
};
|
||||
|
||||
template <typename Ratio, typename FromType>
|
||||
struct promoter_type<Ratio, FromType, false /*!FromRatioFlag*/> {
|
||||
/* to 'promote' a non-ratio, use denominator=1 */
|
||||
static constexpr Ratio promote(FromType x) { return Ratio(x, 1); }
|
||||
};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename Ratio, typename ToType, bool ToRatioFlag>
|
||||
struct converter_type;
|
||||
|
||||
template <typename Ratio, typename ToType>
|
||||
struct converter_type<Ratio, ToType, true /*ToRatioFlag*/> {
|
||||
/* to convert to a ratio, can just use built-in conversion */
|
||||
static constexpr ToType convert(Ratio x) { return ToType(x.num(), x.den()); }
|
||||
};
|
||||
|
||||
template <typename Ratio, typename ToType>
|
||||
struct converter_type<Ratio, ToType, false /*!ToRatioFlag*/> {
|
||||
/* to convert to non-ratio, do division */
|
||||
static constexpr ToType convert(Ratio x) { return x.num() / static_cast<ToType>(x.den()); }
|
||||
};
|
||||
}
|
||||
|
||||
/** @brief create a ratio in lowest terms from two integers **/
|
||||
template <typename Int1, typename Int2>
|
||||
constexpr auto
|
||||
make_ratio (Int1 n, Int2 d = 1) -> ratio<std::common_type_t<Int1, Int2>>
|
||||
{
|
||||
return ratio<std::common_type_t<Int1, Int2>>(n, d).maybe_reduce();
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
/** @brief auxiliary function for binary ratio operations
|
||||
*
|
||||
* Support binary ratio operations on combinations:
|
||||
* - (ratio<T>, ratio<U>)
|
||||
* - (ratio<T>, U) // where U is not a ratio
|
||||
* - (T, ratio(U)) // where T is not a ratio
|
||||
*
|
||||
* Goals:
|
||||
*
|
||||
* 1. Support expressions like
|
||||
*
|
||||
* @code
|
||||
* auto x = 1 + make_ratio(2,3);
|
||||
* @endcode
|
||||
*
|
||||
* 2. promote to wider types as needed
|
||||
*
|
||||
* @code
|
||||
* auto x = make_ratio(2,3) + make_ratio(1ul,2ul);
|
||||
* static_assert(std::same_as<x::component_type, unsigned long>);
|
||||
* @endcode
|
||||
*
|
||||
* 3. avoid interfering with other templates that may overload operator+
|
||||
*
|
||||
* @pre at least one of (Left,Right) must be known to be a ratio
|
||||
**/
|
||||
template <typename Left,
|
||||
typename Right,
|
||||
bool LeftIsRatio = ratio_concept<Left>,
|
||||
bool RightIsRatio = ratio_concept<Right>>
|
||||
struct op_aux_type;
|
||||
|
||||
/** @brief specialization for two ratio types **/
|
||||
template <typename LeftRatio,
|
||||
typename RightRatio>
|
||||
requires (ratio_concept<LeftRatio> && ratio_concept<RightRatio>)
|
||||
struct op_aux_type<LeftRatio, RightRatio, true /*LeftIsRatio*/, true /*RightIsRatio*/> {
|
||||
using component_type = std::common_type_t<typename LeftRatio::component_type,
|
||||
typename RightRatio::component_type>;
|
||||
|
||||
using ratio_type = ratio<component_type>;
|
||||
|
||||
static constexpr ratio_type add (const LeftRatio & x,
|
||||
const RightRatio & y)
|
||||
{
|
||||
return ratio_type::add(x, y);
|
||||
}
|
||||
|
||||
static constexpr ratio_type subtract (const LeftRatio & x,
|
||||
const RightRatio & y)
|
||||
{
|
||||
return ratio_type::subtract(x, y);
|
||||
}
|
||||
|
||||
static constexpr ratio_type multiply (const LeftRatio & x,
|
||||
const RightRatio & y)
|
||||
{
|
||||
return ratio_type::multiply(x, y);
|
||||
}
|
||||
|
||||
static constexpr ratio_type divide (const LeftRatio & x,
|
||||
const RightRatio & y)
|
||||
{
|
||||
return ratio_type::divide(x, y);
|
||||
}
|
||||
|
||||
static constexpr auto compare (const LeftRatio & x,
|
||||
const RightRatio & y)
|
||||
{
|
||||
return ratio_type::compare(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
/** @brief specialization for left-hand ratio and right-hand integer value **/
|
||||
template <typename LeftRatio,
|
||||
typename Right>
|
||||
requires (ratio_concept<LeftRatio> && !ratio_concept<Right>)
|
||||
struct op_aux_type<LeftRatio, Right, true /*LeftIsRatio*/, false /*RightIsRatio*/> {
|
||||
using component_type = std::common_type_t<typename LeftRatio::component_type, Right>;
|
||||
|
||||
using ratio_type = ratio<component_type>;
|
||||
|
||||
static constexpr ratio_type add (const LeftRatio & x,
|
||||
const Right & y)
|
||||
{
|
||||
/* reminder: adding an integer can't introduce reduced terms */
|
||||
return ratio_type(x.num() + x.den() * y, x.den());
|
||||
}
|
||||
|
||||
static constexpr ratio_type subtract (const LeftRatio & x,
|
||||
const Right & y)
|
||||
{
|
||||
/* reminder: subtracting an integer can't introduce reduced terms */
|
||||
return ratio_type(x.num() - x.den() * y, x.den());
|
||||
}
|
||||
|
||||
static constexpr ratio_type multiply (const LeftRatio & x,
|
||||
const Right & yp)
|
||||
{
|
||||
auto gcf = std::gcd(x.den(), yp);
|
||||
|
||||
auto a = x.num();
|
||||
auto b = x.den() / gcf;
|
||||
auto y = yp / gcf;
|
||||
|
||||
return ratio_type(a*y, b);
|
||||
}
|
||||
|
||||
static constexpr ratio_type divide (const LeftRatio & x,
|
||||
const Right & yp)
|
||||
{
|
||||
auto gcf = std::gcd(x.num(), yp);
|
||||
|
||||
auto a = x.num() / gcf;
|
||||
auto b = x.den();
|
||||
auto y = yp / gcf;
|
||||
|
||||
return ratio_type(a*y, b);
|
||||
}
|
||||
|
||||
static constexpr auto compare (const LeftRatio & x,
|
||||
const Right & y)
|
||||
{
|
||||
/* note: in c++26 std::signof is constexpr, usable here */
|
||||
if (x.den() >= 0)
|
||||
return compare_aux(x, y);
|
||||
else
|
||||
return compare_aux(LeftRatio(-x.num(), -x.den()), y);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr auto compare_aux (const LeftRatio & x, const Right & y) {
|
||||
return (x.num() <=> x.den() * y);
|
||||
}
|
||||
};
|
||||
|
||||
/** @brief specialization for left-hand integer value and right-hand ratio **/
|
||||
template <typename Left,
|
||||
typename RightRatio>
|
||||
requires (!ratio_concept<Left> && ratio_concept<RightRatio>)
|
||||
struct op_aux_type<Left, RightRatio, false /*LeftIsRatio*/, true /*RightIsRatio*/> {
|
||||
using component_type = std::common_type_t<Left, typename RightRatio::component_type>;
|
||||
|
||||
using ratio_type = ratio<component_type>;
|
||||
|
||||
static constexpr ratio_type add(const Left & x,
|
||||
const RightRatio & y)
|
||||
{
|
||||
/* reminder: adding an integer can't introduce reduced terms */
|
||||
return ratio_type(x * y.den() + y.num(), y.den());
|
||||
}
|
||||
|
||||
static constexpr ratio_type subtract(const Left & x,
|
||||
const RightRatio & y)
|
||||
{
|
||||
/* reminder: subtracting an integer can't introduce reduced terms */
|
||||
return ratio_type(x * y.den() - y.num(), y.den());
|
||||
}
|
||||
|
||||
static constexpr ratio_type multiply (const Left & xp,
|
||||
const RightRatio & y)
|
||||
{
|
||||
auto gcf = std::gcd(xp, y.den());
|
||||
|
||||
auto x = xp / gcf;
|
||||
auto c = y.num();
|
||||
auto d = y.den() / gcf;
|
||||
|
||||
return ratio_type(x*c, d);
|
||||
}
|
||||
|
||||
static constexpr ratio_type divide (const Left & x,
|
||||
const RightRatio & y)
|
||||
{
|
||||
return multiply(x, y.reciprocal());
|
||||
}
|
||||
|
||||
static constexpr auto compare(const Left & x,
|
||||
const RightRatio & y)
|
||||
{
|
||||
if (y.den() >= 0)
|
||||
return compare_aux(x, y);
|
||||
else
|
||||
return compare_aux(x, RightRatio(-y.num(), -y.den()));
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr auto compare_aux (const Left & x,
|
||||
const RightRatio & y)
|
||||
{
|
||||
return (x * y.den() <=> y.num());
|
||||
};
|
||||
};
|
||||
} /*namespace detail*/
|
||||
|
||||
/** @defgroup ratio-arithmetic **/
|
||||
///@{
|
||||
/** @brief add two ratios.
|
||||
*
|
||||
* One argument may be a non-ratio type if it can be promoted to a ratio
|
||||
**/
|
||||
template <typename Ratio1, typename Ratio2>
|
||||
inline constexpr auto
|
||||
operator+ (const Ratio1 & x, const Ratio2 & y)
|
||||
requires (ratio_concept<Ratio1> || ratio_concept<Ratio2>)
|
||||
{
|
||||
return detail::op_aux_type<Ratio1, Ratio2>::add(x, y);
|
||||
}
|
||||
|
||||
/** @brief subtract two ratios.
|
||||
*
|
||||
* One argument may be a non-ratio type if it can be promoted to a ratio
|
||||
**/
|
||||
template <typename Ratio1, typename Ratio2>
|
||||
inline constexpr auto
|
||||
operator- (const Ratio1 & x, const Ratio2 & y)
|
||||
requires (ratio_concept<Ratio1> || ratio_concept<Ratio2>)
|
||||
{
|
||||
return detail::op_aux_type<Ratio1, Ratio2>::subtract(x, y);
|
||||
}
|
||||
|
||||
/** @brief multiply two ratios
|
||||
*
|
||||
* One argument may be a non-ratio type if it can be promoted to a ratio
|
||||
**/
|
||||
template <typename Ratio1, typename Ratio2>
|
||||
inline constexpr auto
|
||||
operator* (const Ratio1 & x, const Ratio2 & y)
|
||||
requires (ratio_concept<Ratio1> || ratio_concept<Ratio2>)
|
||||
{
|
||||
return detail::op_aux_type<Ratio1, Ratio2>::multiply(x, y);
|
||||
}
|
||||
|
||||
/** @brief divide two ratios
|
||||
*
|
||||
* One argument may be a non-ratio type if it can be promoted to a ratio
|
||||
**/
|
||||
template <typename Ratio1, typename Ratio2>
|
||||
inline constexpr auto
|
||||
operator/ (const Ratio1 & x, const Ratio2 & y)
|
||||
requires (ratio_concept<Ratio1> || ratio_concept<Ratio2>)
|
||||
{
|
||||
return detail::op_aux_type<Ratio1, Ratio2>::divide(x, y);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** @defgroup ratio-3way-compare 3way comparison **/
|
||||
///@{
|
||||
/** @brief compare two ratios for equality
|
||||
*
|
||||
* One argument may be a non-ratio type if it can be promoted to a ratio
|
||||
**/
|
||||
template <typename Ratio1, typename Ratio2>
|
||||
inline constexpr bool
|
||||
operator== (const Ratio1 & x, const Ratio2 & y)
|
||||
requires (ratio_concept<Ratio1> || ratio_concept<Ratio2>)
|
||||
{
|
||||
return (detail::op_aux_type<Ratio1, Ratio2>::compare(x, y) == 0);
|
||||
}
|
||||
|
||||
/** @brief compare two ratios
|
||||
*
|
||||
* One argument may be a non-ratio type if it can be promoted to a ratio
|
||||
**/
|
||||
template <typename Ratio1, typename Ratio2>
|
||||
inline constexpr auto
|
||||
operator<=> (const Ratio1 & x, const Ratio2 & y)
|
||||
requires (ratio_concept<Ratio1> || ratio_concept<Ratio2>)
|
||||
{
|
||||
return detail::op_aux_type<Ratio1, Ratio2>::compare(x, y);
|
||||
}
|
||||
///@}
|
||||
|
||||
} /*namespace ratio*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end ratio.hpp **/
|
||||
29
xo-ratio/include/xo/ratio/ratio_concept.hpp
Normal file
29
xo-ratio/include/xo/ratio/ratio_concept.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/** @file ratio_concept.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "numeric_concept.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace ratio {
|
||||
/* also expect:
|
||||
* Ratio::num_type / Ratio::den_type rounds towards -inf
|
||||
*/
|
||||
template <typename Ratio>
|
||||
concept ratio_concept = requires(Ratio ratio)
|
||||
{
|
||||
typename Ratio::component_type;
|
||||
typename Ratio::component_type;
|
||||
|
||||
{ ratio.num() } -> std::same_as<typename Ratio::component_type>;
|
||||
{ ratio.den() } -> std::same_as<typename Ratio::component_type>;
|
||||
} && numeric_concept<typename Ratio::component_type>;
|
||||
|
||||
} /*namespace ratio*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/** end ratio_concept.hpp **/
|
||||
42
xo-ratio/include/xo/ratio/ratio_iostream.hpp
Normal file
42
xo-ratio/include/xo/ratio/ratio_iostream.hpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/** @file ratio_iostream.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ratio.hpp"
|
||||
#include <ostream>
|
||||
|
||||
namespace xo {
|
||||
namespace ratio {
|
||||
/** @brief print ratio x on stream os.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* print_ratio(std::cerr, make_ratio(1,2); // outputs "<ratio 1/2>"
|
||||
* @endcode
|
||||
**/
|
||||
template <typename Int>
|
||||
void
|
||||
print_ratio (std::ostream & os, const ratio<Int> & x) {
|
||||
os << "<ratio " << x.num() << "/" << x.den() << ">";
|
||||
}
|
||||
|
||||
/** @brief print ratio x on stream os.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* std::cout << make_ratio(2,3); // outputs "<ratio 2/3>"
|
||||
* @endcode
|
||||
**/
|
||||
template <typename Int>
|
||||
inline std::ostream &
|
||||
operator<< (std::ostream & os, const ratio<Int> & x) {
|
||||
print_ratio(os, x);
|
||||
return os;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** end ratio_iostream.hpp **/
|
||||
25
xo-ratio/include/xo/ratio/ratio_reflect.hpp
Normal file
25
xo-ratio/include/xo/ratio/ratio_reflect.hpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/** @file ratio_reflect.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xo/reflectutil/reflect_struct_info.hpp"
|
||||
#include "ratio.hpp"
|
||||
//#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace reflect {
|
||||
template <typename Int>
|
||||
REFLECT_BASE_STRUCT_INFO_TBODY(xo::ratio::ratio<Int>, 2);
|
||||
|
||||
template <typename Int>
|
||||
REFLECT_STRUCT_MEMBER_INFO_TBODY(xo::ratio::ratio<Int>, 0, num);
|
||||
|
||||
template <typename Int>
|
||||
REFLECT_STRUCT_MEMBER_INFO_TBODY(xo::ratio::ratio<Int>, 1, den);
|
||||
}
|
||||
}
|
||||
|
||||
/** end ratio_reflect.hpp **/
|
||||
24
xo-ratio/utest/CMakeLists.txt
Normal file
24
xo-ratio/utest/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# xo-ratio/utest/CMakeLists.txt
|
||||
|
||||
set(SELF_EXE utest.ratio)
|
||||
set(SELF_SRCS
|
||||
ratio_utest_main.cpp
|
||||
ratio_reflect.test.cpp
|
||||
ratio.test.cpp)
|
||||
|
||||
if (ENABLE_TESTING)
|
||||
xo_add_utest_executable(${SELF_EXE} ${SELF_SRCS})
|
||||
|
||||
xo_utest_coverage_config2()
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# dependencies..
|
||||
|
||||
xo_self_headeronly_dependency(${SELF_EXE} xo_ratio)
|
||||
xo_dependency(${SELF_EXE} reflect)
|
||||
xo_dependency(${SELF_EXE} randomgen)
|
||||
xo_dependency(${SELF_EXE} indentlog)
|
||||
xo_external_target_dependency(${SELF_EXE} Catch2 Catch2::Catch2)
|
||||
endif()
|
||||
|
||||
# end CMakeLists.txt
|
||||
241
xo-ratio/utest/ratio.test.cpp
Normal file
241
xo-ratio/utest/ratio.test.cpp
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
/** @file ratio.utest.cpp **/
|
||||
|
||||
#include "xo/ratio/ratio.hpp"
|
||||
#include "xo/ratio/ratio_iostream.hpp"
|
||||
#include "xo/randomgen/random_seed.hpp"
|
||||
#include "xo/randomgen/xoshiro256.hpp"
|
||||
#include "xo/indentlog/scope.hpp"
|
||||
#include "xo/indentlog/print/vector.hpp"
|
||||
#include "xo/indentlog/print/array.hpp"
|
||||
#include "xo/indentlog/print/tag.hpp"
|
||||
//#include "xo/indentlog/print/hex.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
|
||||
namespace xo {
|
||||
|
||||
using std::exponential_distribution;
|
||||
using std::bernoulli_distribution;
|
||||
|
||||
namespace ut {
|
||||
template <typename Int>
|
||||
struct ratio_distribution {
|
||||
ratio_distribution(double sign_prob, double int_lambda)
|
||||
: sign_dist_{sign_prob}, int_dist_{int_lambda} {}
|
||||
|
||||
template <typename Rng>
|
||||
xo::ratio::ratio<Int>
|
||||
random_ratio(Rng & rng) {
|
||||
Int num_sign = sign_dist_(rng) ? -1 : +1;
|
||||
Int num = num_sign * (1 + int_dist_(rng));
|
||||
Int den_sign = sign_dist_(rng) ? -1 : +1;
|
||||
Int den = den_sign * (1 + int_dist_(rng));
|
||||
|
||||
return xo::ratio::ratio(num, den).normalize();
|
||||
}
|
||||
|
||||
template <typename Rng>
|
||||
xo::ratio::ratio<Int> operator()(Rng & rng) {
|
||||
return random_ratio(rng);
|
||||
}
|
||||
|
||||
/* generate negative numbers some of the time */
|
||||
bernoulli_distribution sign_dist_;
|
||||
/* create ratios involving integers, but don't need integers to be too large */
|
||||
exponential_distribution<double> int_dist_;
|
||||
};
|
||||
|
||||
template <typename Rng>
|
||||
void
|
||||
ratio_tests(Rng & rng)
|
||||
{
|
||||
constexpr bool debug_flag = false;
|
||||
|
||||
std::size_t n_ratio = 25;
|
||||
std::size_t n_experiment = n_ratio * n_ratio / 4;
|
||||
/* want to avoid integer overflow when exponentiating */
|
||||
constexpr int max_pwr = 5;
|
||||
|
||||
scope log(XO_DEBUG2(debug_flag, "ratio_tests"));
|
||||
log && log(xtag("n_ratio", n_ratio));
|
||||
|
||||
ratio_distribution<int> ratio_dist(0.25 /*sign_prob*/,
|
||||
0.05 /*lambda */);
|
||||
bernoulli_distribution sign_dist(0.5);
|
||||
exponential_distribution<double> power_dist(0.2 /*lambda*/);
|
||||
|
||||
std::vector<xo::ratio::ratio<int>> ratio_v;
|
||||
|
||||
/* ensure 0, 1, -1 all present */
|
||||
ratio_v.push_back(xo::ratio::ratio<int>(0,1));
|
||||
ratio_v.push_back(xo::ratio::ratio<int>(1,1));
|
||||
ratio_v.push_back(xo::ratio::ratio<int>(-1,1));
|
||||
|
||||
for (std::uint32_t i=0, n=n_ratio - ratio_v.size(); i<n; ++i) {
|
||||
ratio_v.push_back(ratio_dist(rng));
|
||||
|
||||
REQUIRE(std::gcd(ratio_v[i].num(), ratio_v[i].den()) == 1);
|
||||
}
|
||||
|
||||
INFO(XTAG(ratio_v));
|
||||
|
||||
for (std::uint32_t i=0; i<n_experiment; ++i) {
|
||||
INFO(tostr(XTAG(i), XTAG(n_experiment)));
|
||||
|
||||
/* choose a couple of ratios at random */
|
||||
auto ratio1 = ratio_v[rng() % n_ratio];
|
||||
auto ratio2 = ratio_v[rng() % n_ratio];
|
||||
|
||||
double ratio1_approx = ratio1.num() / static_cast<double>(ratio1.den());
|
||||
double ratio2_approx = ratio2.num() / static_cast<double>(ratio2.den());
|
||||
|
||||
{
|
||||
auto sum = ratio1 + ratio2;
|
||||
|
||||
double sum_approx = sum.num() / static_cast<double>(sum.den());
|
||||
|
||||
log && log(XTAG(ratio1), XTAG(ratio2), XTAG(sum));
|
||||
|
||||
REQUIRE(sum_approx == Approx(ratio1_approx + ratio2_approx).epsilon(1e-6));
|
||||
REQUIRE(std::gcd(sum.num(), sum.den()) == 1);
|
||||
REQUIRE(sum.den() > 0);
|
||||
|
||||
/* comparison tests. piggyback on sum */
|
||||
{
|
||||
auto cmp_approx = (sum_approx <=> ratio1_approx);
|
||||
REQUIRE(cmp_approx == (sum <=> ratio1));
|
||||
}
|
||||
{
|
||||
bool eq = (sum == ratio1);
|
||||
bool eq_approx = (sum_approx == ratio1_approx);
|
||||
REQUIRE(eq == eq_approx);
|
||||
}
|
||||
{
|
||||
bool ne = (sum != ratio1);
|
||||
bool ne_approx = (sum_approx != ratio1_approx);
|
||||
REQUIRE(ne == ne_approx);
|
||||
}
|
||||
{
|
||||
bool gt = (sum > ratio1);
|
||||
bool gt_approx = (sum_approx > ratio1_approx);
|
||||
REQUIRE(gt == gt_approx);
|
||||
}
|
||||
{
|
||||
bool ge = (sum >= ratio1);
|
||||
bool ge_approx = (sum_approx >= ratio1_approx);
|
||||
REQUIRE(ge == ge_approx);
|
||||
}
|
||||
{
|
||||
bool lt = (sum > ratio1);
|
||||
bool lt_approx = (sum_approx > ratio1_approx);
|
||||
REQUIRE(lt == lt_approx);
|
||||
}
|
||||
{
|
||||
bool le = (sum >= ratio1);
|
||||
bool le_approx = (sum_approx >= ratio1_approx);
|
||||
REQUIRE(le == le_approx);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto neg = -ratio1;
|
||||
|
||||
double neg_approx = neg.num() / static_cast<double>(neg.den());
|
||||
|
||||
log && log(XTAG(ratio1), XTAG(neg));
|
||||
|
||||
REQUIRE(neg_approx == Approx(-ratio1_approx).epsilon(1e-06));
|
||||
REQUIRE(std::gcd(neg.num(), neg.den()) == 1);
|
||||
REQUIRE(neg.den() > 0);
|
||||
}
|
||||
|
||||
{
|
||||
auto diff = ratio1 - ratio2;
|
||||
|
||||
double diff_approx = diff.num() / static_cast<double>(diff.den());
|
||||
|
||||
log && log(XTAG(ratio1), XTAG(ratio2), XTAG(diff));
|
||||
|
||||
REQUIRE(diff_approx == Approx(ratio1_approx - ratio2_approx).epsilon(1e-6));
|
||||
REQUIRE(std::gcd(diff.num(), diff.den()) == 1);
|
||||
REQUIRE(diff.den() > 0);
|
||||
}
|
||||
|
||||
{
|
||||
auto prod = ratio1 * ratio2;
|
||||
|
||||
double prod_approx = prod.num() / static_cast<double>(prod.den());
|
||||
|
||||
log && log(XTAG(ratio1), XTAG(ratio2), XTAG(prod));
|
||||
|
||||
REQUIRE(prod_approx == Approx(ratio1_approx * ratio2_approx).epsilon(1e-6));
|
||||
REQUIRE(std::gcd(prod.num(), prod.den()) == 1);
|
||||
REQUIRE(prod.den() > 0);
|
||||
}
|
||||
|
||||
{
|
||||
auto div = ratio1 * ratio2;
|
||||
|
||||
double div_approx = div.num() / static_cast<double>(div.den());
|
||||
|
||||
log && log(XTAG(ratio1), XTAG(ratio2), XTAG(div));
|
||||
|
||||
REQUIRE(div_approx == Approx(ratio1_approx * ratio2_approx).epsilon(1e-6));
|
||||
REQUIRE(std::gcd(div.num(), div.den()) == 1);
|
||||
REQUIRE(div.den() > 0);
|
||||
}
|
||||
|
||||
{
|
||||
int exp = (sign_dist(rng) ? -1 : +1) * power_dist(rng);
|
||||
|
||||
if (std::abs(exp) >= max_pwr) {
|
||||
exp = (std::signbit(exp) ? -1 : +1) * max_pwr;
|
||||
}
|
||||
|
||||
auto pwr = ratio1.power(exp);
|
||||
|
||||
double pwr_approx = pwr.num() / static_cast<double>(pwr.den());
|
||||
|
||||
log && log(XTAG(ratio1), XTAG(exp), XTAG(pwr));
|
||||
|
||||
REQUIRE(pwr_approx == Approx(::pow(ratio1_approx, exp)).epsilon(1e-6));
|
||||
REQUIRE(std::gcd(pwr.num(), pwr.den()) == 1);
|
||||
REQUIRE(pwr.den() >= 0);
|
||||
}
|
||||
|
||||
{
|
||||
auto ratio1_str = ratio1.template to_str<20>();
|
||||
|
||||
log && log(XTAG(ratio1_str));
|
||||
|
||||
REQUIRE(!ratio1_str.empty());
|
||||
|
||||
/* TODO: more tests here --
|
||||
* if we write parser, verify can recover original ratio
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("ratio", "[ratio]") {
|
||||
//constexpr bool c_debug_flag = false;
|
||||
|
||||
// can get bits from /dev/random by uncommenting the 2nd line below
|
||||
uint64_t seed = 5521646833469436535ul;
|
||||
//rng::Seed<rng::xoshiro256ss> seed;
|
||||
|
||||
//std::cerr << "ratio: seed=" << seed << std::endl;
|
||||
|
||||
auto rng = rng::xoshiro256ss(seed);
|
||||
|
||||
ratio_tests(rng);
|
||||
} /*TEST_CASE(ratio)*/
|
||||
|
||||
} /*namespace ut*/
|
||||
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/** end ratio.utest.cpp **/
|
||||
32
xo-ratio/utest/ratio_reflect.test.cpp
Normal file
32
xo-ratio/utest/ratio_reflect.test.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* @file ratio_reflect.test.cpp */
|
||||
|
||||
#include "xo/ratio/ratio_reflect.hpp"
|
||||
#include "xo/reflect/reflect_struct.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
namespace xo {
|
||||
namespace ut {
|
||||
TEST_CASE("ratio_reflect", "[ratio][reflect]") {
|
||||
using xo::reflect::reflect_struct;
|
||||
using xo::reflect::TypeDescrBase;
|
||||
using xo::reflect::TypeDescr;
|
||||
using xo::ratio::ratio;
|
||||
|
||||
/* verify ratio reflection */
|
||||
|
||||
TypeDescr td = reflect_struct<ratio<std::int64_t>>();
|
||||
|
||||
REQUIRE(td->is_struct());
|
||||
REQUIRE(td->metatype() == xo::reflect::Metatype::mt_struct);
|
||||
REQUIRE(td->n_child(nullptr) == 2);
|
||||
REQUIRE(td->struct_member(0).member_name() == "num");
|
||||
REQUIRE(td->struct_member(0).get_member_td()->short_name() == "long int");
|
||||
REQUIRE(td->struct_member(1).member_name() == "den");
|
||||
REQUIRE(td->struct_member(1).get_member_td()->short_name() == "long int");
|
||||
|
||||
TypeDescrBase::print_reflected_types(std::cerr);
|
||||
}
|
||||
} /*namespace ut*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end ratio_reflect.test.cpp */
|
||||
6
xo-ratio/utest/ratio_utest_main.cpp
Normal file
6
xo-ratio/utest/ratio_utest_main.cpp
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/** @file ratio_utest_main.cpp **/
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
/** end ratio_utest_main.cpp **/
|
||||
Loading…
Add table
Add a link
Reference in a new issue