pax_global_header00006660000000000000000000000064145127577400014526gustar00rootroot0000000000000052 comment=4e8617e6baf7d277b54354abade5703363140792 zug-0.1.1/000077500000000000000000000000001451275774000123325ustar00rootroot00000000000000zug-0.1.1/.clang-format000066400000000000000000000020661451275774000147110ustar00rootroot00000000000000--- AlignAfterOpenBracket: Align AlignConsecutiveAssignments: 'true' AlignEscapedNewlines: Right AlignTrailingComments: 'true' AllowShortFunctionsOnASingleLine: 'true' AllowShortBlocksOnASingleLine: 'true' AlwaysBreakTemplateDeclarations: 'true' AccessModifierOffset: -4 BinPackArguments: 'false' BinPackParameters: 'false' BreakBeforeBraces: Mozilla BreakBeforeInheritanceComma: 'true' BreakBeforeTernaryOperators: 'true' BreakConstructorInitializers: BeforeComma BreakStringLiterals: 'true' ColumnLimit: '80' CompactNamespaces: 'false' ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' FixNamespaceComments: 'true' IndentCaseLabels: 'false' IndentWidth: '4' IndentWrappedFunctionNames: 'false' KeepEmptyLinesAtTheStartOfBlocks: 'false' Language: Cpp MaxEmptyLinesToKeep: '1' NamespaceIndentation: None PointerAlignment: Left ReflowComments: 'true' SortIncludes: 'true' SortUsingDeclarations: 'true' SpaceAfterCStyleCast: 'true' SpaceAfterTemplateKeyword: 'true' SpaceBeforeAssignmentOperators: 'true' SpaceBeforeParens: ControlStatements TabWidth: '4' UseTab: Never ... zug-0.1.1/.dir-locals.el000066400000000000000000000003471451275774000147670ustar00rootroot00000000000000((nil . ((indent-tabs-mode . nil) (show-trailing-whitespace . t) (fill-column . 80))) (c-mode . ((mode . c++))) (c++-mode . ((eval add-hook 'before-save-hook #'clang-format-buffer nil t)))) zug-0.1.1/.github/000077500000000000000000000000001451275774000136725ustar00rootroot00000000000000zug-0.1.1/.github/FUNDING.yml000066400000000000000000000001251451275774000155050ustar00rootroot00000000000000github: arximboldi patreon: sinusoidal custom: ["paypal.me/sinusoidal", sinusoid.al] zug-0.1.1/.github/workflows/000077500000000000000000000000001451275774000157275ustar00rootroot00000000000000zug-0.1.1/.github/workflows/test.yml000066400000000000000000000045461451275774000174420ustar00rootroot00000000000000name: test on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: cachix/install-nix-action@v22 with: nix_path: nixpkgs=channel:nixos-unstable - uses: cachix/cachix-action@v12 with: name: arximboldi signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' - run: nix-build docs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: submodules: true - uses: cachix/install-nix-action@v22 with: nix_path: nixpkgs=channel:nixos-unstable - uses: cachix/cachix-action@v12 with: name: arximboldi signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' - run: nix-shell --run "mkdir build" - run: nix-shell --run "cd build && cmake .." - run: nix-shell --run "cd build && make docs" - uses: shimataro/ssh-key-action@v2 if: github.ref == 'refs/heads/master' with: key: ${{ secrets.SINUSOIDES_SSH_KEY }} known_hosts: ${{ secrets.SINUSOIDES_KNOWN_HOSTS }} - run: nix-shell --run "cd build && make upload-docs" if: github.ref == 'refs/heads/master' check: strategy: matrix: type: [Debug, Release] compiler: [gcc, clang] opts: [[]] include: - type: Debug compiler: gcc opts: ['coverage'] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: cachix/install-nix-action@v22 with: nix_path: nixpkgs=channel:nixos-unstable - uses: cachix/cachix-action@v12 with: name: arximboldi signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' - run: nix-shell --argstr compiler ${{ matrix.compiler }} --run "mkdir build" - name: configure cmake run: | nix-shell --argstr compiler ${{ matrix.compiler }} --run " cd build && cmake .. \ -DCMAKE_BUILD_TYPE=${{ matrix.type }} \ -DENABLE_COVERAGE=${{ contains(matrix.opts, 'coverage') }} " - run: nix-shell --argstr compiler ${{ matrix.compiler }} --run "cd build && make check -j`nproc`" - run: nix-shell --argstr compiler ${{ matrix.compiler }} --run "bash <(curl -s https://codecov.io/bash)" if: ${{ contains(matrix.opts, 'coverage') }} zug-0.1.1/.gitignore000066400000000000000000000001231451275774000143160ustar00rootroot00000000000000bazel-*/ build* result zug/config.hpp doc/_build doc/_doxygen tools/travis/ssh-key zug-0.1.1/.gitmodules000066400000000000000000000002231451275774000145040ustar00rootroot00000000000000[submodule "tools/sinusoidal-sphinx-theme"] path = tools/sinusoidal-sphinx-theme url = https://github.com/arximboldi/sinusoidal-sphinx-theme.git zug-0.1.1/BUILD000066400000000000000000000003171451275774000131150ustar00rootroot00000000000000package(default_visibility = ["//visibility:public"]) cc_library( name = "zug", include_prefix = "zug", strip_include_prefix = "zug", hdrs = glob([ "zug/**/*.hpp", ]), ) zug-0.1.1/CMakeLists.txt000066400000000000000000000041761451275774000151020ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.8) cmake_policy(SET CMP0048 NEW) # enable project VERSION cmake_policy(SET CMP0056 NEW) # honor link flags in try_compile() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") project(zug VERSION 0.1.0) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") set(CMAKE_EXPORT_COMPILE_COMMANDS on) set(CMAKE_CXX_EXTENSIONS off) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments") endif() include(GNUInstallDirs) option(DISABLE_WERROR "enable --werror") option(zug_BUILD_TESTS "Build tests" ON) option(zug_BUILD_EXAMPLES "Build examples" ON) option(zug_BUILD_DOCS "Build docs" ON) if (NOT MSVC AND NOT DISABLE_WERROR) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") endif() # Targets # ======= # ccache support find_program(CCACHE ccache) if (CCACHE) message(STATUS "Using ccache: ${CCACHE}") set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE}) set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE}) else() message(STATUS "Could not find ccache") endif() # the library add_library(zug INTERFACE) target_include_directories(zug INTERFACE $ $ $) install(TARGETS zug EXPORT ZugConfig) install(EXPORT ZugConfig DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Zug") install(DIRECTORY zug DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") enable_testing() add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Build and run all the tests and examples.") # the library, local development target if(zug_BUILD_TESTS) find_package(Catch2 REQUIRED) add_library(zug-dev INTERFACE) target_link_libraries(zug-dev INTERFACE zug) if (ENABLE_COVERAGE) target_compile_options(zug-dev INTERFACE "--coverage") target_link_libraries(zug-dev INTERFACE "--coverage") endif() add_subdirectory(test) endif() if(zug_BUILD_EXAMPLES) add_subdirectory(example) endif() if (zug_BUILD_DOCS) add_subdirectory(doc) endif() zug-0.1.1/LICENSE000066400000000000000000000024721451275774000133440ustar00rootroot00000000000000Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. zug-0.1.1/README.rst000066400000000000000000000147041451275774000140270ustar00rootroot00000000000000.. image:: https://github.com/arximboldi/zug/workflows/test/badge.svg :target: https://github.com/arximboldi/zug/actions?query=workflow%3Atest+branch%3Amaster :alt: Github Actions Badge .. image:: https://codecov.io/gh/arximboldi/zug/branch/master/graph/badge.svg :target: https://codecov.io/gh/arximboldi/zug :alt: CodeCov Badge .. image:: https://cdn.jsdelivr.net/gh/arximboldi/zug/doc/_static/sinusoidal-badge.svg :target: https://sinusoid.al :alt: Sinusoidal Engineering badge :align: right .. raw:: html Logotype .. include:introduction/start **zug** is a C++ library providing `transducers`_. Transducers are composable sequential transformations independent of the source. They are extremely lightweight, and can be used to express algorithms over pull-based sequences (iterators, files) but also push based sequences (signals, events, asynchronous streams) in a generic way. .. _transducers: https://clojure.org/reference/transducers * **Documentation** (Contents_) * **Code** (GitHub_) * **CppCon 2015 Talk**: *Transducers: from Clojure to C++* (`YouTube `_, `Slides `_) .. _contents: https://sinusoid.es/zug/#contents .. _github: https://github.com/arximboldi/zug .. raw:: html This project is part of a long-term vision helping interactive and concurrent C++ programs become easier to write. **Help this project's long term sustainability by becoming a patron or buying a sponsorship package:** juanpe@sinusoid.al .. include:index/end Example ------- .. code-block:: c++ auto xf = zug::filter([](int x) { return x > 0; }) | zug::map([](int x) { return std::to_string(x); }); Here ``xf`` is a *transducer*, a transformation over a sequence of integers, resulting in a sequence of strings. Note, however, that this transformation makes no reference to whatever it is transforming. In fact, we can apply it in many ways. Transforming a range ~~~~~~~~~~~~~~~~~~~~ .. code-block:: c++ auto data1 = std::vector{3, -2, 42, -10}; auto data2 = zug::into(std::vector{}, xf, data1); assert(data2 == {"3", "42"}); As a lazy iterator ~~~~~~~~~~~~~~~~~~ .. code-block:: c++ auto data1 = std::vector{ ... }; auto data2 = zug::sequence(xf, data1); std::copy(data2.begin(), data2.end(), ...); Generators and sinks ~~~~~~~~~~~~~~~~~~~~ .. code-block:: c++ zug::run(zug::read(std::cin) | xf | zug::write(std::cout)); Reads integers from the terminal and outputs back the positive ones. Transforming cursors ~~~~~~~~~~~~~~~~~~~~ The library is used in `Lager`_, a library implementing the unidirectional data-flow architecture for C++ interactive applications. It is used to treat reactive values as a temporal sequence that can be transformed in arbitrary ways. For example: .. _Lager: https://sinusoid.es/lager .. code-block:: c++ auto x = lager::state{42}; auto y = lager::reader{x.xform(xf)} y.watch([] (auto&& v) { std::cout << v << std::endl; }); x.set(10); // outputs: 10 x.set(-2); // no output Why? ---- You have learn Sean Parent's lesson: `No Raw Loops `_. Instead of iterating over sequences directly, you use STL algoriths like `transform`_, `filter`_, etc, or even better, the new `ranges`_ library. However, what if you have a *sequence* that can not be easily or efficiently expressed as an iterator? Then, you may have to reimplement all these algorithms again, on top of whatever sequence abstraction you have invented, for example, see `RxCpp`_... Or you use transducers. Transducers are generic algorithmic transformations, in a way that is completely agnostic of the actual sequence that is being transformed. As a library author, you can add transducer support for your library, and automatically get access to our wide `collection of transducers`_ and allow your users to simply `write their own`_. .. _ranges: https://en.cppreference.com/w/cpp/ranges .. _transform: https://en.cppreference.com/w/cpp/algorithm/transform .. _filter: https://en.cppreference.com/w/cpp/algorithm/filter .. _RxCpp: https://github.com/ReactiveX/RxCpp .. _collection of transducers: https://sinusoid.es/zug/transducer.html .. _write their own: https://sinusoid.es/zug/transducer.html Dependencies ------------ This library is written in **C++14** and a compliant compiler is necessary. It is `continuously tested`_ with Clang 3.8 and GCC 6, but it might work with other compilers and versions. If compiling with **C++14** and using `skip`_, boost variant is required. For **C++17** and above, no external library is necessary and there are no other requirements. .. _continuously tested: https://travis-ci.org/arximboldi/immer .. _skip: https://sinusoid.es/zug/state.html?#skip Usage ----- This is a **header only** library but to be configured correctly you need to run `CMake`_ first:: mkdir -p build && cd build cmake .. Or you can just copy the ``zug`` subfolder somewhere in your *include path*. Development ----------- .. _nix package manager: https://nixos.org/nix .. _cmake: https://cmake.org/ In order to develop the library, you will need to compile and run the examples, tests and benchmarks. These require some additional tools. The easiest way to install them is by using the `Nix package manager`_. At the root of the repository just type:: nix-shell This will download all required dependencies and create an isolated environment in which you can use these dependencies, without polluting your system. Then you can proceed to generate a development project using `CMake`_:: mkdir build && cd build cmake .. From then on, one may build and run all tests by doing:: make check License ------- .. image:: https://upload.wikimedia.org/wikipedia/commons/c/cd/Boost.png :alt: Boost logo :target: http://boost.org/LICENSE_1_0.txt :align: left **This software is licensed under the Boost Software License 1.0**. The full text of the license is can be accessed `via this link `_ and is also included in the ``LICENSE`` file of this software package. zug-0.1.1/WORKSPACE000066400000000000000000000000001451275774000136010ustar00rootroot00000000000000zug-0.1.1/codecov.yml000066400000000000000000000000361451275774000144760ustar00rootroot00000000000000ignore: - tools - example zug-0.1.1/default.nix000066400000000000000000000012401451275774000144730ustar00rootroot00000000000000with import {}; stdenv.mkDerivation rec { name = "zug-git"; version = "git"; src = builtins.filterSource (path: type: baseNameOf path != ".git" && baseNameOf path != "build" && baseNameOf path != "_build" && baseNameOf path != "reports" && baseNameOf path != "tools") ./.; buildInputs = [ cmake boost ]; cmakeFlags = [ "-Dzug_BUILD_TESTS=OFF" "-Dzug_BUILD_EXAMPLES=OFF" ]; meta = with lib; { homepage = "https://github.com/arximboldi/zug"; description = "library for functional interactive c++ programs"; license = licenses.boost; }; } zug-0.1.1/doc/000077500000000000000000000000001451275774000130775ustar00rootroot00000000000000zug-0.1.1/doc/CMakeLists.txt000066400000000000000000000010501451275774000156330ustar00rootroot00000000000000 # Targets # ======= add_custom_target(doxygen COMMAND doxygen doxygen.config WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") add_custom_target(docs COMMAND make html WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") add_dependencies(docs doxygen) set(zug_ssh_method ssh -p 5488 -o StrictHostKeyChecking=no -i ${CMAKE_SOURCE_DIR}/tools/travis/ssh-key) add_custom_target(upload-docs COMMAND rsync -av -e \"${zug_ssh_method}\" ${CMAKE_CURRENT_SOURCE_DIR}/_build/html/* raskolnikov@sinusoid.es:public/zug/) zug-0.1.1/doc/Makefile000066400000000000000000000167061451275774000145510ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " epub3 to make an epub3" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" @echo " dummy to check syntax errors of document sources" .PHONY: clean clean: rm -rf $(BUILDDIR)/* .PHONY: html html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html bash sphinx-html-hack.bash @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." .PHONY: dirhtml dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." .PHONY: singlehtml singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." .PHONY: pickle pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." .PHONY: json json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." .PHONY: htmlhelp htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." .PHONY: qthelp qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/zug.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/zug.qhc" .PHONY: applehelp applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." .PHONY: devhelp devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/zug" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/zug" @echo "# devhelp" .PHONY: epub epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." .PHONY: epub3 epub3: $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 @echo @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." .PHONY: latex latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." .PHONY: latexpdf latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: latexpdfja latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: text text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." .PHONY: man man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." .PHONY: texinfo texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." .PHONY: info info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." .PHONY: gettext gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." .PHONY: changes changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." .PHONY: linkcheck linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." .PHONY: doctest doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." .PHONY: coverage coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." .PHONY: xml xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." .PHONY: pseudoxml pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." .PHONY: dummy dummy: $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy @echo @echo "Build finished. Dummy builder generates no files." zug-0.1.1/doc/_static/000077500000000000000000000000001451275774000145255ustar00rootroot00000000000000zug-0.1.1/doc/_static/composition-flat.svg000066400000000000000000003152431451275774000205450ustar00rootroot00000000000000 image/svg+xml zug-0.1.1/doc/_static/composition.svg000066400000000000000000000452551451275774000176240ustar00rootroot00000000000000 image/svg+xml *out++ = input'; *out++ = input'; input' = m(input); *out++ = input'; input' = m(input); if (pred(input)) {} output map(m)(output) filter(pred)(map(m)(output)) zug-0.1.1/doc/_static/logo-black.svg000066400000000000000000000105061451275774000172620ustar00rootroot00000000000000 image/svg+xml zug-0.1.1/doc/_static/logo-front.svg000066400000000000000000000105031451275774000173330ustar00rootroot00000000000000 image/svg+xml zug-0.1.1/doc/_static/patreon.svg000066400000000000000000000155631451275774000167300ustar00rootroot00000000000000 image/svg+xmlsupportsupportsupport us on zug-0.1.1/doc/_static/sinusoidal-badge.svg000066400000000000000000000771071451275774000204740ustar00rootroot00000000000000 image/svg+xml zug-0.1.1/doc/_static/zug-logo.svg000066400000000000000000000401071451275774000170130ustar00rootroot00000000000000 image/svg+xml zug-0.1.1/doc/conf.py000066400000000000000000000306101451275774000143760ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # zug documentation build configuration file, created by # sphinx-quickstart on Thu Oct 27 18:10:24 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.mathjax', 'breathe', ] breathe_projects = { "zug": "_doxygen/xml" } breathe_default_project = "zug" # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # from recommonmark.parser import CommonMarkParser from recommonmark.transform import AutoStructify source_parsers = { '.md': CommonMarkParser, } source_suffix = ['.rst', '.md'] def setup(app): app.add_config_value('recommonmark_config', { 'enable_eval_rst': True, }, True) app.add_transform(AutoStructify) # The encoding of source files. # # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'zug' copyright = u'2016, 2017 Juan Pedro Bolivar Puente' author = u'Juan Pedro Bolivar Puente' raw_enabled = True # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = u'0.0.0' # The full version, including alpha/beta/rc tags. release = u'0.0.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # # today = '' # # Else, today_fmt is used as the format for a strftime call. # # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The reST default role (used for this markup: `text`) to use for all # documents. # # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # import sys import os.path sys.path.append(os.path.join(os.path.dirname(__file__), '../tools/sinusoidal-sphinx-theme')) import sinusoidal_sphinx_theme html_theme_path = sinusoidal_sphinx_theme.html_theme_path() html_theme = 'sinusoidal_sphinx_theme' extensions.append("sinusoidal_sphinx_theme") html_theme_options = { "project_nav_name": "zug", "github_link" : "https://github.com/arximboldi/zug", } # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. # " v documentation" by default. # # html_title = u'zug v0.0.0' # A shorter title for the navigation bar. Default is the same as html_title. # # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = '_static/logo-black.svg' # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # # html_extra_path = [] # If not None, a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. # The empty string is equivalent to '%b %d, %Y'. # # html_last_updated_fmt = None # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # # html_additional_pages = {} # If false, no module index is generated. # # html_domain_indices = True # If false, no index is generated. # # html_use_index = True # If true, the index is split into individual pages for each letter. # # html_split_index = False # If true, links to the reST sources are added to the pages. # # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' # # html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # 'ja' uses this config value. # 'zh' user can custom change `jieba` dictionary path. # # html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. # # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'zugdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'zug.tex', u'zug Documentation', u'Juan Pedro Bolivar Puente', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # # latex_use_parts = False # If true, show page references after internal links. # # latex_show_pagerefs = False # If true, show URL addresses after external links. # # latex_show_urls = False # Documents to append as an appendix to all manuals. # # latex_appendices = [] # It false, will not define \strong, \code, itleref, \crossref ... but only # \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added # packages. # # latex_keep_old_macro_names = True # If false, no module index is generated. # # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'zug', u'zug Documentation', [author], 1) ] # If true, show URL addresses after external links. # # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'zug', u'zug Documentation', author, 'zug', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # # texinfo_appendices = [] # If false, no module index is generated. # # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # # texinfo_no_detailmenu = False # -- Options for Epub output ---------------------------------------------- # Bibliographic Dublin Core info. epub_title = project epub_author = author epub_publisher = author epub_copyright = copyright # The basename for the epub file. It defaults to the project name. # epub_basename = project # The HTML theme for the epub output. Since the default themes are not # optimized for small screen space, using the same theme for HTML and epub # output is usually not wise. This defaults to 'epub', a theme designed to save # visual space. # # epub_theme = 'epub' # The language of the text. It defaults to the language option # or 'en' if the language is not set. # # epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. # epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. # # epub_identifier = '' # A unique identification for the text. # # epub_uid = '' # A tuple containing the cover image and cover page html template filenames. # # epub_cover = () # A sequence of (type, uri, title) tuples for the guide element of content.opf. # # epub_guide = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. # # epub_pre_files = [] # HTML files that should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. # # epub_post_files = [] # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] # The depth of the table of contents in toc.ncx. # # epub_tocdepth = 3 # Allow duplicate toc entries. # # epub_tocdup = True # Choose between 'default' and 'includehidden'. # # epub_tocscope = 'default' # Fix unsupported image types using the Pillow. # # epub_fix_images = False # Scale large images. # # epub_max_image_width = 0 # How to display URL addresses: 'footnote', 'no', or 'inline'. # # epub_show_urls = 'inline' # If false, no index is generated. # # epub_use_index = True zug-0.1.1/doc/design.rst000066400000000000000000000451731451275774000151140ustar00rootroot00000000000000.. _design: Design ====== This section elaborates on what a *transducer* is. It will help you understand how to use them, but also how to write your own transducers and processes. What is a transducer? --------------------- .. image:: https://public.sinusoid.es/talks/transducers-cppcon15/pic/rich2.jpg :align: right :width: 40% .. .. centered:: "Transducers extract the essence of *map* and *filter*" | Rich Hickey, author of Clojure, *on Transducers* `(video) `_ `(blog) `_ .. _accumulate: https://en.cppreference.com/w/cpp/algorithm/accumulate .. _reduce: https://clojuredocs.org/clojure.core/reduce .. _fold: https://wiki.haskell.org/Fold Transducers come after the realization that the most general algorithm over sequences is what in C++ we call accumulate_, but in other languages is often called reduce_ or fold_. Accumulate applies a *reducing function* iteratively over a sequence. The reducing function takes a *state* and some *input* as arguments, and returns an updated state. For example, we can define a *reducing function* ``output`` that takes an output iterator as *state*, some value as *input*, and when invoked, outputs the value using the iterator and returns the iterator into the next position: .. code-block:: c++ auto output = [](auto state, auto input) { *state = input; return ++state; }; Using this function, we can implement ``std::copy`` in terms of ``accumulate``: .. code-block:: c++ template OutputIt copy(InputIt first, InputIt last, InputIt out) { return accumulate(first, last, out, output); } We can also use this structure to implement ``transform`` and ``filter``: .. code-block:: c++ template OutputIt transform(InputIt first, InputIt last, InputIt out, Mapping mapping) { return accumulate(first, last, out, [&](auto state, auto input) { return output(state, mapping(input)); }); } template OutputIt filter(InputIt first, InputIt last, InputIt out, Predicate pred) { return accumulate(first, last, out, [&](auto state, auto input) { return pred(input) ? output(state, input) : state; }); } Notice something? The functions look almost identical. There is a part that is almost identical: calls to ``accumulate`` and ``output`` that specify the input and output parts of the :ref:`process`. The meat of the algorithm is sandwitched in between. We can extract their by writing them as functions take a *reducing function* (like output) as an argument, and as result return another *reducing function* that, for example, maps the input betweeing passing it over the next reducing function. A **transducer** is *a function* that takes a *reducing function*, and returns another *reducing function* that wraps it. .. code-block:: c++ template auto map(Mapping mapping) { return [=] (auto step) { return [=] (auto s, auto... ins) { return step(s, mapping(ins...)); }; }; } template auto filter(Predicate pred) { return [=] (auto step) { return [=] (auto s, auto... ins) { return pred(input) ? step(s, ins...) : s; }; }; } .. warning:: This code could be improved by leveraging **move semantics** and **perfect forwarding** wherever we pass stuff to the underlying transducer. This, however, adds too much noise to the code examples. These concepts are already hard enough to understand to those uninitated in functional programming. As an exercise can think about you would use ``std::move`` and ``std::forward`` in these examples. You can check the implementation of these transducers in the library itself for some inspiration. These two functions return a *transducer*, this a function that takes a *reducing function* called ``step`` as an argument, and return another reducing function. We can revise our implementation of our standard algorithms using them: .. code-block:: c++ template OutputIt transform(InputIt first, InputIt last, InputIt out, Mapping mapping) { return accumulate(first, last, out, map(mapping)(output)); } template OutputIt filter(InputIt first, InputIt last, InputIt out, Predicate pred) { return accumulate(first, last, out, filter(pred)(output)); } What if you wanted to write a function that filters and transforms all in one go? .. code-block:: c++ template OutputIt filter_and_transform( InputIt first, InputIt last, InputIt out, Predicate pred, Mapping mapping) { return accumulate(first, last, out, filter(pred)(map(mapping)(output))); } Or using piping for function composition and extracting out the transducer: .. code-block:: c++ template OutputIt filter_and_transform( InputIt first, InputIt last, InputIt out, Predicate pred, Mapping mapping) { auto xf = filter(pred) | mapping(mapping); return accumulate(first, last, out, xf(output))); } .. admonition:: Tip :class: tip The library provides a function ``comp`` for function composition, but also ``operator|`` for more natural reading syntax. You can also turn any function ``f`` into a pipeable one by using ``comp(f)``. .. admonition:: Order of composition The ``filter_and_transform`` function above performs filtering and mapping precisely in that order: it filters first, and only if the value passes the predicate is it mapped at all. This, *composition of transducers reads from left to right*. This may be unintuitive if that this is simply function composition, and function composition reads from right to left. However, remember that a transducer, as a function, is not a transformation over a sequence, but a transformation over an operation. Each transducer is, indeed, called from right to left. Each transducer transforms the operation, wrapping it into a more complex operation. It is like an onion, each transducer adds a new layer to the onion. The result is a more complex operation. When data is fed into the operation, it will enter through the external layer of the onion, this is through the layer that the last transducer (the leftmost!), and penetrate the onion in this reverse order until it reaches the core of the onion---to the rightmost transducer, and then the original *reducing function*. .. image:: _static/composition-flat.svg :width: 100% Dealing with state ------------------ Now you know how to write a simple transducer. But what if you want to do something more advanced? For example, you may want to write a transducer like Python's enumerate_, that given a sequence of values, pairs each element with an index. You may be tempted to do it like this: .. code-block:: c++ auto enumerate = [=] (auto step) { return [=, idx = 0] (auto s, auto... ins) mutable { return step(s, idx++, ins...); }; }; This works, but it has one problem: it hides a mutable variable in the reducer. It needs to be marked ``mutable`` and it is no longer a `pure function`_. We have to be careful about which instance are we using when. But we already have a functional, explicit, *state*, the variable `s`. The problem is that we can not make any assumptions about its type or manipulate it in any way --- only somewhere down in the next reducing function ``step`` can it be understood. .. _pure function: https://en.wikipedia.org/wiki/Pure_function .. _enumerate: https://book.pythontips.com/en/latest/enumerate.html The solution is to instead *wrap* the incoming state, adding any additional data you need next to it. There is some nuance to it: the first time the transducer is invoked there state you receive is unwrapped, but the second time you'll receive a wrapped state. The library provides some methods to deal with this: - ``state_wrap(s, d)``, returns a new wrapped state containing both the state ``s`` and the additional data ``d``. - ``state_unwrap(s)``, returns the underlying state from the potentially wrapped state ``s``. - ``state_data(s, [] { return ...; })``, returns the wrapped data in the state ``s``. If none exists, it, produces an initial value using the supplied callback. Using this tool we can now improve our implementation of ``enumerate``: .. code-block:: c++ auto enumerate = [=] (auto step) { return [=] (auto s, auto... ins) mutable { auto idx = state_data(s, [] { return 0; }); auto next = step(s, idx, ins...); return wrap_state(next, ++idx); }; }; .. note:: In Clojure, stateful transducers hide state within the transducer the way we showed in the initial example. The reason, I believe, that they do not follow our approach, is that since Clojure is a dynamic language, this would add too much of a runtime overhead, as one would need to constantly check whether we are dealing with wrapped or unwrapped data. In C++ all this happens at compile time, with no runtime overhead. Ironically C++ happens to be *more* functional than Clojure in their transducers implementation 😉 Early termination ----------------- Some transducers stop producing any output after certain conditions. One example is ``take`` which it lets at most ``n`` elements of the sequence in. Howe can we signal to the reducing process, and from within the reducing function, that there is no more work to be done? One option would be to throw an exception, that may be catched by the reducing process. But exceptions are an inefficient way of control flow in C++. Instead, we may realize that termination, whether we are done or not, is a property of the state. Think about it: the transducer ``take`` is gonna anotate the state with some kind of *count*. The transducer is done when this count reaches zero. We say that a state that represents a finished computation is *reduced*. We can express this in the library like this: .. code-block:: c++ struct take_tag {}; bool state_wrapper_data_is_reduced(take_tag, int n) { return n <= 0; } auto take(int n) { return comp([=](auto step) { return [=](auto s, auto... is) { return wrap_state( step(state_unwrap(s), is...), state_data(s, [=] { return n; }) - 1); }; }); } Note how we added a ``take_tag`` to identify our state, and how we introduced the ``state_wrapper_data_is_reduced`` to specify whether the data associated to a particular state indicates termination. .. admonition:: Is ``accumulate`` a valid reducing process? You may have noted that we are increasingly adding stuff to how the reducing *process* interacts with the *reducing function*. For once, the type of the state changes after the first iteration. Also, we now have to consider whether the state is reduced (the library provides ``state_is_reduced`` for that) and stop the process in that case. For this reason: ``std::accumulate`` may not work, in general, with the reducing functions produced by our transducers. The library provides it's own function, ``reduce()`` and the convenience ``transduce()`` that work like *accumulate* but considering all these state management quirks. Conditional invocation ---------------------- In the presence of state, we need to reconsider transducers that conditionally invoke the next reducing function, like ``filter``. The problem is: if the next reducing function wraps the state with additional state, we may need to return something of a different type depending on whether we call the reducing function (we get a wrapped state) or don't (we may or may not have a wrapped state at hand already). The solution is to return something like a ``variant`` of a wrapped or an unwrapped state. To ease this, the library provides the functions ``call`` and ``skip``, that take the same arguments (an invocation of the next reducing function) and return the same type, but the latter does not actually call the reducing function. This may be clearer considering this improved implementation of ``filter``: .. code-block:: c++ template auto filter(Predicate pred) { return [=] (auto step) { return [=] (auto s, auto... ins) { return pred(input) ? call(step, s, ins...) : skip(step, s, ins...); }; }; }; Inspecting the state -------------------- We have seen how we can use ``state_data`` and ``state_unwrap`` and ``state_wrap`` to add our oun state to the computation without having special control flow for the first invocation of the reducing function. However, sometimes we may want to indeed do something different when we receive an unwrapped state, signaling that we have not yet done any work. One example is ``dedupe`` which removes duplicate consecutive elements from the sequence. On the first invocation, we know that the element is no duplicate, because there was none before, so we can call the next reducing function unconditionally. The library provides a ``with_state(s, fn1, fn2)`` that takes a state ``s`` and calls ``fn1`` or ``fn2`` depending on whether ``s`` is already wrapped or not. We can use it to implemente ``dedupe`` as follows: .. code-block:: c++ auto dedupe = [](auto step) { return [=](auto s, auto... is) { return with_state(s, [&](auto s) { auto next = std::make_tuple(is...); return wrap_state(step(state_unwrap(s), is...), next); }, [&](auto s) { auto next = std::make_tuple(is...); return next == state_wrapper_data(st) ? s : wrap_state(step(state_unwrap(st), is...), next); }); }; }; Variadics --------- Note how in the *reducing* functions above we are always receiving and passing the inputs using ``ins...``. This is because *transducers are variadic*, this is, an input maybe consist of none, one, or many elements. This allows for some interesting use-cases. Generators ~~~~~~~~~~ A transducer with no arguments can act as generator, that with every "pulse" it receives no input, but produces a value. For example, we can use a transducer to create a vector with 10 pseudo-random numbers like this: .. code-block:: c++ auto v = into(std::vector{}, zug::map(&std::rand) | zug::take(10)); assert(v.size() == 10); Note that if we did not use ``take(10)`` there, that function would never end. However, we may alternatively define an infinite lazy range of pseudo-random numbers using: .. code-block:: c++ auto s = zug::sequence(zug::map(&std::rand)); std::copy(s.begin(), s.end() + 10, ...); Zipping ~~~~~~~ Alternatively, we can use transducers to combine multiple sequences into one, for example, by pairing every two elements. When using ranges this can be slightly cumbersome, since every range must have one and only one value type, so the every element must be combined into a tuple. With transducers, the two elements can be passed to the next transducer to combine them however you want. For example, if we have two vectors of integers, we can produce a vector with the sum of every two elements like this: .. code-block:: c++ auto v1 = std::vector{ 1, 2, 3, 4}; auto v2 = std::vector{10, 20, 30, 40}; auto xf = zug::map(std::plus<>{}); auto r = zug::into(std::vector{}, xf, v1, v2); assert(r == {11, 22, 33, 44}); Of course, if what you want is, in fact, to generate tuples, the library has you covered: .. code-block:: c++ auto v1 = std::vector{ 1, 2, 3, 4}; auto v2 = std::vector{10, 20, 30, 40}; auto r = zug::into(std::vector>{}, zug::zip, v1, v2); assert(r == {{1, 10}, {2, 20}, {3, 30}, {4, 40}}); Or maybe you have tuples, and want to operate on the elements: .. code-block:: c++ auto v = std::vector>{ {1, 10}, {2, 20}, {3, 30}, {4, 40} }; auto xf = zug::unzip | zug::map(std::plus<>{}); auto r = zug::into(std::vector{}, xf, v); assert(r == {11, 22, 33, 44}); Performance ----------- After being amazed, or maybe horrified, by these castles of lambda functions, you shall be wondering: *but how the heck does this perform?* The short answer is: **very well**. Most of the time, a combination of transducers can be as fast as a hand-rolled loop that performs the transformation. At every step, a transducer is just a simple function and it is very easy to inline. In fact, when transforming a range eagerly it is normally more efficient than equivalent C++20 range adaptor, since there is no intermediate chaining of polling iterators, instead, the data is passed directly through a chain of reducing functions. However, some things might be slightly slower, and using ``zug::sequence`` to create a lazy iterator based on a transducer can often be slower than a range adaptor. The comparison is kind of apple to oranges, since they are different abstractions. In fact, while indeed transducers can be convenient to express transformations over containers, they can also express, with minimal overhead, transformations over push-based temporal sequences. This is something that is simply impossible with the standard library. Check `Lager`_ for an application of this ability of transducers. .. _Lager: https://github.com/arximboldi/lager .. admonition:: Contribute! These conclussions are based on ad-hoc inspection of the generated assembly and benchmarking done during the development of the library. Sadly, there is no sistematic benchmark suite included with the library. Of course, developing one would be a good learning exercise, `contact us`_ if you would like to contribute one! .. _contact us: https://github.com/arximboldi/zug zug-0.1.1/doc/doxygen.config000066400000000000000000000011041451275774000157370ustar00rootroot00000000000000PROJECT_NAME = "zug" OUTPUT_DIRECTORY = _doxygen GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO GENERATE_HTML = NO GENERATE_XML = YES INPUT = \ ../zug \ ../zug/transducer \ ../zug/reducing \ ../zug/meta INCLUDE_PATH = .. QUIET = YES JAVADOC_AUTOBRIEF = YES ENABLE_PREPROCESSING = YES MARKDOWN_SUPPORT = YES MACRO_EXPANSION = YES ALIASES = "rst=\verbatim embed:rst:leading-asterisk\n" ALIASES += "endrst=\n\endverbatim" ALIASES += "rst{1}=\rst\1\endrst" zug-0.1.1/doc/index.rst000066400000000000000000000005411451275774000147400ustar00rootroot00000000000000 .. include:: ../README.rst :end-before: include:index/end Contents -------- .. toctree:: :caption: User Manual :maxdepth: 3 introduction design transducer reducing processes type-erasure state .. toctree:: :caption: Extras :maxdepth: 3 meta util ---- * :ref:`genindex` * :ref:`modindex` * :ref:`search` zug-0.1.1/doc/introduction.rst000066400000000000000000000001431451275774000163500ustar00rootroot00000000000000 Introduction ============ .. include:: ../README.rst :start-after: include:introduction/start zug-0.1.1/doc/meta.rst000066400000000000000000000000651451275774000145600ustar00rootroot00000000000000 Meta ==== .. doxygengroup:: meta :content-only: zug-0.1.1/doc/processes.rst000066400000000000000000000024411451275774000156400ustar00rootroot00000000000000.. _process: Processes ========= A transducer in itself describes a transformation between abstract sequences, but it is completely agnostic of the physical or temporal nature of that sequence. We call a *process* the mechanism that actually feeds data in and out out a transducer. In this section, we describe several *processes* provided by the library. A few of them are processes for processing C++ iterator based sequences and ranges. Some, like `reductor`, may help implementing your own processes. For an example of a third-party library that implements its own transducible components, check the cursors module in Lager_. .. _Lager: https://sinusoid.es/lager reduce ------ .. doxygenfunction:: reduce reduce_nested ------------- .. doxygenfunction:: reduce_nested transduce --------- .. doxygenfunction:: transduce run --- .. doxygenfunction:: run into ---- .. doxygenfunction:: into .. doxygenfunction:: into_vector sequence -------- .. doxygengroup:: sequence :content-only: reductor -------- Reductors encapsulate a *reducing function* and its *reduction state* in a more object oriented interface. It can be useful when implementing your own processes that supports transducers. ---- .. doxygengroup:: reductor :content-only: .. doxygenstruct:: zug::reductor_base :members: zug-0.1.1/doc/reducing.rst000066400000000000000000000007151451275774000154340ustar00rootroot00000000000000 Reducing functions ================== As we say in the :ref:`design` section, a transducer in itself is agnostic of any actual sequence. To use a transducer, you pass it a *reducing function*, to obtain a new reducing function that can then be used in a :ref:`process`. output ------ .. doxygenvariable:: output first ----- .. doxygenvariable:: first last ----- .. doxygenvariable:: last emplacing_back -------------- .. doxygenvariable:: emplacing_back zug-0.1.1/doc/sphinx-html-hack.bash000077500000000000000000000060361451275774000171250ustar00rootroot00000000000000#!/bin/bash location=`dirname $0` echo "Running $0 at $location" # Fixes issues described here among others # https://github.com/michaeljones/breathe/issues/284 fix-missing-class-name() { src='\(.*\)class ' dst='\2class\1' sed -i "s@$src@$dst@g" $location/_build/html/*.html } fix-missing-struct-name() { src='\(.*\)struct ' dst='\2struct\1' sed -i "s@$src@$dst@g" $location/_build/html/*.html } fix-double-using-keyword() { src='usingusing ' dst='using ' sed -i "s@$src@$dst@g" $location/_build/html/*.html } fix-do-not-repeat-type-in-member-using-declaration() { src='using \(\([^:]*::\)*\)\([^ ]*\) = \([^<]*\)' dst='using \3 = \4' sed -i "s@$src@$dst@g" $location/_build/html/*.html } fix-do-not-repeat-type-in-member-using-declaration fix-remove-double-class-name() { # src='\([^&]*\)<\([^&]*\)>::' # dst='\1::' src='\([^<]*\)' dst='' sed -i "s@$src@$dst@g" $location/_build/html/*.html } fix-remove-straneous-typedefs() { src='typedef ' dst='' sed -i "s@$src@$dst@g" $location/_build/html/*.html } fix-remove-straneous-typedefs-2() { src='= typedef ' dst='= ' sed -i "s@$src@$dst@g" $location/_build/html/*.html } fix-remove-straneous-typedefs-2 fix-remove-straneous-using-declarations() { src='using template<>
' dst='' sed -i "s@$src@$dst@g" $location/_build/html/*.html } fix-remove-straneous-template-in-using-declarations-1() { src='\(
\n]*>\)\ntemplate<>
' dst='\1' pre=':a;N;$!ba;' sed -i "$pre;s@$src@$dst@g" $location/_build/html/*.html } fix-remove-straneous-template-in-using-declarations-1 fix-remove-straneous-template-in-using-declarations-2() { src='>template<>
`` allows us to take any function or callable object, regardless of its type, as long as they have a compatible signature. Similarly, this library provides a type ``transducer`` that can hold any transducer that can take and produce inputs and outputs of the given types. .. _type erasure: https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Type_Erasure ---- .. doxygenclass:: zug::transducer zug-0.1.1/doc/util.rst000066400000000000000000000000761451275774000146110ustar00rootroot00000000000000Utilities ========= .. doxygengroup:: util :content-only: zug-0.1.1/example/000077500000000000000000000000001451275774000137655ustar00rootroot00000000000000zug-0.1.1/example/CMakeLists.txt000066400000000000000000000001331451275774000165220ustar00rootroot00000000000000 add_custom_target(examples COMMENT "Build all examples") add_dependencies(check examples) zug-0.1.1/nix/000077500000000000000000000000001451275774000131305ustar00rootroot00000000000000zug-0.1.1/nix/docs.nix000066400000000000000000000013771451275774000146100ustar00rootroot00000000000000{ nixpkgs ? }: with import nixpkgs {}; rec { breathe = with python27Packages; buildPythonPackage rec { version = "git-arximboldi-${commit}"; pname = "breathe"; name = "${pname}-${version}"; commit = "5074aecb5ad37bb70f50216eaa01d03a375801ec"; src = fetchFromGitHub { owner = "arximboldi"; repo = "breathe"; rev = commit; sha256 = "10kkh3wb0ggyxx1a7x50aklhhw0cq269g3jddf2gb3pv9gpbj7sa"; }; propagatedBuildInputs = [ docutils sphinx ]; meta = with stdenv.lib; { homepage = https://github.com/michaeljones/breathe; license = licenses.bsd3; description = "Sphinx Doxygen renderer"; inherit (sphinx.meta) platforms; }; }; recommonmark = python27Packages.recommonmark; } zug-0.1.1/shell.nix000066400000000000000000000027661451275774000141740ustar00rootroot00000000000000{ compiler ? "", nixpkgs ? (import {}).fetchFromGitHub { owner = "NixOS"; repo = "nixpkgs"; rev = "31cd1b4afbaf0b1e81272ee9c31d1ab606503aed"; sha256 = "1hd48vkdrj8rgihrkjfhr3amqlf3phjshqyylslb836n6vlp18pp"; }}: with import nixpkgs {}; let # For the documentation tools we use an older Nixpkgs since the # newer versions seem to be not working great... old-nixpkgs-src = fetchFromGitHub { owner = "NixOS"; repo = "nixpkgs"; rev = "d0d905668c010b65795b57afdf7f0360aac6245b"; sha256 = "1kqxfmsik1s1jsmim20n5l4kq6wq8743h5h17igfxxbbwwqry88l"; }; old-nixpkgs = import old-nixpkgs-src {}; docs = import ./nix/docs.nix { nixpkgs = old-nixpkgs-src; }; compiler-pkg = if compiler != "" then pkgs.${compiler} else stdenv.cc; the-stdenv = if compiler-pkg.isClang then clangStdenv else stdenv; in the-stdenv.mkDerivation rec { name = "zug-env"; buildInputs = [ cmake boost catch2 ccache old-nixpkgs.doxygen (old-nixpkgs.python.withPackages (ps: [ ps.sphinx docs.breathe docs.recommonmark ])) ]; shellHook = '' export ZUG_ROOT=`dirname ${toString ./shell.nix}` addToSearchPath PATH "$ZUG_ROOT/build" addToSearchPath PATH "$ZUG_ROOT/build/example" addToSearchPath PATH "$ZUG_ROOT/build/test" ''; } zug-0.1.1/test/000077500000000000000000000000001451275774000133115ustar00rootroot00000000000000zug-0.1.1/test/CMakeLists.txt000066400000000000000000000023521451275774000160530ustar00rootroot00000000000000# Utils # ===== function(zug_target_name_for out_target out_file file) get_filename_component(_extension ${_file} EXT) file(RELATIVE_PATH _relative ${CMAKE_CURRENT_SOURCE_DIR} ${file}) string(REPLACE "${_extension}" "" _name ${_relative}) string(REGEX REPLACE "/" "-" _name ${_name}) set(${out_target} "test-${_name}" PARENT_SCOPE) string(REPLACE "${_extension}" "" _name ${_relative}) string(REGEX REPLACE "/" "-" _name ${_name}) set(${out_file} "${_name}" PARENT_SCOPE) endfunction() # Targets # ======= add_custom_target(tests COMMENT "Build all the unit tests.") add_dependencies(check tests) add_subdirectory(__inline_var_tests) file(GLOB_RECURSE zug_unit_tests "*.cpp") list(FILTER zug_unit_tests EXCLUDE REGEX "${CURRENT_CMAKE_DIRECTORY}/__*") foreach(_file IN LISTS zug_unit_tests) message("found unit test: " ${_file}) zug_target_name_for(_target _output "${_file}") add_executable(${_target} EXCLUDE_FROM_ALL "${_file}") set_target_properties(${_target} PROPERTIES OUTPUT_NAME ${_output}) add_dependencies(tests ${_target}) target_compile_definitions(${_target} PUBLIC CATCH_CONFIG_MAIN) target_link_libraries(${_target} PUBLIC zug-dev Catch2::Catch2) add_test("test/${_output}" ${_output}) endforeach() zug-0.1.1/test/__inline_var_tests/000077500000000000000000000000001451275774000171575ustar00rootroot00000000000000zug-0.1.1/test/__inline_var_tests/CMakeLists.txt000066400000000000000000000005261451275774000217220ustar00rootroot00000000000000 # Targets # ======= message("adding unit test: inline_var_tests") file(GLOB_RECURSE inline_var_test_files "*.cpp") add_executable(inline_var_tests EXCLUDE_FROM_ALL ${inline_var_test_files}) add_dependencies(tests inline_var_tests) target_link_libraries(inline_var_tests PUBLIC zug-dev) add_test("test/inline_var_tests" inline_var_tests) zug-0.1.1/test/__inline_var_tests/inline_var.cpp000066400000000000000000000011241451275774000220070ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente (and Carl Bussey, maybe?) // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include "resources/client1.hpp" #include "resources/client2.hpp" #include #include using namespace zug::detail; TEST_CASE("inline_var: variable included in two files has same address") { CHECK(client1::address_of_inline_var() == client2::address_of_inline_var()); } zug-0.1.1/test/__inline_var_tests/main.cpp000066400000000000000000000000661451275774000206110ustar00rootroot00000000000000#define CATCH_CONFIG_MAIN #include zug-0.1.1/test/__inline_var_tests/resources/000077500000000000000000000000001451275774000211715ustar00rootroot00000000000000zug-0.1.1/test/__inline_var_tests/resources/client1.cpp000066400000000000000000000010101451275774000232240ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente (and Carl Bussey, maybe?) // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include "client1.hpp" #include namespace zug { namespace detail { namespace client1 { const to_be_inlined* address_of_inline_var() { return std::addressof(inline_var); } } // namespace client1 } // namespace detail } // namespace zug zug-0.1.1/test/__inline_var_tests/resources/client1.hpp000066400000000000000000000007261451275774000232460ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente (and Carl Bussey, maybe?) // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include "decl.hpp" namespace zug { namespace detail { namespace client1 { const to_be_inlined* address_of_inline_var(); } // namespace client1 } // namespace detail } // namespace zug zug-0.1.1/test/__inline_var_tests/resources/client2.cpp000066400000000000000000000010101451275774000232250ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente (and Carl Bussey, maybe?) // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include "client2.hpp" #include namespace zug { namespace detail { namespace client2 { const to_be_inlined* address_of_inline_var() { return std::addressof(inline_var); } } // namespace client2 } // namespace detail } // namespace zug zug-0.1.1/test/__inline_var_tests/resources/client2.hpp000066400000000000000000000007261451275774000232470ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente (and Carl Bussey, maybe?) // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include "decl.hpp" namespace zug { namespace detail { namespace client2 { const to_be_inlined* address_of_inline_var(); } // namespace client2 } // namespace detail } // namespace zug zug-0.1.1/test/__inline_var_tests/resources/decl.hpp000066400000000000000000000007461451275774000226200ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente (and Carl Bussey, maybe?) // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include namespace zug { namespace detail { struct to_be_inlined {}; ZUG_INLINE_CONSTEXPR auto inline_var = to_be_inlined{}; } // namespace detail } // namespace zug zug-0.1.1/test/compose.cpp000066400000000000000000000073541451275774000154730ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include using namespace zug; auto add_one = [](int x) { return x + 1; }; auto mult_five = [](int x) { return x * 5; }; auto fst = [](auto&& x) { return x[0]; }; auto divide = [](int x, int y) { return x / y; }; TEST_CASE("comp: call composed functions returns output of composition") { auto result = comp(add_one, add_one, add_one)(5); CHECK(result == 8); } TEST_CASE("comp: execution order is from right to left function") { SECTION("add_one(mult_five(x))") { auto result = comp(add_one, mult_five)(10); CHECK(result == 51); } SECTION("mult_five(add_one(x))") { auto result = comp(mult_five, add_one)(10); CHECK(result == 55); } } TEST_CASE("comp: supports transformation between input and output type") { static_assert( std::is_same< std::result_of_t)>, int>::value, "comp should support transformation from array to int"); auto result = comp(add_one, fst)(std::array{{1, 2, 3, 4}}); CHECK(result == 2); } TEST_CASE("comp: supports multiple input arguments") { auto result = comp(add_one, divide)(10, 2); CHECK(result == 6); } TEST_CASE("comp: supports single function with multiple args") { auto result = comp(divide)(10, 2); CHECK(result == 5); } TEST_CASE("comp: comp a comp") { static_assert( std::is_same< decltype(comp(comp(add_one, add_one), divide)), composed>:: value, "comping a comp produces a flattened composed"); auto result = comp(comp(add_one, add_one), divide)(12, 4); CHECK(result == 5); } TEST_CASE("comp: comp two comps") { static_assert( std::is_same>::value, "comping two comps produces a flattened composed"); auto result = comp(comp(add_one), comp(add_one))(10); CHECK(result == 12); } TEST_CASE("comp: comp an lvalue comp") { auto comped = comp(add_one); auto result = comp(comped, comp(add_one))(10); CHECK(result == 12); } TEST_CASE("comp: comp a const lvalue comp") { const auto comped = comp(add_one); auto result = comp(comped, comped)(10); CHECK(result == 12); } auto c_add_one = comp([](int x) { return x + 1; }); auto c_mult_five = comp([](int x) { return x * 5; }); TEST_CASE("operator|: call composed functions returns output of composition") { auto result = (c_add_one | c_add_one)(10); CHECK(result == 12); } TEST_CASE("operator|: execution order is from right to left function") { SECTION("add_one(mult_five(x))") { auto result = (c_add_one | c_mult_five)(10); CHECK(result == 51); } SECTION("mult_five(add_one(x))") { auto result = (c_mult_five | c_add_one)(10); CHECK(result == 55); } } TEST_CASE("operator|: composable non-composable type") { auto composable = comp([](auto x) { return x + 1; }); auto non_composable = [](auto x) { return x + 1; }; SECTION("composable | non-composable") { auto result = (composable | non_composable)(10); CHECK(result == 12); } SECTION("non-composable | composable") { auto result = (non_composable | composable)(10); CHECK(result == 12); } } zug-0.1.1/test/into.cpp000066400000000000000000000031521451275774000147670ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include using namespace zug; TEST_CASE("into, mutation") { auto v = std::vector{1, 2, 3}; auto res = std::vector{}; auto& res2 = into(res, identity, v); CHECK(res == (std::vector{1, 2, 3})); CHECK(&res == &res2); } TEST_CASE("into, non mutation") { auto v = std::vector{1, 2, 3}; auto res = into(std::vector{}, identity, v); CHECK(res == (std::vector{1, 2, 3})); } TEST_CASE("into, appends") { auto v = std::vector{1, 2, 3}; auto res = into(std::vector{0}, identity, v); CHECK(res == (std::vector{0, 1, 2, 3})); } TEST_CASE("into, transduction") { auto v = std::vector{1, 2, 3, 4}; auto res = into(std::vector{}, comp(filter([](int x) { return x % 2 == 0; }), map([](int x) { return std::to_string(x); })), v); CHECK(res == (std::vector{"2", "4"})); } TEST_CASE("into, zipping") { auto v1 = std::vector{1, 2, 3, 4}; auto v2 = std::vector{"a", "b"}; using tup = std::tuple; auto res = into(std::vector{}, identity, v1, v2); CHECK(res == (std::vector{tup(1, "a"), tup(2, "b")})); } zug-0.1.1/test/into_vector.cpp000066400000000000000000000047571451275774000163650ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include using namespace zug; namespace test_detail { struct to_string_t { std::string operator()(int x) const; }; struct stoi_t { int operator()(std::string x) const; }; } // namespace test_detail TEST_CASE("into_vector, type deduction") { using namespace test_detail; static_assert( std::is_same, decltype(into_vector(identity, std::vector{}))>{}, ""); static_assert( std::is_same>, decltype(into_vector(identity, std::vector{}, std::vector{}))>{}, ""); static_assert(std::is_same, decltype(into_vector(map(to_string_t{}), std::vector{}))>{}, ""); static_assert( std::is_same, decltype(into_vector(map(stoi_t{}), std::vector{}))>{}, ""); } TEST_CASE("into_vector, zipping") { auto v1 = std::vector{1, 2, 3, 4}; auto v2 = std::vector{"a", "b"}; using tup = std::tuple; auto res = into_vector(identity, v1, v2); CHECK(res == (std::vector{tup(1, "a"), tup(2, "b")})); } TEST_CASE("into_vector, transduction") { auto v = std::vector{1, 2, 3, 4}; auto xform = comp(filter([](int x) { return x % 2 == 0; }), map([](int x) { return std::to_string(x); })); auto res = into_vector(xform, v); CHECK(res == (std::vector{"2", "4"})); } TEST_CASE("into_vector, type erasure") { auto v = std::vector{1, 2, 3, 4}; auto xform = transducer{ comp(filter([](int x) { return x % 2 == 0; }), map([](int x) { return std::to_string(x); }))}; auto res = into_vector(xform, v); CHECK(res == (std::vector{"2", "4"})); } zug-0.1.1/test/meta.cpp000066400000000000000000000037541451275774000147540ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include using namespace zug; TEST_CASE("output_of, identity") { using meta::pack; using identity_t = decltype(identity); static_assert(output_of_t{} == pack<>{}, ""); static_assert(output_of_t{} == pack{}, ""); static_assert( output_of_t{} == pack{}, ""); static_assert(output_of_t>{} == pack{}, ""); static_assert(output_of_t{} == pack{}, ""); static_assert(output_of_t{} == pack{}, ""); static_assert(output_of_t{} == pack{}, ""); static_assert(output_of_t{} == pack{}, ""); } TEST_CASE("result_of, identity") { using meta::pack; using identity_t = decltype(identity); static_assert(pack>{} == pack>{}, ""); static_assert(pack>{} == pack{}, ""); static_assert(pack>{} == pack>{}, ""); static_assert(pack>>{} == pack>{}, ""); static_assert(pack>{} == pack{}, ""); static_assert(pack>{} == pack>{}, ""); } zug-0.1.1/test/reduce.cpp000066400000000000000000000044201451275774000152640ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include "spies.hpp" TEST_CASE("reduce protects against moved self assignment") { auto v = std::vector{1, 2, 3, 6}; CHECK(zug::reduce(zug::first, v, v) == v); } namespace { struct non_default { non_default() = delete; explicit non_default(int) {} non_default(const non_default&) = default; non_default(non_default&&) = default; non_default& operator=(const non_default&) = default; non_default& operator=(non_default&&) = default; }; struct foo_tag {}; template constexpr auto foo(T step) { return [=](auto&& s, auto&&... is) { return zug::wrap_state( step(zug::state_unwrap(ZUG_FWD(s)), ZUG_FWD(is)...), non_default{42}); }; } } // namespace TEST_CASE("reduce does not construct state wrapper from state") { auto v = std::vector{1, 2, 3, 6}; auto r = zug::reduce(foo(zug::first), 13, v); CHECK(r == 13); } TEST_CASE("reduce copies values out of lvalue container") { using elem = testing::copy_spy<>; auto x = elem{}; auto v = std::vector{x, x, x, x}; auto copies = x.copied.count(); zug::reduce(zug::last, x, v); CHECK(x.copied.count() == copies + 4); } TEST_CASE("reduce moves values out of rvalue container") { using elem = testing::copy_spy<>; auto x = elem{}; auto v = std::vector{x, x, x, x}; auto copies = x.copied.count(); zug::reduce(zug::last, std::move(x), std::move(v)); CHECK(x.copied.count() == copies); } TEST_CASE("moves values out of rvalue container variadic") { using elem = testing::copy_spy<>; auto x = elem{}; auto v1 = std::vector{x, x, x, x}; auto v2 = v1; auto init = std::make_tuple(x, x); auto copies = x.copied.count(); zug::reduce(zug::last, std::move(init), std::move(v1), std::move(v2)); CHECK(x.copied.count() == copies); } zug-0.1.1/test/reductor.cpp000066400000000000000000000055111451275774000156460ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include #include "spies.hpp" using namespace zug; TEST_CASE("reductor, reductor") { auto r = make_reductor(std::plus<>{}, 0, 1); r(2); r(3); r(4); CHECK(r.complete() == 10); } TEST_CASE("reductor, empty reductor") { auto r = make_empty_reductor(std::plus<>{}, 0); r(2); r(3); r(4); CHECK(r.complete() == 9); } TEST_CASE("reductor, reductor no move") { auto r = make_reductor(std::plus<>{}, std::string{}, ""); r("hello"); r(" "); r("world"); CHECK(r.complete() == "hello world"); CHECK(r.complete() == "hello world"); } TEST_CASE("reductor, reductor chaining") { auto result = make_reductor(std::plus<>{}, std::string{}, "")("hello")(" ")( "my")(" ")("friend")("!") .complete(); CHECK(result == "hello my friend!"); } TEST_CASE("reductor, reductor move") { auto s = testing::copy_spy<>{}; auto r = make_reductor(first, std::move(s), 0); r(1); r(2); r(3); auto c = std::move(r).complete(); CHECK(c.copied.count() == 0); } TEST_CASE("reductor, generator") { auto r = make_reductor(enumerate(last), -1); CHECK(r.complete() == 0); r(); CHECK(r.complete() == 1); r(); CHECK(r.complete() == 2); } TEST_CASE("reductor, generator empty") { auto r = make_empty_reductor(enumerate(last), std::size_t{42}); CHECK(r.complete() == 42u); r(); CHECK(r.complete() == 0u); r(); CHECK(r.complete() == 1u); } TEST_CASE("reductor, termination") { auto r = make_reductor(take(4)(enumerate(last)), 0); CHECK(r()); CHECK(r()); CHECK(!r()); CHECK(!r); } TEST_CASE("reductor, termination empty") { auto r = make_empty_reductor(take(4)(enumerate(last)), 0); CHECK(r()); CHECK(r()); CHECK(r()); CHECK(!r()); CHECK(!r); } TEST_CASE("reductor, current") { auto r = make_empty_reductor(take(4)(enumerate(last)), std::size_t{0}); CHECK(r()); CHECK(r.current() == 0); r.current(std::size_t{10}); CHECK(r.current() == 10); CHECK(r()); CHECK(r.current() == 1); CHECK(r()); CHECK(r.current() == 2); CHECK(!r()); CHECK(!r); } TEST_CASE("reductor, constant") { const auto r1 = make_reductor(enumerate(last), 0); const auto r2 = r1(); const auto r2_ = r1(); const auto r3 = r2(); CHECK(r2.complete() == 1); CHECK(r2_.complete() == 1); CHECK(r3.complete() == 2); } zug-0.1.1/test/run.cpp000066400000000000000000000022211451275774000146160ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include #include #include using namespace zug; TEST_CASE("run, nullary") { auto input = std::stringstream{"1 2 3 4 5"}; auto output = std::stringstream{}; run(comp(read(input), write(output, ';'))); CHECK(output.str() == "1;2;3;4;5"); } TEST_CASE("run, unary") { auto v = std::vector{1, 2, 3, 4, 5}; auto output = std::stringstream{}; run(write(output, ';'), v); CHECK(output.str() == "1;2;3;4;5"); } TEST_CASE("run, variadic") { auto v1 = std::vector{1, 2, 3, 4, 5}; auto v2 = std::vector{'h', 'e', 'l', 'l', 'o'}; auto output = std::stringstream{}; run(write(output, ';', ' '), v1, v2); CHECK(output.str() == "1 h;2 e;3 l;4 l;5 o"); } zug-0.1.1/test/sequence.cpp000066400000000000000000000061301451275774000156250ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include #include #include #include #include using namespace zug; namespace { template void my_copy(RangeT&& range, OutputIt d_first) { std::copy(std::begin(range), std::end(range), d_first); } } // namespace TEST_CASE("sequence, identity") { auto v = std::vector{1, 2, 3, 4}; auto r = std::vector{}; auto seq = sequence(identity, v); my_copy(seq, std::back_inserter(r)); CHECK(r == v); } TEST_CASE("sequence, filter_map") { auto v = std::vector{1, 2, 3, 4}; auto r = std::vector{}; auto seq = sequence(comp(filter([](int x) { return x % 2 == 0; }), map([](int x) { return x * 2; })), v); my_copy(seq, std::back_inserter(r)); CHECK(r == (std::vector{{4, 8}})); } TEST_CASE("sequence, rvalues") { auto r = std::vector{}; my_copy(sequence(identity, std::vector{1, 2, 3, 4}), std::back_inserter(r)); CHECK(r == (std::vector{1, 2, 3, 4})); } TEST_CASE("sequence, constant") { const auto v = std::vector{1, 2, 3, 4}; auto r = std::vector{}; my_copy(sequence(identity, v), std::back_inserter(r)); CHECK(r == v); } TEST_CASE("sequence, no shared iterator state") { const auto v = std::vector{1, 2, 3, 4}; auto seq = sequence(take(3), v); auto f2 = seq.begin(); auto e = seq.end(); auto f1 = f2++; auto r1 = std::vector{}; std::copy(f1, e, std::back_inserter(r1)); CHECK(r1 == (decltype(r1){1, 2, 3})); auto r2 = std::vector{}; std::copy(f2, e, std::back_inserter(r2)); CHECK(r2 == (decltype(r2){2, 3})); } TEST_CASE("sequence, empty") { const auto v = std::vector{}; auto seq = sequence(take(3), v); auto r = std::vector{}; my_copy(seq, std::back_inserter(r)); CHECK(r == (decltype(r){})); } TEST_CASE("sequence, variadic growing no type deduction") { using res_t = ZUG_VARIANT; const auto v1 = std::vector{1, 2, 3, 4}; const auto v2 = std::vector{"a", "b", "c"}; auto seq = sequence(interleave, v1, v2); auto r = std::vector{}; my_copy(seq, std::back_inserter(r)); CHECK( r == (decltype(r){ res_t{1}, res_t{"a"}, res_t{2}, res_t{"b"}, res_t{3}, res_t{"c"}})); } TEST_CASE("sequence, generator") { auto seq = sequence(enumerate); auto r = std::vector{}; std::copy_n(seq.begin(), 4u, std::back_inserter(r)); CHECK(r == (decltype(r){0u, 1u, 2u, 3u})); } zug-0.1.1/test/spies.hpp000066400000000000000000000112261451275774000151470ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include namespace testing { namespace mocks { template struct defaulting { template T operator()(Args&&...) { return T(); } }; template struct returning { returning() = default; template returning(const Fn& mock) : mock_(mock) {} template T operator()(Args&&...) { return mock_(); } private: std::function mock_; }; } // namespace mocks namespace detail { class spy_base { public: std::size_t count() const { return *count_; } void called() { ++*count_; } private: std::shared_ptr count_ = std::make_shared(0); }; } // namespace detail /*! * Functor that counts the number of times it was called. * * @todo Support comparing the actual arguments. Keep generic * interface using boost::any */ template > class spy_fn : public detail::spy_base { MockT mock_; public: spy_fn() = default; template spy_fn(MockT2 mock) : mock_(std::move(mock)) {} template spy_fn(MockT2 mock, const spy_base& spy) : spy_base(spy) , mock_(std::move(mock)) {} template decltype(auto) operator()(Args&&... args) { called(); return this->mock_(std::forward(args)...); } }; /*! * Returns a spy object that uses fn as mock implementation. */ template inline auto spy(const Fn& fn) -> spy_fn { return spy_fn(fn); } /*! * Returns a spy object with a no-op mock implementation. */ inline auto spy() -> spy_fn<> { return spy_fn<>(); } namespace detail { template class scoped_intruder { MockT* mock_; MockT original_; public: scoped_intruder& operator=(const scoped_intruder&) = delete; scoped_intruder(const scoped_intruder&) = delete; scoped_intruder(scoped_intruder&& other) { swap(*this, other); } scoped_intruder& operator=(scoped_intruder&& other) { if (this != &other) { swap(*this, other); } } scoped_intruder(MockT& mock, MockT replacement) : mock_(&mock) , original_(mock) { *mock_ = replacement; } ~scoped_intruder() { if (mock_) { *mock_ = original_; } } template auto operator()(Args&&... args) -> decltype((*mock_)(std::forward(args)...)) { assert(mock_ && "must not call intruder after having moved from it"); return (*mock_)(std::forward(args)...); } friend void swap(scoped_intruder& a, scoped_intruder& b) { using std::swap; swap(a.mock_, b.mock_); swap(a.original_, b.original_); } }; } // namespace detail /*! * Given a functor object `mock` of a general functor with type erasure * (e.g. std::function or boost::function) installs a spy that counts * the calls and returns such a spy. */ template inline auto spy_on(MockT& mock) -> spy_fn> { auto s = spy(mock); return {detail::scoped_intruder(mock, s), s}; } /*! * Like @a spy_on(), but it installs the `replacement` function instead * of keeping the original one. The spy is uninstalled on * destruction, and it is not copyable. */ template inline auto spy_on(MockT& mock, const FnT& replacement) -> spy_fn> { auto s = spy(replacement); return {detail::scoped_intruder(mock, s), s}; } namespace detail { struct default_copy_spy_base_t {}; } // namespace detail /*! * Utility for testing how many times an object is copied. */ template struct copy_spy : BaseT { using base_t = BaseT; spy_fn<> copied; copy_spy(BaseT base = {}) : BaseT{std::move(base)} {} copy_spy(copy_spy&&) = default; copy_spy& operator=(copy_spy&&) = default; copy_spy(const copy_spy& x) : base_t(x) , copied(x.copied) { copied(); } copy_spy& operator=(const copy_spy& x) { base_t::operator=(x); copied = x.copied; copied(); return *this; } }; } // namespace testing zug-0.1.1/test/state_traits.cpp000066400000000000000000000065251451275774000165330ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include #include #include #include #include using namespace zug; TEST_CASE("state_traits, unwrap all wrap state") { CHECK(state_unwrap_all(42) == 42); CHECK(state_unwrap_all(wrap_state(42, {})) == 42); CHECK(state_unwrap_all(wrap_state(wrap_state(42, {}), "")) == 42); } TEST_CASE("state_traits, unwrap all skip state") { auto odd = [](int x) { return x % 2; }; auto rf = comp(filter(odd), take(10))(last); auto s = rf(int{}, 41); CHECK(state_unwrap_all(s) == 41); s = rf(s, 13); CHECK(state_unwrap_all(s) == 13); } TEST_CASE("state_traits, unwrap all type erased") { auto rf = transducer{identity}(last); auto s = rf(int{}, 41); CHECK(state_unwrap_all(s) == 41); s = rf(s, 13); CHECK(state_unwrap_all(s) == 13); } TEST_CASE("state_traits, unwrap all complex") { auto odd = [](int x) { return x % 2; }; auto rf = transducer{comp(filter(odd), take(10))}(last); auto s = rf(int{}, 41); CHECK(state_unwrap_all(s) == 41); s = rf(s, 13); CHECK(state_unwrap_all(s) == 13); } TEST_CASE("state_traits, unwrap all moar complex") { auto odd = [](std::size_t x) { return x % 2; }; auto rf = transducer, std::size_t>{ comp(enumerate, filter(odd), take(10))}(last); auto s = rf(std::size_t{}); CHECK(state_unwrap_all(s) == 0); s = rf(s); CHECK(state_unwrap_all(s) == 1); s = rf(s); CHECK(state_unwrap_all(s) == 1); s = rf(s); CHECK(state_unwrap_all(s) == 3); } TEST_CASE("state_traits, rewrap wrap state") { struct t1 : meta::pack {}; struct t2 : meta::pack {}; CHECK(state_rewrap(42, 13) == 13); CHECK(state_rewrap(wrap_state(42, {}), 13) == wrap_state(13, {})); CHECK(state_rewrap(wrap_state(wrap_state(42, {}), ""), 13) == wrap_state(wrap_state(13, {}), "")); } TEST_CASE("state_traits, rewrap skip state") { auto odd = [](int x) { return x % 2; }; auto rf = comp(filter(odd), take(3))(last); auto s = rf(int{}, 41); s = state_rewrap(s, 13); CHECK(state_unwrap_all(s) == 13); s = rf(rf(rf(s, 1), 2), 3); CHECK(state_unwrap_all(s) == 3); CHECK(state_is_reduced(s)); } TEST_CASE("state_traits, rewrap type erased") { auto rf = transducer{identity}(last); auto s = rf(int{}, 41); s = state_rewrap(s, 13); CHECK(state_unwrap_all(s) == 13); } TEST_CASE("state_traits, rewrap moar complex") { auto odd = [](std::size_t x) { return x % 2; }; auto rf = transducer, std::size_t>{ comp(enumerate, filter(odd), take(10))}(last); auto s = rf(std::size_t{}); s = rf(s); s = state_rewrap(s, std::size_t{13}); CHECK(state_unwrap_all(s) == 13); s = rf(s); CHECK(state_unwrap_all(s) == 13); s = rf(s); CHECK(state_unwrap_all(s) == 3); } zug-0.1.1/test/transduce.cpp000066400000000000000000000022441451275774000160070ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include using namespace zug; TEST_CASE("transduce, identity") { auto v = std::vector{1, 2, 3, 6}; CHECK(transduce(identity, std::plus{}, 1, v) == 13); } TEST_CASE("transduce, variadic") { auto v1 = std::vector{1, 2, 3, 6}; auto v2 = std::vector{1, 2, 1, 2}; CHECK(transduce(map(std::multiplies{}), std::plus{}, 1, v1, v2) == 21); } TEST_CASE("transduce, variadic_different_lengths") { auto v1 = std::vector{1, 2, 3, 6}; auto v2 = std::vector{1, 2, 1}; CHECK(transduce(map(std::multiplies{}), std::plus{}, 1, v1, v2) == 9); } TEST_CASE("transduce, early termination does not leak") { auto v1 = {1, 2, 3, 4}; CHECK(transduce(take(2), std::plus{}, 0, v1) == 3); } zug-0.1.1/test/transducer/000077500000000000000000000000001451275774000154635ustar00rootroot00000000000000zug-0.1.1/test/transducer/cat.cpp000066400000000000000000000016411451275774000167400ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include using namespace zug; TEST_CASE("cat, cat") { // example1 { auto v = std::vector>{{1, 2}, {3}, {4, 5, 6}}; auto r = into_vector(cat, v); CHECK(r == (std::vector{1, 2, 3, 4, 5, 6})); // } } TEST_CASE("cat, multiple") { // example2 { auto v1 = std::vector>{{1, 2}, {3, 4}}; auto v2 = std::vector>{{'a'}, {'c', 'd'}}; auto r = into_vector(cat, v1, v2); using t = std::vector>; CHECK(r == (t{{1, 'a'}, {3, 'c'}, {4, 'd'}})); // } } zug-0.1.1/test/transducer/chain.cpp000066400000000000000000000037151451275774000172570ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include using namespace zug; TEST_CASE("chain, append") { // example1 { auto v1 = std::vector{1, 2}; auto v2 = std::vector{13, 42, 5}; auto res = into_vector(chain(v2), v1); CHECK(res == (std::vector{1, 2, 13, 42, 5})); // } } TEST_CASE("chain, variadic") { // example2 { auto v1 = std::vector{1, 2}; auto v2 = std::vector{13, 42, 5}; auto res = into_vector(chain(v2, v1), v1); CHECK(res == (std::vector{1, 2, 13, 42, 5, 1, 2})); // } } TEST_CASE("chain, no chaining if no input") { // example22 { // This is a limitation of the pure stateful transducer design auto v1 = std::vector{}; auto v2 = std::vector{13, 42, 5}; auto res = into_vector(chain(v2), v1); CHECK(res == (std::vector{})); // } } TEST_CASE("chainl, prepend") { // example3 { auto v1 = std::vector{1, 2}; auto v2 = std::vector{13, 42, 5}; auto res = into_vector(chainl(v2), v1); CHECK(res == (std::vector{13, 42, 5, 1, 2})); // } } TEST_CASE("chainl, variadic") { // example4 { auto v1 = std::vector{1, 2}; auto v2 = std::vector{13, 42, 5}; auto res = into_vector(chainl(v2, v1), v1); CHECK(res == (std::vector{1, 2, 13, 42, 5, 1, 2})); // } } TEST_CASE("chainl, no chaining if no input") { // example5 { // This is a limitation of the pure stateful transducer design auto v1 = std::vector{}; auto v2 = std::vector{13, 42, 5}; auto res = into_vector(chainl(v2), v1); CHECK(res == (std::vector{})); // } } zug-0.1.1/test/transducer/count.cpp000066400000000000000000000017421451275774000173230ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include using namespace zug; TEST_CASE("count, count") { // example1 { auto v = std::vector{13, 42, 5}; auto r = into_vector(count(), v); using t = std::vector>; CHECK(r == (t{{13, 0u}, {42, 1u}, {5, 2u}})); // } } TEST_CASE("count, generator") { auto res = into_vector(comp(count('a'), take(3))); CHECK(res == (std::vector{{'a', 'b', 'c'}})); } TEST_CASE("count, stepped") { // example2 { auto r = into_vector(count(1.0, 0.5) | take(5)); CHECK(r == (std::vector{{1.0, 1.5, 2.0, 2.5, 3.0}})); // } } zug-0.1.1/test/transducer/cycle.cpp000066400000000000000000000022041451275774000172640ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include using namespace zug; TEST_CASE("cycle, generator") { // example1 { auto v1 = std::vector{13, 42, 5, 6, 15}; auto v2 = std::vector{0, 1}; auto r = into_vector(cycle(v2), v1); using t = std::vector>; CHECK(r == (t{{13, 0}, {42, 1}, {5, 0}, {6, 1}, {15, 0}})); // } } TEST_CASE("cycle, variadic") { // example2 { auto v1 = std::vector{0, 1}; auto v2 = std::vector{"one", "two", "three"}; auto r = into_vector(cycle(v1, v2) | take(5)); using t = std::vector>; CHECK(r == (t{{0, "one"}, // {1, "two"}, {0, "three"}, {1, "one"}, {0, "two"}})); // } } zug-0.1.1/test/transducer/dedupe.cpp000066400000000000000000000017131451275774000174370ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include using namespace zug; TEST_CASE("dedupe, into") { // example1 { auto v = std::vector{1, 1, 2, 1, 1, 3, 2, 2, 2, 1}; auto res = into_vector(dedupe, v); CHECK(res == (decltype(res){1, 2, 1, 3, 2, 1})); // } } TEST_CASE("dedupe, variadic") { using tup = std::tuple; auto v = std::vector{1, 2, 1, 1, 1, 1, 1}; auto res = into_vector(comp(cycle(std::string("aabb")), dedupe), v); CHECK( res == (decltype(res){ tup(1, 'a'), tup(2, 'a'), tup(1, 'b'), tup(1, 'a'), tup(1, 'b')})); } zug-0.1.1/test/transducer/distinct.cpp000066400000000000000000000016761451275774000200220ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include using namespace zug; TEST_CASE("distinct, into") { // example1 { auto v = std::vector{1, 2, 1, 3, 2, 1}; auto res = into_vector(distinct, v); CHECK(res == (decltype(res){1, 2, 3})); // } } TEST_CASE("distinct, variadic") { using tup = std::tuple; auto v = std::vector{1, 2, 1, 3, 2, 1, 2}; auto res = into_vector(comp(cycle(std::string("ab")), distinct), v); CHECK( res == (decltype(res){ tup(1, 'a'), tup(2, 'b'), tup(3, 'b'), tup(2, 'a'), tup(1, 'b')})); } zug-0.1.1/test/transducer/drop.cpp000066400000000000000000000021771451275774000171420ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include using namespace zug; TEST_CASE("drop, none") { auto v = std::vector{1, 2, 3, 4, 5}; auto res = into_vector(drop(0), v); CHECK(res == (decltype(res){1, 2, 3, 4, 5})); } TEST_CASE("drop, some") { // example1 { auto v = std::vector{1, 2, 3, 4, 5}; auto res = into_vector(drop(2), v); CHECK(res == (decltype(res){3, 4, 5})); // } } TEST_CASE("drop, everything") { auto v = std::vector{1, 2, 3, 4, 5}; auto res = into_vector(drop(6), v); CHECK(res == (decltype(res){})); } TEST_CASE("drop, compose") { using namespace std::placeholders; auto v = std::vector{1, 2, 3, 4, 5}; auto res = into_vector(comp(drop(2), take(2)), v); CHECK(res == (decltype(res){3, 4})); } zug-0.1.1/test/transducer/drop_while.cpp000066400000000000000000000034101451275774000203210ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include using namespace zug; TEST_CASE("drop_while, into") { using namespace std::placeholders; auto v = std::vector{1, 2, 3, 4, 3}; { auto lt0 = std::bind(std::less<>{}, _1, 0); auto res = into_vector(drop_while(lt0), v); CHECK(res == (decltype(res){1, 2, 3, 4, 3})); } { auto lt4 = std::bind(std::less<>{}, _1, 4); auto res = into_vector(drop_while(lt4), v); CHECK(res == (decltype(res){4, 3})); } { auto lt5 = std::bind(std::less<>{}, _1, 5); auto res = into_vector(drop_while(lt5), v); CHECK(res == (decltype(res){})); } } TEST_CASE("drop_while, example") { // example1 { auto v = std::vector{1, 2, 3, 4, 3, 2, 1}; auto res = into_vector(drop_while([](int x) { return x < 4; }), v); CHECK(res == (decltype(res){4, 3, 2, 1})); // } } namespace { bool free_lt4(int x) { return x < 4; } } // namespace TEST_CASE("drop_while, invoke") { auto v = std::vector{1, 2, 3, 4, 3}; auto res = into_vector(drop_while(&free_lt4), v); CHECK(res == (decltype(res){4, 3})); } TEST_CASE("drop_while, compose") { using namespace std::placeholders; auto v = std::vector{1, 2, 3, 4, 5}; auto lt3 = std::bind(std::less<>{}, _1, 3); auto res = into_vector(comp(drop_while(lt3), take(2)), v); CHECK(res == (decltype(res){3, 4})); } zug-0.1.1/test/transducer/each.cpp000066400000000000000000000011321451275774000170640ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include using namespace zug; TEST_CASE("each, each") { // example1 { auto v = std::vector{1, 2, 3, 6}; auto r1 = std::vector{}; auto r2 = into_vector(each([&](int x) { r1.push_back(x); }), v); CHECK(v == r1); CHECK(v == r2); // } } zug-0.1.1/test/transducer/eager.cpp000066400000000000000000000036521451275774000172600ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include #include "../spies.hpp" using namespace zug; TEST_CASE("eager, into_vector") { auto v = std::vector{1, 2, 3, 4}; auto r = into_vector(eager(identity), v); CHECK(r == (decltype(r){1, 2, 3, 4})); } TEST_CASE("eager, sorted") { auto v = std::vector{6, 2, 1, 12, 3}; auto times2 = [](int x) { return x * 2; }; auto div3 = [](int x) { return x % 3 == 0; }; auto r = into(std::vector{}, comp(map(times2), sorted, filter(div3)), v); CHECK(r == (decltype(r){6, 12, 24})); } TEST_CASE("eager, sorted example") { // example1 { auto v = std::vector{6, 2, 1, 12, 3}; auto r = into_vector(sorted, v); CHECK(r == (decltype(r){1, 2, 3, 6, 12})); // } } TEST_CASE("eager, reversed") { auto v = std::vector{1, 2, 3, 6, 12}; auto times2 = [](int x) { return x * 2; }; auto div3 = [](int x) { return x % 3 == 0; }; auto r = into(std::vector{}, comp(map(times2), reversed, filter(div3)), v); CHECK(r == (decltype(r){24, 12, 6})); } TEST_CASE("eager, reversed example") { // example2 { auto v = std::vector{1, 2, 3, 4, 5}; auto r = into_vector(reversed, v); CHECK(r == (decltype(r){5, 4, 3, 2, 1})); // } } TEST_CASE("eager, moves_data_around") { using elem = testing::copy_spy<>; auto x = elem{}; auto v = std::vector{x, x, x, x}; auto copies = x.copied.count(); into_vector(reversed, std::move(v)); CHECK(x.copied.count() == copies); } zug-0.1.1/test/transducer/enumerate.cpp000066400000000000000000000014551451275774000201610ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include using namespace zug; TEST_CASE("enumerate, example") { // example1 { auto s = std::string{"hello"}; auto r = into_vector(enumerate, s); CHECK(r == decltype(r){{0, 'h'}, {1, 'e'}, {2, 'l'}, {3, 'l'}, {4, 'o'}}); // } } TEST_CASE("enumerate_from, example") { // example2 { auto s = std::string{"hello"}; auto r = into_vector(enumerate_from(2), s); CHECK(r == decltype(r){{2, 'h'}, {3, 'e'}, {4, 'l'}, {5, 'l'}, {6, 'o'}}); // } } zug-0.1.1/test/transducer/filter.cpp000066400000000000000000000114041451275774000174540ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include #include #include #include #include #include #include namespace { bool free_odd(int x) { return x % 2 == 0; } } // namespace using namespace zug; TEST_CASE("filter, example") { // example1 { auto v = std::vector{1, 2, 3, 4, 5, 6}; auto res = into_vector(filter([](int x) { return x % 2; }), v); CHECK(res == decltype(v){1, 3, 5}); // } } TEST_CASE("filter, invoke") { auto v = std::vector{1, 2, 3, 6}; auto res = transduce(filter(&free_odd), std::plus{}, 1, v); CHECK(res == 9); } TEST_CASE("filter, composition") { auto v = std::vector{1, 2, 3, 6}; auto times2 = [](int x) { return x * 2; }; auto odd = [](int x) { return x % 2 == 0; }; // transducers compose from left to right, this is equivalent to // Haskell-like expression: // // foldl (+) $ map times2 $ filter odd $ v // auto res = transduce(comp(filter(odd), map(times2)), std::plus{}, 1, v); CHECK(res == 17); } TEST_CASE("filter, operator| composition") { auto v = std::vector{1, 2, 3, 6}; auto times2 = [](int x) { return x * 2; }; auto odd = [](int x) { return x % 2 == 0; }; // transducers compose from left to right, this is equivalent to // Haskell-like expression: // // foldl (+) $ map times2 $ filter odd $ v // auto res = transduce(filter(odd) | map(times2), std::plus{}, 1, v); CHECK(res == 17); } TEST_CASE("filter, make sure inputs cant be doubly sinked") { auto orig = std::vector{{1, 2, 3}}; auto pred = [](std::vector x) { return x.size() > 2; }; auto v = orig; auto x = filter(pred)(last)(std::vector{}, std::move(v)); CHECK(x == orig); } TEST_CASE("filter, compose with stateful transducer") { auto v = std::vector{{1, 2, 3, 4, 5, 6}}; auto even = [](int x) { return x % 2 == 0; }; auto res = transduce(comp(filter(even), take(2)), std::plus{}, 1, v); CHECK(res == 7); } TEST_CASE("filter, compose with stateful transducer type erased") { auto v = std::vector{{1, 2, 3, 4, 5, 6}}; auto even = [](int x) { return x % 2 == 0; }; auto res = transduce( comp(filter(even), transducer(take(2))), std::plus{}, 1, v); CHECK(res == 7); } TEST_CASE("filter, compose type erased with stateful transducer") { auto v = std::vector{{1, 2, 3, 4, 5, 6}}; auto even = [](int x) { return x % 2 == 0; }; auto res = transduce( comp(transducer(filter(even)), take(2)), std::plus{}, 1, v); CHECK(res == 7); } TEST_CASE("filter, type erase and compose with stateful transducer") { auto v = std::vector{{1, 2, 3, 4, 5, 6}}; auto even = [](int x) { return x % 2 == 0; }; auto res = transduce( transducer{comp(filter(even), take(2))}, std::plus{}, 1, v); CHECK(res == 7); } TEST_CASE("filter, type erasure triple mortal back flip") { auto even = [](int x) { return x % 2 == 0; }; auto gt3 = [](int x) { return x > 3; }; auto lt8 = [](int x) { return x < 8; }; auto plus1 = [](int x) { return x + 1; }; auto times2 = [](int x) { return x * 2; }; auto longer3 = [](std::vector x) { return x.size() > 3u; }; auto fstlt10 = [](std::vector x) { return !x.empty() && x[0] < 10; }; auto v = std::vector{{1, 2, 3, 4, 5, 6, 7, 8, 9}}; auto xform = transducer{ comp(comp(take_while(lt8), partition(3u), take(10), mapcat(plus1), filter(even), comp(transducer{map(times2)}, filter(gt3))), partition(6u), transducer, int>{ comp(transducer>{filter(longer3)}, take_while(fstlt10), cat, transducer{take(2)})}, filter(gt3), comp(take(5), transducer{map(times2)}, filter(even)))}; { auto res = transduce(xform, std::plus{}, 1, v); CHECK(res == 25); } { auto res = into_vector(xform, v); CHECK(res == (decltype(res){8, 16})); } } zug-0.1.1/test/transducer/interelave.cpp000066400000000000000000000016321451275774000203270ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include using namespace zug; TEST_CASE("interleave, interleave") { auto v1 = std::vector{{1, 2, 3}}; auto v2 = std::vector{{4, 5, 6}}; auto res = into_vector(interleave, v1, v2); CHECK(res == (std::vector{1, 4, 2, 5, 3, 6})); } TEST_CASE("interleave, interleave termineates early enough") { auto v1 = std::vector{{1, 2, 3}}; auto v2 = std::vector{{4, 5, 6}}; auto res = into_vector(comp(interleave, take(3)), v1, v2); CHECK(res == (std::vector{1, 4, 2})); } zug-0.1.1/test/transducer/interleave.cpp000066400000000000000000000011031451275774000203200ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include using namespace zug; TEST_CASE("interleave, example") { // example1 { auto v = std::vector{1, 2, 3, 4, 5}; auto r = into_vector(interleave, v, v); CHECK(r == decltype(r){1, 1, 2, 2, 3, 3, 4, 4, 5, 5}); // } } zug-0.1.1/test/transducer/interpose.cpp000066400000000000000000000024351451275774000202030ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include using namespace zug; TEST_CASE("interpose, into") { // example1 { auto v = std::vector{1, 2, 3, 4, 5}; auto res = into_vector(interpose(42), v); CHECK(res == (decltype(res){1, 42, 2, 42, 3, 42, 4, 42, 5})); // } } TEST_CASE("interpose, termination") { auto v = std::vector{1, 2, 3, 4, 5}; auto res = into_vector(comp(interpose(42), take(4)), v); CHECK(res == (decltype(res){1, 42, 2, 42})); } TEST_CASE("interpose, variadic") { using tup = std::tuple; auto v1 = std::vector{1, 2, 3, 4, 5}; auto v2 = std::vector{'a', 'b', 'c'}; auto res = into_vector(interpose(42, 'Z'), v1, v2); CHECK(res == (decltype(res){tup(1, 'a'), tup(42, 'Z'), tup(2, 'b'), tup(42, 'Z'), tup(3, 'c')})); } zug-0.1.1/test/transducer/iter.cpp000066400000000000000000000023401451275774000171310ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include using namespace zug; TEST_CASE("iter, generator") { // example1 { auto v = std::vector{13, 42, 5}; auto res = into_vector(iter(v)); CHECK(res == (std::vector{13, 42, 5})); // } } TEST_CASE("iter, merging") { // example2 { auto v1 = std::vector{13, 42, 5, 6, 7}; auto v2 = std::vector{"one", "two", "three"}; auto res = into_vector(iter(v1), v2); CHECK(res == (decltype(res){{"one", 13}, {"two", 42}, {"three", 5}})); // } } TEST_CASE("iter, variadic") { auto v1 = std::vector{13, 42, 5, 6, 7}; auto v2 = std::vector{"one", "two", "three"}; auto res = into_vector(iter(v1, v2)); CHECK(res == (decltype(res){std::make_tuple(13, "one"), std::make_tuple(42, "two"), std::make_tuple(5, "three")})); } zug-0.1.1/test/transducer/map.cpp000066400000000000000000000020711451275774000167440ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include using namespace zug; TEST_CASE("map, mapping") { auto v = std::vector{1, 2, 3, 6}; auto times2 = [](int x) { return x * 2; }; CHECK(transduce(map(times2), std::plus{}, 1, v) == 25); } TEST_CASE("map, example") { // example1 { auto v = std::vector{1, 2, 3, 6}; auto r = into_vector(map([](int x) { return x * 2; }), v); CHECK(r == decltype(r){2, 4, 6, 12}); // } } namespace { int free_times2(int x) { return x * 2; } } // anonymous namespace TEST_CASE("map, mapping invoke") { auto v = std::vector{1, 2, 3, 6}; CHECK(transduce(map(&free_times2), std::plus{}, 1, v) == 25); } zug-0.1.1/test/transducer/map_indexed.cpp000066400000000000000000000012561451275774000204500ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include using namespace zug; TEST_CASE("map_indexed, mapping") { // example1 { auto v = std::vector{"1", "2", "3", "6"}; auto timesi = [](std::string x, std::size_t index) { return std::stoi(x) * static_cast(index); }; CHECK(transduce(map_indexed(timesi), std::plus{}, 1, v) == 27); // } } zug-0.1.1/test/transducer/mapcat.cpp000066400000000000000000000011511451275774000174320ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include using namespace zug; TEST_CASE("mapcat, mapcat") { // example1 { auto v = std::vector>{{1, 2}, {3}, {4, 5, 6}}; auto res = into_vector(mapcat([](int x) { return x * 2; }), v); CHECK(res == (std::vector{2, 4, 6, 8, 10, 12})); // } } zug-0.1.1/test/transducer/partition.cpp000066400000000000000000000044401451275774000202020ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include #include #include #include "../spies.hpp" using namespace zug; TEST_CASE("partition, partition flushing") { // example1 { auto v = std::vector{1, 2, 3, 4, 5, 6, 7}; auto r = into_vector(partition(2u), v); using t = std::vector>; CHECK(r == t{{1, 2}, {3, 4}, {5, 6}, {7}}); // } } TEST_CASE("partition, partition") { auto v = std::vector{1, 2, 3, 4, 5, 6}; auto res = into(std::vector>{}, partition(2u), v); CHECK(res == (std::vector>{{1, 2}, {3, 4}, {5, 6}})); } namespace { struct some_step_fn { template T operator()(T x, Xs&&...) const { return x; } }; } // namespace TEST_CASE("partition, partition does not copy step function") { auto step = testing::copy_spy{}; auto v = std::vector{1, 2, 3, 4, 5, 7, 8, 9}; reduce(partition(2u)(step), 0, v); CHECK(step.copied.count() == 2); } TEST_CASE("partition, partition moves the state through") { auto v = std::vector{1, 2, 3, 4, 5}; auto spy = reduce(partition(2u)(first), testing::copy_spy<>{}, v); CHECK(spy.copied.count() == 0); } TEST_CASE("partition, defining early termination with completion") { auto v = std::vector{{1, 2, 3, 4, 5}}; auto res = into(std::vector>{}, comp(take(3), partition(2u)), v); CHECK(res == (std::vector>{{1, 2}, {3}})); } TEST_CASE("partition, reduce nested deals with empty sequence properly") { auto v = std::vector>{{{}, {1, 2, 3}, {}}}; auto res = into(std::vector>{}, comp(cat, transducer>{partition(2u)}), v); CHECK(res == (std::vector>{{1, 2}, {3}})); } zug-0.1.1/test/transducer/partition_by.cpp000066400000000000000000000045301451275774000206740ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include #include #include #include "../spies.hpp" using namespace zug; TEST_CASE("partition_by, partition_by") { auto v = std::vector{1, 1, 2, 2, 2, 3}; auto res = into_vector(partition_by(identity), v); CHECK(res == (decltype(res){{1, 1}, {2, 2, 2}, {3}})); } namespace { int free_mod2(int x) { return x % 2; } } // namespace TEST_CASE("partition_by, invoke") { auto v = std::vector{1, 1, 2, 4, 2, 3}; auto res = into_vector(partition_by(&free_mod2), v); CHECK(res == (decltype(res){{1, 1}, {2, 4, 2}, {3}})); } TEST_CASE("partition_by, example") { // example1 { auto v = std::vector{1, 1, 2, 4, 2, 3}; auto res = into_vector(partition_by([](int x) { return x % 2; }), v); using t = std::vector>; CHECK(res == t{{1, 1}, {2, 4, 2}, {3}}); // } } TEST_CASE("partition_by, more example") { // example2 { auto v = std::vector{1, 1, 2, 2, 2, 3}; auto res = into_vector(partition_by(identity), v); CHECK(res == decltype(res){{1, 1}, {2, 2, 2}, {3}}); // } } TEST_CASE("partition_by, partition_by does not copy step function") { auto step = testing::copy_spy{}; auto v = std::vector{1, 2, 3, 4, 5, 7, 8, 9}; reduce(partition_by(identity)(step), 0, v); CHECK(step.copied.count() == 2); } TEST_CASE("partition_by, partition by moves the state through") { auto v = std::vector{1, 2, 3, 4, 5}; auto spy = reduce(partition_by(identity)(first), testing::copy_spy<>{}, v); CHECK(spy.copied.count() == 0); } TEST_CASE("partition_by, reduce nested deals with empty sequence properly") { auto v = std::vector>{{{}, {1, 1, 2}, {}}}; auto part = transducer>{partition_by(identity)}; auto res = into_vector(comp(cat, part), v); CHECK(res == (std::vector>{{1, 1}, {2}})); } zug-0.1.1/test/transducer/product.cpp000066400000000000000000000036761451275774000176630ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include #include using namespace zug; TEST_CASE("product, product") { // example1 { auto v1 = std::vector{1, 2}; auto v2 = std::vector{3, 4}; auto res = into_vector(product(v2), v1); using t = std::vector>; CHECK(res == t{{1, 3}, {1, 4}, {2, 3}, {2, 4}}); // } } TEST_CASE("product, variadic") { // example2 { auto v1 = std::vector{1, 2}; auto v2 = std::vector{3, 4}; auto v3 = std::vector{'a', 'b'}; auto res = into_vector(product(v2, v3), v1); using t = std::vector>; CHECK(res == t{{1, 3, 'a'}, {1, 3, 'b'}, {1, 4, 'a'}, {1, 4, 'b'}, {2, 3, 'a'}, {2, 3, 'b'}, {2, 4, 'a'}, {2, 4, 'b'}}); // } } TEST_CASE("product, generator") { using tup = std::tuple; auto v1 = std::vector{1, 2}; auto v2 = std::vector{4, 5}; auto res = into_vector(comp(take(1), product(v1, v2))); CHECK(res == (decltype(res){tup(1, 4), tup(1, 5), tup(2, 4), tup(2, 5)})); } TEST_CASE("product, tranducer product example") { using tup = std::tuple; auto idx = sequence(range(2)); auto res = into_vector(product(idx), idx); for (auto&& x : res) { std::cout << std::get<0>(x) << ", " << std::get<1>(x) << std::endl; } CHECK(res == (decltype(res){tup(0, 0), tup(0, 1), tup(1, 0), tup(1, 1)})); } zug-0.1.1/test/transducer/random_sample.cpp000066400000000000000000000036431451275774000210160ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include struct deterministic_engine { std::uint64_t count = 0; constexpr static std::uint64_t max() { return std::numeric_limits::max(); } constexpr static std::uint64_t min() { return std::numeric_limits::min(); } std::uint64_t operator()() { return count += max() / 6; } }; #define ZUG_DEFAULT_RANDOM_ENGINE deterministic_engine #include #include #include #include using namespace zug; TEST_CASE("random_sample, simple") { // example1 { CHECK(transduce(range(20) | random_sample(0.5), std::plus<>{}, std::size_t{}) == 100); CHECK(transduce(range(20) | random_sample(1), std::plus<>{}, std::size_t{}) == 190); CHECK(transduce(range(20) | random_sample(0), // std::plus<>{}, std::size_t{}) == 0); // } } TEST_CASE("random_sample, custom generator") { auto eng = deterministic_engine{}; auto gen = [eng]() mutable { return double(eng()) * 2 / double(eng.max()); }; CHECK(transduce(comp(range(20), random_sample(0.5, gen)), std::plus{}, std::size_t{}) == 36); CHECK(transduce(comp(range(20), random_sample(1, gen)), std::plus{}, std::size_t{}) == 100); CHECK(transduce(comp(range(20), random_sample(0, gen)), std::plus{}, std::size_t{}) == 0); } zug-0.1.1/test/transducer/range.cpp000066400000000000000000000031061451275774000172630ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include using namespace zug; TEST_CASE("range, stop") { // example1 { auto res = into_vector(range(3)); CHECK(res == std::vector{{0, 1, 2}}); // } } TEST_CASE("range, start stop") { // example2 { auto res = into_vector(range(2, 6)); CHECK(res == std::vector{2, 3, 4, 5}); // } } TEST_CASE("range, start stop step") { // example3 { auto res = into_vector(range(2, 6, 2)); CHECK(res == std::vector{2, 4}); // } } TEST_CASE("range, paralleld") { // example4 { auto s = std::string{"hello"}; auto res = into_vector(range(2, 6, 2), s); CHECK(res == std::vector>{{'h', 2}, {'e', 4}}); // } } TEST_CASE("range, start stop step floating_point") { auto res = into_vector(range(2.0, 6.0, 1.2)); auto expected = std::vector{{2.0, 3.2, 4.4, 5.6}}; CHECK(transduce(map([](double x, double y) { CHECK_THAT(x, Catch::Matchers::WithinAbs(y, 0.1)); return true; }), last, true, res, expected)); } zug-0.1.1/test/transducer/read.cpp000066400000000000000000000021361451275774000171040ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include using namespace zug; TEST_CASE("read, into") { // example1 { auto stream = std::stringstream{"1 2 3 4 5 6"}; auto res = into_vector(read(stream)); CHECK(res == std::vector{1, 2, 3, 4, 5, 6}); // } } TEST_CASE("read, into variadic") { // example2 { auto stream = std::stringstream{"1 hello 2 my 3 friend 4 !!"}; auto res = into_vector(read(stream)); CHECK(res == std::vector>{ {1, "hello"}, {2, "my"}, {3, "friend"}, {4, "!!"}}); // } } TEST_CASE("read, into ends on bad input") { auto stream = std::stringstream{"1 2 3 fuck 4 5 6"}; auto res = into_vector(read(stream)); CHECK(res == (decltype(res){1, 2, 3})); } zug-0.1.1/test/transducer/readbuf.cpp000066400000000000000000000021561451275774000176030ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include #include using namespace zug; TEST_CASE("readbuf, constant sized") { // example1 { auto as_str = map([](auto buf) { return std::string{buf.begin(), buf.end()}; }); auto stream = std::stringstream{"12345"}; auto res = into_vector(readbuf<3>(stream) | as_str); CHECK(res == std::vector{"123", "45"}); // } } TEST_CASE("readbuf, dynamic sized") { // example2 { auto as_str = map([](auto buf) { return std::string{buf.begin(), buf.end()}; }); auto stream = std::stringstream{"12345"}; auto res = into_vector(readbuf(stream, 3) | as_str); CHECK(res == std::vector{"123", "45"}); // } } zug-0.1.1/test/transducer/remove.cpp000066400000000000000000000013601451275774000174640ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include using namespace zug; TEST_CASE("remove, simple") { // example1 { auto v = std::vector{1, 2, 3, 6}; auto times2 = [](int x) { return x * 2; }; auto odd = [](int x) { return x % 2 == 0; }; auto res = transduce(remove(odd) | map(times2), std::plus{}, 1, v); CHECK(res == 9); // } } zug-0.1.1/test/transducer/repeat.cpp000066400000000000000000000023511451275774000174500ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include using namespace zug; TEST_CASE("repeat, repeat") { using tup = std::tuple; auto hola = "hola"; auto v = std::vector{13, 42, 5}; auto res = into_vector(repeat(hola), v); CHECK(res == (std::vector{{tup(13, hola), tup(42, hola), tup(5, hola)}})); } TEST_CASE("repeat, generator") { // example1 { auto res = into_vector(repeat('a') | take(3)); CHECK(res == (std::vector{{'a', 'a', 'a'}})); // } } TEST_CASE("repeat, parallel") { // example2 { auto v = std::vector{1, 2, 3}; auto res = into_vector(repeat('a'), v); CHECK(res == decltype(res){{1, 'a'}, {2, 'a'}, {3, 'a'}}); // } } TEST_CASE("repeatn, generator") { // example3 { auto res = into_vector(repeatn(3, 'a')); CHECK(res == (std::vector{{'a', 'a', 'a'}})); // } } zug-0.1.1/test/transducer/replace.cpp000066400000000000000000000037361451275774000176130ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include using namespace zug; TEST_CASE("replace, replace") { // example1 { auto table = std::map{{"hola", "adios"}}; auto v = std::vector{"hola", " ", "amigo"}; CHECK(transduce(replace(table), std::plus<>{}, std::string{}, v) == "adios amigo"); // } } TEST_CASE("replace, replace variadic") { using tup = std::tuple; auto table = std::map{{tup(1, "hola"), tup(42, "adios")}}; auto v1 = std::vector{1, 2, 3}; auto v2 = std::vector{"hola", " ", "amigo"}; auto res = into_vector(replace(table), v1, v2); CHECK(res == (decltype(res){tup(42, "adios"), tup(2, " "), tup(3, "amigo")})); } TEST_CASE("replace, replace all") { // example2 { auto table = std::map{{"hola", 1}, {"amigo", 2}}; auto v = std::vector{"hola", " ", "amigo"}; CHECK(transduce(replace_all(table), std::plus<>{}, int{}, v) == 3); // } } TEST_CASE("replace, replace safe pass") { // example3 { auto table = std::map{{"hola", 1}, {"amigo", 2}}; auto v = std::vector{"hola", "amigo"}; CHECK(transduce(replace_all_safe(table), std::plus<>{}, int{}, v) == 3); // } } TEST_CASE("replace, replace safe exception") { // example4 { auto table = std::map{{"hola", 12}, {"amigo", 42}}; auto v = std::vector{"hola", "oops", "amigo"}; CHECK_THROWS_AS(transduce(replace_all_safe(table), std::plus<>{}, int{}, v), std::out_of_range); // } } zug-0.1.1/test/transducer/scan.cpp000066400000000000000000000015211451275774000171120ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include using namespace zug; TEST_CASE("scan, scan") { // example1 { auto v = std::vector{1, 2, 3, 4, 5, 6}; auto r = into_vector(scan(0, std::plus<>{}), v); CHECK(r == (std::vector{1, 3, 6, 10, 15, 21})); // } } TEST_CASE("scan, stateful") { // example2 { auto r = into_vector(count(1) | scan(0, take(6)(std::plus<>{}))); CHECK(r == std::vector{1, 3, 6, 10, 15, 21}); // } } zug-0.1.1/test/transducer/sink.cpp000066400000000000000000000023601451275774000171340ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include #include "../spies.hpp" using namespace zug; TEST_CASE("sink, sink") { // example1 { auto v = std::vector{1, 2, 3, 6}; auto r = std::vector{}; run(sink([&](int x) { r.push_back(x); }), v); CHECK(v == r); // } } TEST_CASE("sink, sink2") { // example2 { auto v = std::vector{1, 42, 13, 6}; auto r = into_vector(sink([&](int x) {}) | enumerate, v); CHECK(r == std::vector{0, 1, 2, 3}); // } } TEST_CASE("sink, moves values out of rvalue_container") { using elem = testing::copy_spy<>; auto x = elem{}; auto v = std::vector{x, x, x, x}; auto copies = x.copied.count(); auto r = into_vector(comp(sink([](elem) {}), enumerate), std::move(v)); CHECK(x.copied.count() == copies); CHECK(r == (decltype(r){0, 1, 2, 3})); } zug-0.1.1/test/transducer/take.cpp000066400000000000000000000052341451275774000171170ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include #include #include #include "../spies.hpp" #include using namespace zug; TEST_CASE("take, take") { // example1 { auto v = std::vector{1, 2, 3, 4, 5}; auto res = into_vector(take(3), v); CHECK(res == std::vector{1, 2, 3}); // } } TEST_CASE("take, take cat terminates early") { auto v = std::vector>{{1, 2}, {3}, {4, 5, 6}}; auto res = into(std::vector{}, comp(cat, map([](int x) { CHECK(x < 5); return x; }), take(4)), v); CHECK(res == (std::vector{1, 2, 3, 4})); } TEST_CASE("take, take stops early enough") { auto v = std::vector{1, 2, 3, 4, 5, 6}; auto res = into(std::vector{}, comp(map([](int x) { if (x > 4) throw std::runtime_error("bad!"); return x; }), take(3)), v); CHECK(res == (std::vector{1, 2, 3})); } TEST_CASE("take, take stops early enough2") { auto v = std::vector{1, 2, 3, 4, 5, 6}; auto res = into(std::vector{}, comp(take(3), map([](int x) { if (x > 4) throw std::runtime_error("bad!"); return x; })), v); CHECK(res == (std::vector{1, 2, 3})); } TEST_CASE("take, take moves the state through") { auto v = std::vector{1, 2, 3, 4, 5}; auto spy = reduce(take(5)(first), testing::copy_spy<>{}, v); CHECK(spy.copied.count() == 0); } TEST_CASE("take, take reduce nested finished") { auto v = std::vector>{{1}, {2}, {3}}; auto res = into(std::vector{}, comp(cat, take(1)), v); CHECK(res == (std::vector{1})); } TEST_CASE("take, take zero not supported") { auto v = std::vector{1, 2, 3, 4, 5}; CHECK_THROWS_AS(into(std::vector{}, take(0), v), detail::empty_transducer_error); } zug-0.1.1/test/transducer/take_nth.cpp000066400000000000000000000024751451275774000177740ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include "../spies.hpp" using namespace zug; TEST_CASE("take_nth, into") { auto v = std::vector{1, 2, 3, 4, 5}; { auto res = into(std::vector{}, take_nth(1), v); CHECK(res == (decltype(res){1, 2, 3, 4, 5})); } { auto res = into(std::vector{}, take_nth(2), v); CHECK(res == (decltype(res){1, 3, 5})); } { auto res = into(std::vector{}, take_nth(3), v); CHECK(res == (decltype(res){1, 4})); } } TEST_CASE("take_nth, example") { // example1 { auto v = std::vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; auto res = into_vector(take_nth(3), v); CHECK(res == std::vector{1, 4, 7, 10}); // } } TEST_CASE("take_nth, compose") { using namespace std::placeholders; auto v = std::vector{1, 2, 3, 4, 5}; auto res = into_vector(comp(take_nth(2), take(2)), v); CHECK(res == (decltype(res){1, 3})); } zug-0.1.1/test/transducer/take_while.cpp000066400000000000000000000023711451275774000203060ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include "../spies.hpp" using namespace zug; TEST_CASE("take_while, into") { using namespace std::placeholders; // example1 { auto v = std::vector{1, 2, 3, 4, 5}; auto pred = std::bind(std::less<>{}, _1, 4); auto res = into_vector(take_while(pred), v); CHECK(res == decltype(res){1, 2, 3}); // } } namespace { bool free_lt4(int x) { return x < 4; } } // namespace TEST_CASE("take_while, invoke") { auto v = std::vector{1, 2, 3, 4, 3}; auto res = into_vector(take_while(free_lt4), v); CHECK(res == (decltype(res){1, 2, 3})); } TEST_CASE("take_while, generate with map") { auto count = int{}; auto counter = [&] { return count++; }; auto lt10 = [](int x) { return x < 10; }; auto res = transduce(comp(map(counter), take_while(lt10)), std::plus{}, 1); CHECK(res == 46); } zug-0.1.1/test/transducer/transducer.cpp000066400000000000000000000041331451275774000203420ustar00rootroot00000000000000#include #include #include #include #include #include using namespace zug; TEST_CASE("transducer: comp composition") { auto add_one = transducer(map([](auto x) { return x + 1; })); auto xform = comp(add_one, add_one); auto result = transduce(xform, last, 0, std::vector{10}); CHECK(result == 12); } TEST_CASE("transducer: operator| composition") { auto add_one = transducer(map([](auto x) { return x + 1; })); auto xform = add_one | add_one; auto result = transduce(xform, last, 0, std::vector{10}); CHECK(result == 12); } TEST_CASE("transducer: operator| composition with non type-erased transducer") { auto add_one = transducer(map([](auto x) { return x + 1; })); auto add_two = map([](auto x) { return x + 2; }); SECTION("type-erased | non type-erased") { auto xform = add_one | add_two; auto result = transduce(xform, last, 0, std::vector{10}); CHECK(result == 13); } SECTION("non type-erased | type-erased") { auto xform = add_two | add_one; auto result = transduce(xform, last, 0, std::vector{10}); CHECK(result == 13); } } TEST_CASE( "transducer: operator| composition with non-composable type transducer") { auto add_five = [](auto&& step) { return [=](auto&& s, auto&&... is) mutable { return step( ZUG_FWD(s), compat::invoke([](auto x) { return x + 5; }, ZUG_FWD(is)...)); }; }; auto add_one = transducer(map([](auto x) { return x + 1; })); SECTION("transducer | non-composable") { auto xform = add_one | add_five; auto result = transduce(xform, last, 0, std::vector{660}); CHECK(result == 666); } SECTION("non-composable | transducer") { auto xform = add_five | add_one; auto result = transduce(xform, last, 0, std::vector{660}); CHECK(result == 666); } } zug-0.1.1/test/transducer/unzip.cpp000066400000000000000000000036431451275774000173420ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include #include #include using namespace zug; TEST_CASE("unzip, minimal") { auto r = unzip(map([](auto a, auto b) { return a + b; })(last))( 0, std::make_tuple(13, 42)); CHECK(r == 55); } TEST_CASE("unzip, example") { // example1 { auto v = std::vector>{{1, 10}, {2, 20}, {3, 30}}; auto r = into_vector(unzip | map(std::plus<>{}), v); CHECK(r == std::vector{11, 22, 33}); // } } TEST_CASE("unzip, unzip transducer") { using tup = std::tuple; auto v1 = std::vector{{tup(13, 1.1), tup(42, 2.2), tup(5, 3.3)}}; auto res = transduce( unzip, [](double x, int y, double z) { return x + y + z; }, 1.0, v1); CHECK_THAT(res, Catch::Matchers::WithinAbs(67.6, 0.001)); } TEST_CASE("unzip, unzip transducer variadic multitype") { using tup1 = std::tuple; using tup2 = std::tuple; using arr = std::array; auto v1 = std::vector{{tup1(13, 1.1), tup1(42, 2.2), tup1(5, 3.3)}}; auto v2 = std::vector{{1, 2, 3}}; auto v3 = std::vector{{tup2('a'), tup2('b'), tup2('c')}}; auto v4 = std::vector{{arr{{1, 2}}, arr{{3, 4}}, arr{{5, 6}}}}; auto res = transduce( unzip, [](double x, int y, double z, short v, char w, int a, int b) { return x + y + z + v + w + a + b; }, 1.0, v1, v2, v3, v4); CHECK_THAT(res, Catch::Matchers::WithinAbs(388.6, 0.001)); } zug-0.1.1/test/transducer/write.cpp000066400000000000000000000027531451275774000173300ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include #include using namespace zug; TEST_CASE("write, write") { // example1 { auto v = std::vector{1, 2, 3, 4}; auto stream = std::stringstream{}; run(write(stream), v); CHECK(stream.str() == "1234"); // } } TEST_CASE("write, in separator") { // example2 { auto v = std::vector{1, 2, 3, 4}; auto stream = std::stringstream{}; run(write(stream, ' '), v); CHECK(stream.str() == "1 2 3 4"); // } } TEST_CASE("write, in separator variadic is arg separator") { // example3 { auto v1 = std::vector{1, 2, 3, 4}; auto v2 = std::vector{'y', 'e', 'a', 'h'}; auto stream = std::stringstream{}; run(write(stream, ' '), v1, v2); CHECK(stream.str() == "1 y 2 e 3 a 4 h"); // } } TEST_CASE("write, in separator and arg separator") { // example4 { auto v1 = std::vector{1, 2, 3, 4}; auto v2 = std::vector{'y', 'e', 'a', 'h'}; auto stream = std::stringstream{}; run(write(stream, ' ', ','), v1, v2); CHECK(stream.str() == "1,y 2,e 3,a 4,h"); // } } zug-0.1.1/test/transducer/writebuf.cpp000066400000000000000000000015731451275774000200240ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include #include #include #include using namespace zug; TEST_CASE("writebuf, writebuf partitions") { auto s = std::string{"123456"}; auto stream = std::stringstream{}; run(comp(partition(3u), writebuf(stream)), s); CHECK(stream.str() == s); } TEST_CASE("writebuf, writebuf strings") { // example1 { auto v = std::vector{"123", "4", "56"}; auto stream = std::stringstream{}; run(writebuf(stream), v); CHECK(stream.str() == "123456"); // } } zug-0.1.1/test/transducer/zip.cpp000066400000000000000000000014041451275774000167700ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include #include #include using namespace zug; TEST_CASE("zip, zip transducer") { // example1 { auto v1 = std::vector{13, 42, 5}; auto v2 = std::vector{1.1, 2.2, 3.3}; auto res = transduce( zip, [](double x, std::tuple t) { return x + std::get<0>(t) + std::get<1>(t); }, 1.0, v1, v2); CHECK_THAT(res, Catch::Matchers::WithinAbs(67.6, 0.001)); // } } zug-0.1.1/tools/000077500000000000000000000000001451275774000134725ustar00rootroot00000000000000zug-0.1.1/tools/sinusoidal-sphinx-theme/000077500000000000000000000000001451275774000202535ustar00rootroot00000000000000zug-0.1.1/zug/000077500000000000000000000000001451275774000131375ustar00rootroot00000000000000zug-0.1.1/zug/any_state.hpp000066400000000000000000000207311451275774000156420ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include #include #include #if ZUG_TRACE_ANY_STATE_ALLOC #include #endif namespace zug { //! @defgroup any_state //! @{ /*! * Polymorphically holds any value implementing the `state_traits`. * This type is used for the implementation of `transducer`. */ class any_state { public: any_state() noexcept : data_(reinterpret_cast(&null_holder_storage<>::value)) , size_(0) {} ~any_state() noexcept { if (size_) { content()->~holder_base(); delete[] data_; } } any_state(any_state&& other) : data_(other.data_) , size_{} { using std::swap; swap(size_, other.size_); } any_state(const any_state& other) : data_(other.size_ ? new char[other.size_] : nullptr) , size_(other.size_) { #if ZUG_TRACE_ANY_STATE_ALLOC std::cout << "alloc-c" << std::endl; #endif other.content()->clone(data_); } template any_state( ValueType&& value, std::enable_if_t< !std::is_base_of>::value && !std::is_base_of>::value>* = 0) : data_(new char[sizeof(holder>)]) , size_(sizeof(holder>)) { new (data_) holder>(std::forward(value)); #if ZUG_TRACE_ANY_STATE_ALLOC std::cout << "alloc-n " << typeid(std::decay_t).name() << std::endl; #endif } any_state& operator=(any_state&& other) { using std::swap; swap(data_, other.data_); swap(size_, other.size_); return *this; } any_state& operator=(const any_state& rhs) { if (&rhs != this) { realloc_(rhs.content()->size()); rhs.content()->clone(data_); } return *this; } template auto operator=(ValueType&& rhs) -> std::enable_if_t< !std::is_base_of>::value, any_state&> { realloc_(sizeof(holder>)); new (data_) holder>(std::forward(rhs)); return *this; } template std::decay_t& as() & { return as_impl(meta::pack>{}); } template std::decay_t&& as() && { return std::move(as_impl(meta::pack>{})); } template const std::decay_t& as() const& { return const_cast(this)->as_impl( meta::pack>{}); } template void check() const { if (!has()) { throw std::runtime_error(std::string("Have ") + type().name() + ", but expect " + typeid(std::decay_t).name()); } } template bool has() const { return has_impl(meta::pack>{}); } const std::type_info& type() const noexcept { return content()->type(); } private: void realloc_(std::size_t size) { if (size_ > 0) content()->~holder_base(); if (size_ < size) { if (size_ > 0) delete[] data_; data_ = new char[size]; size_ = size; #if ZUG_TRACE_ANY_STATE_ALLOC std::cout << "alloc-r" << std::endl; #endif } } template T& as_impl(meta::pack) { #if ZUG_SAFE_ANY_STATE check(); #endif return reinterpret_cast*>(data_)->held; } any_state& as_impl(meta::pack) { return *this; } template bool has_impl(meta::pack) const { return content()->type() == typeid(T); } bool has_impl(meta::pack) const { return true; } friend struct state_traits; struct holder_base { virtual ~holder_base() = default; virtual const std::type_info& type() const noexcept = 0; virtual void clone(char* data) const = 0; virtual void move(char* data) const = 0; virtual any_state complete() const = 0; virtual bool is_reduced() const = 0; virtual any_state unwrap() const = 0; virtual any_state unwrap_all() const = 0; virtual any_state rewrap(any_state) const = 0; virtual any_state data() const = 0; virtual std::size_t size() const = 0; }; template struct holder : holder_base { T held; template holder(TT&& x) : held(std::forward(x)) {} const std::type_info& type() const noexcept override { return typeid(T); } void clone(char* data) const override { new (data) holder(held); } void move(char* data) const override { new (data) holder(std::move(held)); } any_state complete() const override { return state_complete(held); } bool is_reduced() const override { return state_is_reduced(held); } any_state unwrap() const override { return state_unwrap(held); } any_state unwrap_all() const override { return state_unwrap_all(held); } any_state rewrap(any_state x) const override { return state_rewrap(held, std::move(x)); } any_state data() const override { return state_data(held, [] { return any_state{}; }); } std::size_t size() const override { return sizeof(T); } }; struct null_holder : holder_base { virtual ~null_holder() = default; const std::type_info& type() const noexcept override { return typeid(void); } void clone(char* data) const override { new (data) null_holder; } void move(char* data) const override { new (data) null_holder; } any_state complete() const override { return {}; } bool is_reduced() const override { return false; } any_state unwrap() const override { return {}; } any_state unwrap_all() const override { return {}; } any_state rewrap(any_state x) const override { return x; } any_state data() const override { return {}; } std::size_t size() const override { return 0; } }; template struct null_holder_storage { static null_holder value; }; holder_base* content() const { return reinterpret_cast(data_); } char* data_; std::size_t size_; }; template any_state::null_holder any_state::null_holder_storage::value{}; template <> struct state_traits { template static decltype(auto) complete(T&& t) { return std::forward(t).content()->complete(); } template static decltype(auto) is_reduced(T&& t) { return std::forward(t).content()->is_reduced(); } template static decltype(auto) unwrap(T&& t) { return std::forward(t).content()->unwrap(); } template static decltype(auto) unwrap_all(T&& t) { return std::forward(t).content()->unwrap_all(); } template static decltype(auto) rewrap(T&& t, U&& x) { return std::forward(t).content()->rewrap(std::forward(x)); } template static auto data(T&& t, D&& d) -> std::decay_t(d)())> { using data_t = std::decay_t(d)())>; auto data = t.content()->data(); return data.template has() ? data.template as() : std::forward(d)(); } }; //! @} } // namespace zug zug-0.1.1/zug/compat/000077500000000000000000000000001451275774000144225ustar00rootroot00000000000000zug-0.1.1/zug/compat/apply.hpp000066400000000000000000000044131451275774000162620ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include #include #include #include #include namespace zug { namespace compat { namespace detail { using std::get; struct could_not_get_index_sequence {}; template struct get_integer_sequence_aux { using type = std::conditional_t<(N == 0), could_not_get_index_sequence, std::make_integer_sequence>; }; template struct get_integer_sequence_aux< T, I, N, meta::void_t(std::declval()))>> : get_integer_sequence_aux {}; template struct get_integer_sequence : get_integer_sequence_aux {}; template struct get_integer_sequence, I> { using type = std::make_integer_sequence; }; template struct get_integer_sequence, I> { using type = std::make_integer_sequence; }; template struct get_integer_sequence, I> { using type = std::make_integer_sequence; }; template constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence) { return invoke(std::forward(f), std::get(std::forward(t))...); } template using get_integer_sequence_t = typename get_integer_sequence, I>::type; template using get_index_sequence_t = get_integer_sequence_t; } // namespace detail template constexpr decltype(auto) apply(F&& f, Tuple&& t) { return detail::apply_impl(std::forward(f), std::forward(t), detail::get_index_sequence_t{}); } } // namespace compat } // namespace zug zug-0.1.1/zug/compat/invoke.hpp000066400000000000000000000035221451275774000164300ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include namespace zug { namespace compat { namespace detail { #define ZUG_DETAIL_DECLTYPE_RETURN(body_expr) \ decltype(body_expr) { return (body_expr); } template inline auto do_invoke(F&& f, Args&&... args) -> ZUG_DETAIL_DECLTYPE_RETURN( std::forward(f)(std::forward(args)...)) template inline auto do_invoke(T Base::*pmd, Derived&& ref) -> ZUG_DETAIL_DECLTYPE_RETURN(std::forward(ref).*pmd) template inline auto do_invoke(PMD pmd, Pointer&& ptr) -> ZUG_DETAIL_DECLTYPE_RETURN((*std::forward(ptr)).*pmd) template inline auto do_invoke(T Base::*pmf, Derived&& ref, Args&&... args) -> ZUG_DETAIL_DECLTYPE_RETURN((std::forward(ref).* pmf)(std::forward(args)...)) template inline auto do_invoke(PMF pmf, Pointer&& ptr, Args&&... args) -> ZUG_DETAIL_DECLTYPE_RETURN(((*std::forward(ptr)).* pmf)(std::forward(args)...)) } // namespace detail /*! * Like C++17 `std::invoke` */ template auto invoke(F&& f, ArgTypes&&... args) -> decltype(detail::do_invoke(std::forward(f), std::forward(args)...)) { return detail::do_invoke(std::forward(f), std::forward(args)...); } } // namespace compat } // namespace zug zug-0.1.1/zug/compose.hpp000066400000000000000000000120151451275774000153140ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente and Carl Bussey // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include #include #include #include #include namespace zug { namespace detail { template struct invoke_composition_impl { template static constexpr decltype(auto) apply(Fns&& fns, Args&&... args) { return invoke_composition_impl::apply( std::forward(fns), compat::invoke(std::get(std::forward(fns)), std::forward(args)...)); } }; template <> struct invoke_composition_impl<0> { template static constexpr decltype(auto) apply(Fns&& fns, Args&&... args) { return compat::invoke(std::get<0>(std::forward(fns)), std::forward(args)...); } }; template constexpr decltype(auto) invoke_composition(Fns&& fns, Args&&... args) { constexpr auto Size = std::tuple_size>::value; return invoke_composition_impl::apply( std::forward(fns), std::forward(args)...); } struct pipeable {}; } // namespace detail template struct composed : detail::pipeable , std::tuple { using base_t = std::tuple; template < typename TupleFns, std::enable_if_t< !std::is_same>::value>* = nullptr> constexpr composed(TupleFns&& fns) : base_t{std::forward(fns)} {} template constexpr decltype(auto) operator()(T&&... xs) & { return detail::invoke_composition(as_tuple(), std::forward(xs)...); } template constexpr decltype(auto) operator()(T&&... xs) const& { return detail::invoke_composition(as_tuple(), std::forward(xs)...); } template constexpr decltype(auto) operator()(T&&... xs) && { return detail::invoke_composition(std::move(*this).as_tuple(), std::forward(xs)...); } base_t& as_tuple() & { return *this; } const base_t& as_tuple() const& { return *this; } base_t&& as_tuple() && { return std::move(*this); } }; namespace detail { template constexpr bool is_composed_v = false; template constexpr bool is_composed_v> = true; template constexpr bool is_pipeable_impl_v = false; template constexpr bool is_pipeable_impl_v::value>> = true; template constexpr bool is_pipeable_v = detail::is_pipeable_impl_v; template >>* = nullptr> decltype(auto) to_function_tuple(Composed&& c, meta::try_t) { return std::forward(c).as_tuple(); } template auto to_function_tuple(Fn&& fn, meta::catch_t) { return std::make_tuple(std::forward(fn)); } template decltype(auto) to_function_tuple(T&& t) { return to_function_tuple(std::forward(t), meta::try_t{}); } template struct make_composed_result; template struct make_composed_result> { using type = composed; }; template using make_composed_result_t = typename make_composed_result::type; template auto make_composed(TupleFns&& fns) -> make_composed_result_t> { return {std::forward(fns)}; } } // namespace detail /*! * Right-to left function composition. Returns an object *g* that * composes all the given functions @f$ f_i @f$, such that * @f[ * g(x) = f_1(f_2(...f_n(x))) * @f] * * Functions are invoked via standard *INVOKE*, allowing to compose * function pointers, member functions, etc. */ template constexpr auto comp(F&& f) -> composed> { return {std::forward(f)}; } template constexpr auto comp(Fn&& f, Fns&&... fns) { return detail::make_composed( std::tuple_cat(detail::to_function_tuple(std::forward(f)), detail::to_function_tuple(std::forward(fns))...)); } template > || detail::is_pipeable_v>>* = nullptr> constexpr auto operator|(Lhs&& lhs, Rhs&& rhs) { return comp(std::forward(lhs), std::forward(rhs)); } } // namespace zug zug-0.1.1/zug/detail/000077500000000000000000000000001451275774000144015ustar00rootroot00000000000000zug-0.1.1/zug/detail/copy_traits.hpp000066400000000000000000000034711451275774000174570ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include namespace zug { namespace detail { template struct copy_cv { using type = DestT; }; template struct copy_cv { using type = volatile DestT; }; template struct copy_cv { using type = const DestT; }; template struct copy_cv { using type = const volatile DestT; }; template using copy_cv_t = typename copy_cv::type; template struct copy_reference { using type = DestT; }; template struct copy_reference { using type = DestT&; }; template struct copy_reference { using type = DestT&&; }; template using copy_reference_t = typename copy_reference::type; /*! * Adds reference and cv-qualifications from `OrigT` to `DestT`. In * spite of the name, `is_same>, T>::value` * is not true for every possible `T`, since rules about extent and * function decay can not be unambiguously inversed. */ template struct copy_decay : copy_reference, DestT>> {}; template using copy_decay_t = typename copy_decay::type; } // namespace detail } // namespace zug zug-0.1.1/zug/detail/empty_transducer_error.hpp000066400000000000000000000023711451275774000217160ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include namespace zug { namespace detail { /*! * Error thrown by transducers when they are empty. The problem is that making * an empty transducer requires skipping on the first iteration. Skipping comes * with a cost. So for performance reasons, a transducer might choose to just * rise an error if it produces a completely empty sequence. * * @note This is kept in `detail` because we want to explore alternative designs * that do not require skipping. */ struct empty_transducer_error : std::runtime_error { using base_t = std::runtime_error; using base_t::base_t; empty_transducer_error() : base_t("empty transducer") {} }; /*! * Utility to check whether a range is empty and return it. */ template auto check_non_empty(RangeT&& x) -> RangeT&& { using std::begin; using std::end; if (begin(x) == end(x)) throw empty_transducer_error{}; return std::forward(x); } } // namespace detail } // namespace zug zug-0.1.1/zug/detail/inline_constexpr.hpp000066400000000000000000000012651451275774000205010ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente (and Carl Bussey?) // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #if defined(__cpp_inline_variables) #define ZUG_INLINE_CONSTEXPR inline constexpr #else #if defined(_MSC_VER) #define ZUG_INLINE_CONSTEXPR extern constexpr __declspec(selectany) #elif defined(__GNUC__) #define ZUG_INLINE_CONSTEXPR extern constexpr __attribute__((weak)) #else #error "no inline constexpr support" #define ZUG_INLINE_CONSTEXPR constexpr /* not inline */ #endif #endif // defined(__cpp_inline_variables) zug-0.1.1/zug/detail/is_non_empty.hpp000066400000000000000000000011351451275774000176150ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include namespace zug { namespace detail { inline bool is_non_empty() { return true; } template bool is_non_empty(const RangeT& r, const RangeTs&... rs) { using std::begin; using std::end; return begin(r) != end(r) && is_non_empty(rs...); } } // namespace detail } // namespace zug zug-0.1.1/zug/detail/iterator_facade.hpp000066400000000000000000000124751451275774000202370ustar00rootroot00000000000000// // immer: immutable data structures for C++ // Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include #include namespace zug { namespace detail { struct iterator_core_access { template static decltype(auto) dereference(T&& x) { return x.dereference(); } template static decltype(auto) increment(T&& x) { return x.increment(); } template static decltype(auto) decrement(T&& x) { return x.decrement(); } template static decltype(auto) equal(T1&& x1, T2&& x2) { return x1.equal(x2); } template static decltype(auto) advance(T&& x, D d) { return x.advance(d); } template static decltype(auto) distance_to(T1&& x1, T2&& x2) { return x1.distance_to(x2); } }; /*! * Minimalistic reimplementation of boost::iterator_facade */ template class iterator_facade { protected: using access_t = iterator_core_access; constexpr static auto is_random_access = std::is_base_of::value; constexpr static auto is_bidirectional = std::is_base_of::value; class reference_proxy { friend iterator_facade; DerivedT iter_; reference_proxy(DerivedT iter) : iter_{std::move(iter)} {} public: operator ReferenceT() const { return *iter_; } }; const DerivedT& derived() const { static_assert(std::is_base_of::value, "must pass a derived thing"); return *static_cast(this); } DerivedT& derived() { static_assert(std::is_base_of::value, "must pass a derived thing"); return *static_cast(this); } public: using iterator_category = IteratorCategoryT; using value_type = T; using difference_type = DifferenceTypeT; using pointer = PointerT; using reference = ReferenceT; ReferenceT operator*() const { return access_t::dereference(derived()); } PointerT operator->() const { return &access_t::dereference(derived()); } reference_proxy operator[](DifferenceTypeT n) const { static_assert(is_random_access, ""); return derived() + n; } bool operator==(const DerivedT& rhs) const { return access_t::equal(derived(), rhs); } bool operator!=(const DerivedT& rhs) const { return !access_t::equal(derived(), rhs); } DerivedT& operator++() { access_t::increment(derived()); return derived(); } DerivedT operator++(int) { auto tmp = derived(); access_t::increment(derived()); return tmp; } DerivedT& operator--() { static_assert(is_bidirectional || is_random_access, ""); access_t::decrement(derived()); return derived(); } DerivedT operator--(int) { static_assert(is_bidirectional || is_random_access, ""); auto tmp = derived(); access_t::decrement(derived()); return tmp; } DerivedT& operator+=(DifferenceTypeT n) { access_t::advance(derived(), n); return derived(); } DerivedT& operator-=(DifferenceTypeT n) { access_t::advance(derived(), -n); return derived(); } DerivedT operator+(DifferenceTypeT n) const { static_assert(is_random_access, ""); auto tmp = derived(); return tmp += n; } friend DerivedT operator+(DifferenceTypeT n, const DerivedT& i) { static_assert(is_random_access, ""); return i + n; } DerivedT operator-(DifferenceTypeT n) const { static_assert(is_random_access, ""); auto tmp = derived(); return tmp -= n; } DifferenceTypeT operator-(const DerivedT& rhs) const { static_assert(is_random_access, ""); return access_t::distance_to(rhs, derived()); } bool operator<(const DerivedT& rhs) const { static_assert(is_random_access, ""); return access_t::distance_to(derived(), rhs) > 0; } bool operator<=(const DerivedT& rhs) const { static_assert(is_random_access, ""); return access_t::distance_to(derived(), rhs) >= 0; } bool operator>(const DerivedT& rhs) const { static_assert(is_random_access, ""); return access_t::distance_to(derived(), rhs) < 0; } bool operator>=(const DerivedT& rhs) const { static_assert(is_random_access, ""); return access_t::distance_to(derived(), rhs) <= 0; } }; } // namespace detail } // namespace zug zug-0.1.1/zug/detail/iterator_range.hpp000066400000000000000000000023431451275774000201210ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include namespace zug { namespace detail { template struct iterator_range { using value_type = typename std::iterator_traits::value_type; using iterator = I; using const_iterator = I; using sentinel = S; iterator_range(I first, S last) : begin_{first} , end_{last} {} constexpr I& begin() & { return begin_; } constexpr I const& begin() const& { return begin_; } constexpr S& end() & { return end_; } constexpr S const& end() const& { return end_; } private: I begin_; S end_; }; template auto make_iterator_range(I&& fst, S&& lst) { return iterator_range, std::decay_t>{fst, lst}; } template auto make_iterator_range(Range&& range) { using std::begin; using std::end; return make_iterator_range(begin(range), end(range)); } } // namespace detail } // namespace zug zug-0.1.1/zug/detail/lambda_wrapper.hpp000066400000000000000000000040511451275774000200720ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include namespace zug { namespace detail { // Lambdas are not assignable, which is fucking annoying... it makes the whole // notion of trating them as regular values very hard. I really hate this and // is making me consider going back to the transducer_impl approach from atria, // which maybe is better... template struct lambda_wrapper { lambda_wrapper() = default; lambda_wrapper(const lambda_wrapper& x) = default; lambda_wrapper(lambda_wrapper&& x) = default; lambda_wrapper(const T& x) : value_{x} {} lambda_wrapper(T&& x) : value_{std::move(x)} {} lambda_wrapper& operator=(const lambda_wrapper& x) = default; lambda_wrapper& operator=(lambda_wrapper&& x) noexcept { static_assert(std::is_nothrow_move_constructible::value, ""); value_.T::~T(); new (&value_) T{std::move(x.value_)}; return *this; } const T& operator*() const& { return value_; } T&& operator*() && { return std::move(value_); } T& operator*() & { return value_; } const T& get() const& { return value_; } T&& get() && { return std::move(value_); } T& get() & { return value_; } template decltype(auto) operator()(Ts&&... xs) & { return value_(std::forward(xs)...); } template decltype(auto) operator()(Ts&&... xs) const& { return value_(std::forward(xs)...); } template decltype(auto) operator()(Ts&&... xs) && { return value_(std::forward(xs)...); } private: T value_; }; template lambda_wrapper> wrap_lambda(T&& t) { return {std::forward(t)}; } } // namespace detail } // namespace zug zug-0.1.1/zug/detail/reduce_nested_non_empty.hpp000066400000000000000000000050431451275774000220150ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include #include #if ZUG_REDUCE_TAIL_RECURSIVE #include #define ZUG_REDUCE_NESTED_NON_EMPTY_NON_VARIADIC_IMPL \ ::zug::detail::reduce_nested_non_empty_tail_recursive #elif ZUG_REDUCE_WITH_ACCUMULATE #include #define ZUG_REDUCE_NESTED_NON_EMPTY_NON_VARIADIC_IMPL \ ::zug::detail::reduce_nested_non_empty_accumulate #elif ZUG_REDUCE_ALWAYS_VARIADIC #include #define ZUG_REDUCE_NESTED_NON_EMPTY_NON_VARIADIC_IMPL \ ::zug::detail::reduce_nested_non_empty_variadic #else #include #define ZUG_REDUCE_NESTED_NON_EMPTY_NON_VARIADIC_IMPL \ ::zug::detail::reduce_nested_non_empty_non_variadic #endif namespace zug { namespace detail { template decltype(auto) reduce_nested_non_empty(ReducingFnT&& step, StateT&& state) { return reduce_nested_non_empty_nullary(std::forward(step), std::forward(state)); } template decltype(auto) reduce_nested_non_empty(ReducingFnT&& step, StateT&& state, InputRangeT&& range) { return ZUG_REDUCE_NESTED_NON_EMPTY_NON_VARIADIC_IMPL( std::forward(step), std::forward(state), std::forward(range)); } template decltype(auto) reduce_nested_non_empty(ReducingFnT&& step, StateT&& state, InputRangeT&& range, InputRangeTs&&... ranges) { return reduce_nested_non_empty_variadic( std::forward(step), std::forward(state), std::forward(range), std::forward(ranges)...); } } // namespace detail } // namespace zug zug-0.1.1/zug/detail/reduce_nested_non_empty_accumulate.hpp000066400000000000000000000015401451275774000242160ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include namespace zug { namespace detail { template auto reduce_nested_non_empty_accumulate(ReducingFnT&& step, StateT&& state, InputRangeT&& range) -> std::decay_t { return std::accumulate(std::begin(range), std::end(range), std::forward(state), std::forward(step)); } } // namespace detail } // namespace zug zug-0.1.1/zug/detail/reduce_nested_non_empty_non_variadic.hpp000066400000000000000000000026521451275774000245340ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include #include #include namespace zug { namespace detail { template auto reduce_nested_non_empty_non_variadic(ReducingFnT&& step, StateT&& initial, InputRangeT&& range) -> std::decay_t { using input_t = copy_decay_t>; auto first = std::begin(range); auto last = std::end(range); auto state = step(std::forward(initial), std::forward(*first)); for (++first; !state_is_reduced(state) && first != last; ++first) { // `x = std::move(x)` is undefined behaviour, hence the two // steps approach to protect for when `step` just forwards // the state back. auto new_state = step(std::move(state), std::forward(*first)); state = std::move(new_state); } return state; } } // namespace detail } // namespace zug zug-0.1.1/zug/detail/reduce_nested_non_empty_nullary.hpp000066400000000000000000000016231451275774000235630ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include namespace zug { namespace detail { template auto reduce_nested_non_empty_nullary(ReducingFnT&& step, StateT&& initial) -> std::decay_t { auto state = step(std::forward(initial)); while (!state_is_reduced(state)) { // `x = std::move(x)` is undefined behaviour, hence the two // steps approach to protect for when `step` just forwards // the state back. auto new_state = step(std::move(state)); state = std::move(new_state); } return state; } } // namespace detail } // namespace zug zug-0.1.1/zug/detail/reduce_nested_non_empty_tail_recursive.hpp000066400000000000000000000032371451275774000251200ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include namespace zug { namespace detail { template auto reduce_nested_non_empty_tail_recursive_impl(ReducingFnT&& step, StateT&& state, InputIterT&& first, InputIterT&& last) -> std::decay_t { if (state_is_reduced(state) || first == last) return std::forward(state); auto next_state = step(std::forward(state), *first); return reduce_nested_non_empty_tail_recursive_impl( std::forward(step), std::move(next_state), std::forward(++first), std::forward(last)); } template auto reduce_nested_non_empty_tail_recursive(ReducingFnT&& step, StateT&& initial, InputRangeT&& range) -> std::decay_t { auto first = std::begin(range); auto last = std::end(range); auto state = step(std::forward(initial), *first); return reduce_nested_non_empty_tail_recursive_impl( std::forward(step), std::move(state), ++first, last); } } // namespace detail } // namespace zug zug-0.1.1/zug/detail/reduce_nested_non_empty_variadic.hpp000066400000000000000000000045231451275774000236610ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include #include #include #include #include #include #include namespace zug { namespace detail { template auto reduce_nested_non_empty_variadic_impl(std::index_sequence, meta::pack, ReducingFnT&& step, StateT&& initial, InputRangeTs&&... ranges) -> std::decay_t { auto firsts = std::make_tuple(std::begin(ranges)...); auto lasts = std::make_tuple(std::end(ranges)...); auto state = step(std::forward(initial), std::forward(*std::get(firsts))...); noop(++std::get(firsts)...); while (!state_is_reduced(state) && detail::tuple_all_neq(firsts, lasts)) { auto new_state = step(std::move(state), std::forward(*std::get(firsts))...); state = std::move(new_state); noop(++std::get(firsts)...); } return state; } template decltype(auto) reduce_nested_non_empty_variadic(ReducingFnT&& step, StateT&& state, InputRangeTs&&... ranges) { return reduce_nested_non_empty_variadic_impl( std::make_index_sequence{}, meta::pack>...>{}, std::forward(step), std::forward(state), std::forward(ranges)...); } } // namespace detail } // namespace zug zug-0.1.1/zug/detail/tuple_utils.hpp000066400000000000000000000047541451275774000174750ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include namespace zug { namespace detail { template struct tuple_all_neq_t { template bool operator()(Tuple1T&& t1, Tuple2T&& t2) const { return std::get(std::forward(t1)) != std::get(std::forward(t2)) && tuple_all_neq_t{}(std::forward(t1), std::forward(t2)); } }; template struct tuple_all_neq_t { template bool operator()(Tuple1T&&, Tuple2T&&) const { return true; } }; template bool tuple_all_neq(Tuple1T&& t1, Tuple2T&& t2) { constexpr auto size1 = std::tuple_size>{}; constexpr auto size2 = std::tuple_size>{}; static_assert(size1 == size2, ""); using impl_t = tuple_all_neq_t<0u, (size1 > size2 ? size2 : size1)>; return impl_t{}(std::forward(t1), std::forward(t2)); } // Code from boost. Reciprocal of the golden ratio helps spread entropy and // handles duplicates. See Mike Seymour in magic-numbers-in-boosthash-combine: // http://stackoverflow.com/questions/4948780 template inline void hash_combine(std::size_t& seed, T const& v) { seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } template ::value - 1> struct hash_tuple_impl { static void apply(size_t& seed, Tuple const& tuple) { hash_tuple_impl::apply(seed, tuple); hash_combine(seed, std::get(tuple)); } }; template struct hash_tuple_impl { static void apply(size_t& seed, Tuple const& tuple) { hash_combine(seed, std::get<0>(tuple)); } }; struct tuple_hash { template std::size_t operator()(const std::tuple& x) const noexcept { auto result = std::size_t{}; hash_tuple_impl>::apply(result, x); return result; } }; } // namespace detail } // namespace zug zug-0.1.1/zug/detail/unreachable.hpp000066400000000000000000000007531451275774000173700ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2020 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #if defined(_MSC_VER) #define ZUG_UNREACHABLE() __assume(0) #elif defined(__GNUC__) #define ZUG_UNREACHABLE() __builtin_unreachable() #else #error "__builtin_unreachable or equivalent support required" #define ZUG_UNREACHABLE() #endif zug-0.1.1/zug/into.hpp000066400000000000000000000027701451275774000146270ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #define ZUG_STATEFUL_INTO 1 #include #if ZUG_STATEFUL_INTO #include #else #include #endif namespace zug { #if ZUG_STATEFUL_INTO /*! * Transduces the input `ranges` using `xform`, storing the results in the * collection `col`, which is also returned. * * The results are stored in `col` using `push_back()`. If transducer has * multiple output arguments, they are combined in a `std::tuple`. */ template auto into(CollectionT&& col, XformT&& xform, InputRangeTs&&... ranges) -> CollectionT&& { transduce(std::forward(xform), output, std::back_inserter(col), std::forward(ranges)...); return std::forward(col); } #else template auto into(CollectionT&& col, XformT&& xform, InputRangeTs&&... ranges) -> std::decay_t { return transduce(std::forward(xform), emplacing_back, std::forward(col), std::forward(ranges)...); } #endif } // namespace zug zug-0.1.1/zug/into_vector.hpp000066400000000000000000000022021451275774000161770ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include namespace zug { /*! * Transduces the input `ranges` using `xform`, storing the results in a vector * that is returned. * * The value type of the vector is automatically deduced from the application of * the transducer to the input ranges. If there are multiple arguments in the * outputs from the transducer, they are combined in an `std::tuple`. */ template auto into_vector(XformT&& xform, InputRangeTs&&... ranges) -> std::vector< result_of_t::value_type...>> { using result_t = std::vector< result_of_t::value_type...>>; return into(result_t{}, std::forward(xform), std::forward(ranges)...); } } // namespace zug zug-0.1.1/zug/maybe_reduced.hpp000066400000000000000000000025431451275774000164440ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include namespace zug { //! @defgroup maybe_reduced //! @{ /*! * Tag for `maybe_reduced` state wrapper. */ struct maybe_reduced_tag {}; /*! * State wrapper for transducers that may want to signal that the reduction is * finished. */ template using maybe_reduced = state_wrapper; inline bool state_wrapper_data_is_reduced(maybe_reduced_tag, bool is_reduced) { return is_reduced; } /*! * Wraps `x` in a `maybe_reduced`, where `is_reduced` contains whether the * reduction should actually finish. */ template auto reduced_if(T&& x, bool is_reduced) -> maybe_reduced> { return maybe_reduced>{std::forward(x), is_reduced}; } /*! * Wraps `x` such that the reduction should finish. */ template decltype(auto) reduced(T&& x) { return reduced_if(std::forward(x), true); } /*! * Wraps `x` such that the reduction should continue. */ template decltype(auto) not_reduced(T&& x) { return reduced_if(std::forward(x), false); } //! @} } // namespace zug zug-0.1.1/zug/meta.hpp000066400000000000000000000041071451275774000146000ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include #include #include #include namespace zug { namespace detail { struct output_of_rf_t { template constexpr auto operator()(StateT, InputTs&&...) const -> meta::pack; }; } // namespace detail //! @defgroup meta //! @{ /*! * Metafunction that given a transducer `XformT` and some inputs `InputTs`, * returns the type of the outputs of the transducer, wrapped in a `meta::pack`. * It preserves reference types. */ template struct output_of { using type = decltype( state_complete(std::declval()(detail::output_of_rf_t{})( std::declval(), std::declval()...))); }; template struct output_of> { using type = typename output_of::type; }; template using output_of_t = typename output_of::type; /*! * Metafunction that given a transducer `XformT` and some inputs `InputTs`, * returns the type of the output of the transducer, combined as combined in a * single result with `tuplify`. */ template struct result_of { using type = std::decay_t()( last)(std::declval(), std::declval()...)))>; }; template struct result_of> { using type = typename result_of::type; }; template using result_of_t = typename result_of::type; //! @} } // namespace zug zug-0.1.1/zug/meta/000077500000000000000000000000001451275774000140655ustar00rootroot00000000000000zug-0.1.1/zug/meta/common_type.hpp000066400000000000000000000060231451275774000171300ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include #include #include namespace zug { namespace meta { /*! * Result of `CommonType` when no common type exists for types `Ts` */ template struct could_not_find_common_type { template could_not_find_common_type(T&&) {} could_not_find_common_type(from_void&&) {} }; namespace detail { //! // Removes r-value reference from type `ValT` if `T1` and `T2` is not an r-value // reference. This allows to remove unexpected r-references from a type that // were added because of the use of `declval`. // template struct undeclval : std::conditional_t::value && !std::is_rvalue_reference::value && !std::is_rvalue_reference::value, std::remove_reference, identity> {}; template struct common_type_impl { using type = could_not_find_common_type; }; template struct common_type_impl< T, U, void_t() : std::declval())>> { using type = typename undeclval() : std::declval()), T, U>::type; }; } // namespace detail /*! * Similar to `std::common_type` but addresses several issues. First, * on Clang 3.4, `common_type` fails for `void`, where it should not. * Also, the standard common type removes qualification, which we want * to preserve. Also, `common_type` is SFINAE-friendly only in new * versions of GCC. * * This implementation preserves qualification when possible, and also * is total. When no common type is found, it returns the special type * `CouldNotFindCommonType`, which can be instantiated and converted * from anything. This makes it easier to write functions that return * a common-type of other types, but that might be used in expressions * where the return type is to be discarded. This erroneous type was * chosen instead of `void` to make debugging easier. */ template struct common_type; template <> struct common_type<> { using type = could_not_find_common_type<>; }; template struct common_type { using type = Acc; }; template struct common_type : common_type::type, Rest...> {}; template using common_type_t = typename common_type::type; } // namespace meta } // namespace zug zug-0.1.1/zug/meta/detected.hpp000066400000000000000000000077441451275774000163730ustar00rootroot00000000000000// // zug: transducers for C++ // Copyright (C) 2019 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include #include namespace zug { namespace meta { namespace detail { template class Op, class... Args> struct detector { using value_t = std::false_type; using type = Default; }; template class Op, class... Args> struct detector>, Op, Args...> { using value_t = std::true_type; using type = Op; }; } // namespace detail /*! * Similar to TS Fundamentals 2 `std::nonesuch` */ struct nonesuch { nonesuch() = delete; ~nonesuch() = delete; nonesuch(nonesuch const&) = delete; void operator=(nonesuch const&) = delete; }; /*! * Similar to TS Fundamentals 2 `std::is_detected` */ template