diff --git a/.bumpversion.cfg b/.bumpversion.cfg index ec593fdc94..8499c0131b 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.0.3 +current_version = 1.0.0 commit = True tag = True diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ecae920df9..ab5984c0d0 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -7,12 +7,13 @@ on: - '**' schedule: - cron: '0 0 * * 0' + workflow_dispatch: jobs: tidy: name: Enforce Tidyness - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: 'recursive' - run: sudo apt-get update -qq @@ -22,16 +23,24 @@ jobs: name: Test Tidy Enforcement on macOS runs-on: macos-11 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: + # sometimes checkout action doesn't result in clean directory? + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha || github.ref }} submodules: 'recursive' + - run: git status --ignored - run: brew install coreutils findutils gnu-sed gawk grep rename sphinx-doc # adapted from https://stackoverflow.com/a/42878119 - run: brew link sphinx-doc --force + # updates PATH to use Homebrew-installed tools first + # see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path + - run: echo "/usr/local/bin:$PATH" > $GITHUB_PATH + - run: echo $PATH - run: ./ci/test_tidy.sh test: name: Tests - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: @@ -47,14 +56,21 @@ jobs: CXX: ${{ matrix.cxx }} TEST_SET: ${{ matrix.test-set }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: 'recursive' + - name: Set up GCC + uses: egor-tensin/setup-gcc@v1 + with: + version: 11 + - name: Set up clang + uses: egor-tensin/setup-clang@v1 + with: + version: 15 - run: sudo apt-get update -qq - run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - run: sudo apt-get update -qq - - run: sudo apt-get install -qq g++-8 cmake build-essential python3-pip python3-virtualenv nodejs tar gzip libpthread-stubs0-dev libc6-dbg gdb - - run: sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90 + - run: sudo apt-get install -qq cmake build-essential python3-pip python3-virtualenv nodejs tar gzip libpthread-stubs0-dev libc6-dbg gdb - run: git fetch origin master:refs/remotes/origin/master - run: make install-test-dependencies - run: ${CXX} --version @@ -63,14 +79,17 @@ jobs: make ${TEST_SET} CXX=${CXX} test-web: name: Web Tests - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: 'recursive' + - name: Set up GCC + uses: egor-tensin/setup-gcc@v1 + with: + version: 11 - run: sudo apt-get update -qq - - run: sudo apt-get install -qq g++-8 cmake build-essential python3-pip python3-virtualenv nodejs tar gzip libpthread-stubs0-dev libc6-dbg gdb - - run: sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90 + - run: sudo apt-get install -qq cmake build-essential python3-pip python3-virtualenv nodejs tar gzip libpthread-stubs0-dev libc6-dbg gdb - run: make install-test-dependencies - name: Run headless test uses: GabrielBB/xvfb-action@v1 @@ -78,45 +97,91 @@ jobs: run: make test-web test-coverage: name: Measure Test Coverage - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - name: Set up clang + uses: egor-tensin/setup-clang@v1 + with: + version: 15 + - uses: actions/checkout@v3 with: submodules: 'recursive' - run: sudo apt-get update -qq - - run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - - run: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - - run: sudo apt-add-repository "deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-7 main" - - run: sudo apt-get update -qq - - run: sudo apt-get install -qq g++-8 - - run: sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90 - - run: sudo apt-get install cmake build-essential python3-virtualenv python3-pip nodejs tar gzip libclang-7-dev llvm llvm-dev libllvm7 llvm-7 llvm-7-dev clang-7 libstdc++-7-dev # might have to happen after we update g++ - - run: sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-7 90 - - run: sudo update-alternatives --install /usr/bin/llvm-config llvm-config /usr/bin/llvm-config-7 90 - - run: sudo update-alternatives --install /usr/bin/llvm-profdata llvm-profdata /usr/bin/llvm-profdata-7 90 - - run: sudo update-alternatives --install /usr/bin/llvm-cov llvm-cov /usr/bin/llvm-cov-7 90 + - run: sudo apt-get install cmake build-essential python3-virtualenv python3-pip nodejs tar gzip clang llvm-dev libclang-dev - run: git fetch origin master:refs/remotes/origin/master - run: make install-test-dependencies - - run: export CXX=clang++-7 && make install-coverage-dependencies - - run: export CXX=clang++-7 && make coverage + - run: export CXX=clang++ && make install-coverage-dependencies + - run: export CXX=clang++ && make coverage - run: curl -s https://codecov.io/bash | bash + # adapted from https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions#publishing-a-package-using-an-action + build-container: + name: Build Docker Image + runs-on: ubuntu-22.04 + env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Log in to GitHub Container Registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: type=raw,value=ACTIONS_BUILD_${{ github.run_number }} + - name: Push to GHCR + uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} test-documentation: name: Test Documentation Build - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 + needs: build-container + container: + image: ghcr.io/devosoft/empirical:ACTIONS_BUILD_${{ github.run_number }} + # github actions requires root to access mounted volumes + options: --user root steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: 'recursive' - - uses: mmore500/actions-setup-docker@94429ebc8d9edb4e8c8afb2667bce1e89435f74f - - run: docker build -t devosoft/empirical . - - run: docker ps -a - - run: sudo docker run --name empirical devosoft/empirical /bin/bash -c "set -o pipefail && cd /opt/Empirical/doc && make html coverage | ./headtail.sh && python /opt/Empirical/doc/parse_documentation_coverage.py /opt/Empirical/doc/_build/doc-coverage.json >> /opt/Empirical/doc-coverage.json" - - run: sudo docker cp empirical:/opt/Empirical/doc-coverage.json . + - run: doxygen + - run: cd doc && python make_md.py + - run: cd doc && make html | ./headtail.sh + - run: cd doc && make coverage + - run: ls doc/ && ls doc/_build + - run: cd doc && python parse_documentation_coverage.py _build/doc-coverage.json >> doc-coverage.json + - uses: actions/upload-artifact@v2 + with: + name: doc-coverage + path: doc/doc-coverage.json + deploy-documentation-coverage: + name: Deploy Documentation Coverage + runs-on: ubuntu-22.04 + if: github.ref == 'refs/heads/master' + needs: test-documentation + steps: + - uses: actions/download-artifact@v2 + with: + name: doc-coverage + path: data - uses: sylvanld/action-storage@v1 - if: github.ref == 'refs/heads/master' with: - src: doc-coverage.json - dst: stats/doc-coverage.json + src: data/doc-coverage.json + dst: data/doc-coverage.json paper: runs-on: ubuntu-latest name: Paper Draft @@ -137,9 +202,12 @@ jobs: # PDF. Note, this should be the same directory as the input # paper.md path: paper.pdf - deploy-dockerhub: + deploy-container: name: Deploy to DockerHub - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 + permissions: + contents: read + packages: write if: github.ref == 'refs/heads/master' needs: - tidy diff --git a/.gitignore b/.gitignore index d4668f7843..ac2e137bd6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *~ +*.csv *.debug *.dSYM *.gcov @@ -17,6 +18,8 @@ *.dat *.exe *tmp.* +*-bak.* +*-bak2.* */env/* */3nv/* @@ -24,9 +27,6 @@ .DS_Store a.out a.out.js -fitness.csv -population.csv -systematics.csv incoming/ tmp/ @@ -48,6 +48,7 @@ demos/Emphatic/Emphatic demos/Emphatic/examples/ConceptTest demos/Emphatic/examples/ConceptTest.cpp demos/MABE/examples/NK +demos/MAP-Elites-Arm/web/MAP-Elites-Arm.js demos/NK.bak/ demos/NK/NK demos/NK/web/NK.js @@ -76,6 +77,13 @@ demos/NK/web/jquery-1.11.2.min.js demos/NK/web/NK.asm.js demos/NK/web/NK.html demos/NK/web/NK.js.mem +demos/utils/words/Wordle/Wordle +demos/utils/words/Wordle/web/ +demos/utils/words/annotate-length +demos/utils/words/has-only +demos/utils/words/wordlists/ +demos/utils/words/wordplay-remove +demos/utils/words/wordplay-shuffle doc/doxygen/ examples/*/* @@ -88,7 +96,6 @@ examples/*/* !examples/*/Makefile !examples/timing/BENCHMARKS -tests/*.csv tests/StatsConfig.cfg tests/web/*.js tests/web/package.json diff --git a/.gitmodules b/.gitmodules index b6dbc3bf5d..8a533ca029 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,10 +10,6 @@ path = third-party/cereal url = https://github.com/mmore500/cereal.git shallow = true -[submodule "third-party/span-lite"] - path = third-party/span-lite - url = https://github.com/martinmoene/span-lite.git - shallow = true [submodule "third-party/robin-hood-hashing"] path = third-party/robin-hood-hashing url = https://github.com/martinus/robin-hood-hashing.git diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000000..0abbb86079 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,25 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.10" + jobs: + pre_build: + - doxygen + - cd doc && python make_md.py + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: doc/conf.py + +# Optionally declare the Python requirements required to build your docs +python: + install: + - requirements: doc/requirements.txt diff --git a/.readthedocs.yml b/.readthedocs.yml deleted file mode 100644 index e862e52db5..0000000000 --- a/.readthedocs.yml +++ /dev/null @@ -1,19 +0,0 @@ -# .readthedocs.yml -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -#Required -version: 2 - -# Build documentation in the docs/ directory with Sphinx -sphinx: - configuration: doc/conf.py - -# Optionally build your docs in additional formats such as PDF -formats: [] - -# Optionally set the version of Python and requirements required to build your docs -python: - version: 3.7 - install: - - requirements: doc/requirements.txt diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..cee67ba4ef --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,48 @@ +# Contributing to Empirical + +We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's: + +- Reporting a bug +- Proposing new features +- Submitting new code +- Writing documentation + +## Reporting bugs + +To report bugs, please [open an issue on Github](https://github.com/devosoft/Empirical/issues) +clearly stating the observed (buggy) behavior (screenshots are encouraged), the desired behavior, +which operating system and compiler you're using (including version), and any additional information that might +help us fix the bug. If possible, please provide a minimum example that reproduces the bug. + +## Requesting features + +We welcome suggestions for how to make Empirical better! To give us your ideas, [open an issue on Github](https://github.com/devosoft/Empirical/issues). + +## Submitting new code and documentation + +Empirical is developed by a group of computational scientists, none of whom can be full-time software developers. +We try to fix bugs and implement new features as quickly as we can, but we can't make any promises. +For these reasons, we highly encourage contributors to submit code fixing bugs or implementing new features! +By working together, we can have a more versatile and robust library than any of us could write individually. + +Before writing new code, we reccomend opening an issue or commenting on an existing one to make sure that we're all on the +same page and agree that your contribution will be consistent with Empirical's goals. + +In-depth guides to [getting started with Empirical development](https://empirical.readthedocs.io/en/latest/dev/getting-started.html), +[coding guidelines for Empirical development](https://empirical.readthedocs.io/en/latest/dev/contribution-guidelines-and-review.html), +[writing documentation](https://empirical.readthedocs.io/en/latest/dev/adding-documentation.html), and +[writing tests](https://empirical.readthedocs.io/en/latest/dev/guide-to-testing.html) are available in the documentation. + +Once you start working on a feature, we recommend making a pull request and giving it the "Merge: not ready" label. This way it's easy for everyone +to keep tabs on ongoing Empirical development and the status of new features. Once you're done developing, remove the "Merge: not ready" label and +add the "Merge: ready" label so that we know your pull request is ready for review. + +As part of your pull request, please add yourself to the list of contributors in the [license file](LICENSE.md). + +## Code of Conduct + +Please note we have a [code of conduct](CODE_OF_CONDUCT.md) and follow it in all your interactions with the project. + +## License + +By contributing, you agree that your contributions will be released under [Empirical's MIT License](LICENSE.md). diff --git a/Dockerfile b/Dockerfile index a06368ac46..238d21c095 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Pull base image. -FROM ubuntu:bionic-20210416 +FROM ubuntu:focal-20230412 COPY . /opt/Empirical @@ -8,6 +8,7 @@ SHELL ["/bin/bash", "-c"] # Prevent interactive time zone config. # adapted from https://askubuntu.com/a/1013396 ENV DEBIAN_FRONTEND=noninteractive +ENV SPHINXBUILD="python3.10 -m sphinx" RUN \ echo 'Acquire::http::Timeout "60";' >> "/etc/apt/apt.conf.d/99timeout" \ @@ -23,30 +24,21 @@ RUN \ # remove -backports, -updates, -proposed, -security repositories # looks like we have to grab libxxhash0 from -updates now RUN \ - apt-get update -y \ + for n in $(seq 1 5); do apt-get update -y && sleep 5 && break; done \ && \ - apt-get install --no-install-recommends libxxhash0 \ + apt-get install --no-install-recommends -y libxxhash0 software-properties-common \ && \ - apt-get clean \ - && \ - rm -rf /var/lib/apt/lists/* \ - && \ - find /etc/apt -type f -name '*.list' -exec sed -i 's/\(^deb.*-backports.*\)/#\1/; s/\(^deb.*-updates.*\)/#\1/; s/\(^deb.*-proposed.*\)/#\1/; s/\(^deb.*-security.*\)/#\1/' {} + \ + for n in $(seq 1 5); do add-apt-repository -y ppa:ubuntu-toolchain-r/test && sleep 5 && break; done \ && \ - apt-get update -y \ + for n in $(seq 1 5); do add-apt-repository -y ppa:deadsnakes/ppa && sleep 5 && break; done \ && \ - apt-get install -y software-properties-common=0.96.24.32.1 \ - && \ - add-apt-repository -y ppa:ubuntu-toolchain-r/test \ - && \ - apt-get update -y \ + for n in $(seq 1 5); do apt-get update -y && sleep 5 && break; done \ && \ apt-get install --no-install-recommends --allow-downgrades -y \ - dpkg-dev \ - libc6=2.27-3ubuntu1 \ - libc6-dev \ - libc6-dbg \ build-essential \ + dpkg-dev \ + g++-11 \ + libc6 \ xvfb \ x11vnc \ x11-xkb-utils \ @@ -60,21 +52,10 @@ RUN \ libnss3 \ lsb-release \ xdg-utils \ - g++-8=8-20180414-1ubuntu2 \ - gcc-8-base=8-20180414-1ubuntu2 \ - cpp-8=8-20180414-1ubuntu2 \ - gcc-8=8-20180414-1ubuntu2 \ - gcc-8-base=8-20180414-1ubuntu2 \ - libgcc-8-dev \ - libstdc++-8-dev \ cmake \ - python-virtualenv \ - python-pip-whl \ - python-pip \ - python-setuptools \ - python3-setuptools \ - python3-virtualenv \ - python3-pip \ + 'python3\.10' \ + 'python3\.10-distutils' \ + 'python3\.10-venv' \ nodejs \ npm \ tar \ @@ -84,7 +65,7 @@ RUN \ doxygen \ curl \ perl \ - perl-base=5.26.1-6 \ + perl-base \ git \ htop \ man \ @@ -97,6 +78,7 @@ RUN \ ssh-client \ libasound2 \ gpg-agent \ + doxygen \ && \ apt-get clean \ && \ @@ -104,6 +86,14 @@ RUN \ && \ echo "installed apt packages" +# Set Python 3.10 as the default version of Python 3 +RUN \ + update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.10 1 \ + && \ + update-alternatives --set python3 /usr/bin/python3.10 \ + && \ + ln -s /usr/bin/python3.10 /usr/bin/python + RUN \ echo $' \n\ XVFB=/usr/bin/Xvfb \n\ @@ -162,12 +152,14 @@ ENV DISPLAY :99 RUN echo 'kernel.unprivileged_userns_clone=1' > /etc/sysctl.d/userns.conf RUN \ - update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90 \ + update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 90 \ && \ npm install -g n \ && \ n 14.17 \ && \ + hash -r \ + && \ export python="/usr/bin/python3" \ && \ npm install source-map \ @@ -175,11 +167,19 @@ RUN \ echo "finalized set up dependency versions" RUN \ - pip install wheel==0.30.0 \ + python3.10 --version \ + && \ + python3 --version \ + && \ + curl -sS https://bootstrap.pypa.io/get-pip.py | python3.10 \ && \ - pip3 install wheel==0.30.0 \ + python3.10 -m pip install --upgrade --force-reinstall pip virtualenv \ && \ - pip3 install -r /opt/Empirical/doc/requirements.txt \ + python3.10 -m pip install wheel==0.30.0 six==1.16.0 \ + && \ + python3.10 -m pip install -r /opt/Empirical/third-party/requirements.txt \ + && \ + python3.10 -m pip install -r /opt/Empirical/doc/requirements.txt \ && \ echo "installed documentation build requirements" @@ -190,13 +190,19 @@ RUN \ && \ git submodule init \ && \ - git submodule update -f \ + echo "nameserver 8.8.8.8" > /etc/resolv.conf \ + && \ + n=0; until [ $n -ge 3 ]; do git submodule update -f && break || ((n++)); sleep 5; done; if [ $n -eq 3 ]; then echo "Update failed after 3 attempts."; else echo "Update successful!"; fi \ && \ echo "initialized submodules" RUN \ cd /opt/Empirical \ && \ + curl -sS https://bootstrap.pypa.io/get-pip.py | python3 \ + && \ + python3 -m pip install virtualenv \ + && \ make install-test-dependencies \ && \ echo "installed test dependencies" @@ -254,13 +260,6 @@ RUN \ && \ echo "installed karma-firefox-launcher" -# @mmore500 10-2021: python3 -m pip fixes UnicodeDecodeError -# when installing charset-normalizer from github -RUN \ - python3 -m pip install -r /opt/Empirical/third-party/requirements.txt \ - && \ - echo "installed documentation build requirements" - # Perform any further action as an unprivileged user. # adapted from https://stackoverflow.com/a/27703359 # and https://superuser.com/a/235398 diff --git a/Doxyfile b/Doxyfile index 8fc9188076..31abf156e7 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.10 +# Doxyfile 1.9.7 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -12,16 +12,26 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 @@ -32,7 +42,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "Empirical" +PROJECT_NAME = Empirical # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -58,18 +68,30 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = "doc/doxygen" +OUTPUT_DIRECTORY = doc/doxyoutput -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes -# performance problems for the file system. +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode @@ -81,14 +103,14 @@ ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English @@ -133,7 +155,7 @@ ALWAYS_DETAILED_SEC = NO # operators of the base classes will not be shown. # The default value is: NO. -INLINE_INHERITED_MEMB = YES +INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the @@ -179,6 +201,16 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus @@ -199,6 +231,14 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. @@ -222,20 +262,19 @@ TAB_SIZE = 2 # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) ALIASES = -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all @@ -264,28 +303,40 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. @@ -293,6 +344,26 @@ EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN Use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0. and GITHUB Use the lower case version of title +# with any whitespace replaced by '-' and punctations characters removed.. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or @@ -309,7 +380,7 @@ AUTOLINK_SUPPORT = YES # diagrams that involve STL classes more complete and accurate. # The default value is: NO. -BUILTIN_STL_SUPPORT = NO +BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. @@ -318,7 +389,7 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. @@ -404,6 +475,27 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -424,6 +516,12 @@ EXTRACT_ALL = YES EXTRACT_PRIVATE = NO +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. @@ -442,7 +540,7 @@ EXTRACT_STATIC = YES # for Java sources. # The default value is: YES. -EXTRACT_LOCAL_CLASSES = NO +EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are @@ -461,6 +559,13 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation @@ -472,17 +577,18 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. -HIDE_FRIEND_COMPOUNDS = NO +HIDE_FRIEND_COMPOUNDS = YES # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these @@ -498,12 +604,20 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. -# The default value is: system dependent. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. CASE_SENSE_NAMES = YES @@ -521,11 +635,17 @@ HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. -SHOW_INCLUDE_FILES = YES +SHOW_INCLUDE_FILES = NO # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader @@ -571,7 +691,7 @@ SORT_BRIEF_DOCS = NO # detailed member documentation. # The default value is: NO. -SORT_MEMBERS_CTORS_1ST = NO +SORT_MEMBERS_CTORS_1ST = YES # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will @@ -661,7 +781,7 @@ SHOW_FILES = YES # Folder Tree View (if specified). # The default value is: YES. -SHOW_NAMESPACES = YES +SHOW_NAMESPACES = NO # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from @@ -678,7 +798,8 @@ FILE_VERSION_FILTER = # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE @@ -689,7 +810,7 @@ LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. @@ -724,36 +845,83 @@ WARNINGS = YES WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = NO +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. +# The default value is: NO. + +WARN_AS_ERROR = FAIL_ON_WARNINGS_PRINT + # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). -WARN_LOGFILE = +WARN_LOGFILE = doxygen_warnings.txt #--------------------------------------------------------------------------- # Configuration options related to the input files @@ -765,17 +933,28 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = +INPUT = include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. @@ -784,13 +963,20 @@ INPUT_ENCODING = UTF-8 # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, -# *.vhdl, *.ucf, *.qsf, *.as and *.js. +# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, +# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C +# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. -FILE_PATTERNS = *.cpp, *.cc, *.h *.hpp +FILE_PATTERNS = *.cpp \ + *.cc \ + *.h \ + *.hpp # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -821,22 +1007,25 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = */demos/* -EXCLUDE_PATTERNS += */examples/* -EXCLUDE_PATTERNS += */tests/config/* -EXCLUDE_PATTERNS += */third-party/* +EXCLUDE_PATTERNS = */demos/* \ + */examples/* \ + */tests/config/* \ + */third-party/* \ + */in_progress/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* +# ANamespace::AClass, ANamespace::*Test + +EXCLUDE_SYMBOLS = EMP_* \ + EM_* \ + internal \ + detail \ + decltypedetail \ + __* -EXCLUDE_SYMBOLS = EMP_* -EXCLUDE_SYMBOLS += EM_* # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include @@ -878,8 +1067,17 @@ IMAGE_PATH = # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. +# +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. -INPUT_FILTER = +INPUT_FILTER = doc/filter_namespace # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the @@ -887,6 +1085,10 @@ INPUT_FILTER = # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. FILTER_PATTERNS = @@ -912,6 +1114,15 @@ FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- @@ -939,7 +1150,7 @@ INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO @@ -971,12 +1182,12 @@ SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # @@ -996,7 +1207,7 @@ USE_HTAGS = NO # See also: Section \class. # The default value is: YES. -VERBATIM_HEADERS = YES +VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index @@ -1009,17 +1220,11 @@ VERBATIM_HEADERS = YES ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 4 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = @@ -1098,7 +1303,12 @@ HTML_STYLESHEET = # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = @@ -1113,10 +1323,23 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1125,7 +1348,7 @@ HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1143,14 +1366,16 @@ HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_TIMESTAMP = NO +HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the @@ -1175,13 +1400,14 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1195,6 +1421,13 @@ GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. @@ -1220,8 +1453,12 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1251,7 +1488,7 @@ CHM_FILE = HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated -# (YES) or that it should be included in the master .chm file (NO). +# (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1278,6 +1515,16 @@ BINARY_TOC = NO TOC_EXPAND = NO +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help @@ -1296,7 +1543,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1304,8 +1552,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1313,30 +1561,30 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1379,16 +1627,28 @@ DISABLE_INDEX = NO # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # @@ -1413,6 +1673,24 @@ TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML @@ -1422,19 +1700,14 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. -FORMULA_TRANSPARENT = YES +FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path @@ -1444,11 +1717,29 @@ FORMULA_TRANSPARENT = YES USE_MATHJAX = NO +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + # When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1461,22 +1752,29 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1504,7 +1802,7 @@ MATHJAX_CODEFILE = SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH # setting. When disabled, doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing @@ -1523,7 +1821,8 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1536,8 +1835,9 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1575,7 +1875,7 @@ EXTRA_SEARCH_MAPPINGS = # If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. -GENERATE_LATEX = YES +GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1588,21 +1888,35 @@ LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # -# Note that when enabling USE_PDFLATEX this option is only used for generating -# bitmaps for formulas in the HTML output, but not in the Makefile that is -# written to the output directory. -# The default file is: latex. +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = makeindex + # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. @@ -1632,29 +1946,31 @@ PAPER_TYPE = a4 EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the -# generated LaTeX document. The header should contain everything until the first -# chapter. If it is left blank doxygen will generate a standard header. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that doxygen normally uses. # -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empty -# string, for the replacement values of the other commands the user is referred -# to HTML_HEADER. +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the -# generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. See +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. -# -# Note: Only use a user-defined footer if you know what you are doing! +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = @@ -1687,18 +2003,26 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES, to get a -# higher quality PDF documentation. +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode -# command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. This option is also used -# when generating formulas in HTML. +# The LATEX_BATCHMODE tag ignals the behavior of LaTeX in case of an error. +# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch +# mode nothing is printed on the terminal, errors are scrolled as if is +# hit at every error; missing files that TeX tries to input or request from +# keyboard input (\read on a not open input stream) cause the job to abort, +# NON_STOP In nonstop mode the diagnostic message will appear on the terminal, +# but there is no possibility of user interaction just like in batch mode, +# SCROLL In scroll mode, TeX will stop only for missing files to input or if +# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at +# each error, asking for user intervention. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1711,24 +2035,22 @@ LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO - # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BIB_STYLE = plain +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- @@ -1768,9 +2090,9 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, -# missing definitions are set to their default value. +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the # default style sheet that doxygen normally uses. @@ -1779,22 +2101,12 @@ RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = -# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code -# with syntax highlighting in the RTF output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_SOURCE_CODE = NO - #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- @@ -1866,6 +2178,13 @@ XML_OUTPUT = xml XML_PROGRAMLISTING = YES +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- @@ -1884,23 +2203,14 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. -# The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_PROGRAMLISTING = NO - #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sf.net) file that captures the -# structure of the code including all documentation. Note that this feature is -# still experimental and incomplete at the moment. +# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO @@ -1960,7 +2270,7 @@ ENABLE_PREPROCESSING = YES # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -MACRO_EXPANSION = NO +MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then # the macro expansion is limited to the macros specified with the PREDEFINED and @@ -1979,7 +2289,8 @@ SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the -# preprocessor. +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = @@ -2000,7 +2311,7 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = DOXYGEN_RUNNING +PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The @@ -2067,41 +2378,10 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to diagram generator tools #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# You can include diagrams made with dia in doxygen documentation. Doxygen will -# then run dia to produce the diagram and insert it in the documentation. The -# DIA_PATH tag allows you to specify the directory where the dia binary resides. -# If left empty dia is assumed to be found in the default search path. - -DIA_PATH = - # If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2110,7 +2390,7 @@ HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: -# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO # The default value is: NO. @@ -2127,35 +2407,52 @@ HAVE_DOT = NO DOT_NUM_THREADS = 0 -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. +# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of +# subgraphs. When you want a differently looking font in the dot files that +# doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# For details please see Node, +# Edge and Graph Attributes specification You need to make sure dot is able +# to find the font, which can be done by putting it in a standard location or by +# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" + +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about +# arrows shapes. +# The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTNAME = Helvetica +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification +# The default value is: shape=box,height=0.2,width=0.4. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTSIZE = 10 +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for -# each documented class showing the direct and indirect inheritance relations. -# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will +# generate a graph for each documented class showing the direct and indirect +# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and +# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case +# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the +# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. +# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance +# relations will be shown as texts / links. +# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. CLASS_GRAPH = YES @@ -2169,7 +2466,8 @@ CLASS_GRAPH = YES COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. +# groups, showing the direct groups dependencies. See also the chapter Grouping +# in the manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2192,10 +2490,32 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2220,7 +2540,7 @@ INCLUDE_GRAPH = YES # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -INCLUDED_BY_GRAPH = YES +INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH tag is set to YES then doxygen will generate a call # dependency graph for every global function or class method. @@ -2262,10 +2582,17 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 + # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: -# http://www.graphviz.org/)). +# https://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). @@ -2302,11 +2629,12 @@ DOT_PATH = DOTFILE_DIRS = -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the \mscfile -# command). +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. -MSCFILE_DIRS = +DIA_PATH = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile @@ -2315,13 +2643,18 @@ MSCFILE_DIRS = DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file. If left blank, it is assumed -# PlantUML is not used or called during a preprocessing step. Doxygen will -# generate a warning when it encounters a \startuml command in this case and -# will not generate output for the diagram. +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + # When using plantuml, the specified paths are searched for files specified by # the !include statement in a plantuml block. @@ -2351,18 +2684,6 @@ DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO - # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support @@ -2375,14 +2696,34 @@ DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc temporary +# files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will +# use a built-in version of mscgen tool to produce the charts. Alternatively, +# the MSCGEN_TOOL tag can also specify the name an external tool. For instance, +# specifying prog as the value, doxygen will call the tool as prog -T +# -o . The external tool should support +# output file formats "png", "eps", "svg", and "ismap". + +MSCGEN_TOOL = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = diff --git a/doc/LICENSE.md b/LICENSE.md similarity index 74% rename from doc/LICENSE.md rename to LICENSE.md index 937f082ae4..4ad7f8c871 100644 --- a/doc/LICENSE.md +++ b/LICENSE.md @@ -1,11 +1,31 @@ -=============================================================================== -The MIT License (MIT) +# The MIT License (MIT) + +Copyright (c) 2015-2023 Michigan State University Digital Evolution Laboratory + +Contributors: Charles Ofria, + Emily Dolson, + Jacob Fenton, + Riley Hoffman, + Steven Jorgensen, + Alex Lalejini, + Robin Miller, + Matthew Moreno, + Austin Ferguson, + Jose Guadalupe Hernandez, + Katherine Perry, + Santiago Rodriguez-Papa, + Oliver Baldwin Edwards, + Sarah Boyd, + Tait Weicht, + Jason Stredwick, + Raheem Clemons, + Anya Vostinar, + Luis Zaman, + Ryan Moreno, + Nitash C G, + Jory Schossau, + and Dylan Rainbow -Copyright (c) 2015-2020 Michigan State University -Contributers: Charles Ofria, Emily Dolson, Jacob Fenton, Riley Hoffman, - Steven Jorgensen, Alex Lalejini, Robin Miller, Matthew Moreno, - Katherine Perry, Santiago Rodriguez-Papa, Jory Schossau, - Jason Stredwick, Anya Vostinar, and Luis Zaman Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -57,31 +77,6 @@ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -=============================================================================== -This repository also contains the source code for C3.js (https://github.com/c3js/c3), which is available -under the MIT License: - -The MIT License (MIT) - -Copyright (c) 2013 Masayuki Tanaka - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - =============================================================================== This repository also contains the source code for d3-tip.js (https://github.com/Caged/d3-tip), which is available @@ -113,3 +108,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This repository contains source code adapted from the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception. + +=============================================================================== + +This repository also contains a Bloom filter class was written by Arash Partow (http://www.partow.net/programming/hashfunctions/index.html) and distributed under the MIT License. @copyright Arash Partow, 2000 (modified slightly by Emily Dolson) + +=============================================================================== + +See directories within the third-party directory for information about the licenses dependencies are under. diff --git a/Makefile b/Makefile index 80b57a41ae..c2c8a002f4 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,8 @@ test-cookiecutter: ../cookiecutter-empirical-project cd ../cookiecutter-empirical-project && make clean && make test doc: + doxygen + cd doc && python make_md.py cd doc && make html coverage coverage: diff --git a/Planning/NEXT_PASS b/Planning/NEXT_PASS new file mode 100644 index 0000000000..3568548ccb --- /dev/null +++ b/Planning/NEXT_PASS @@ -0,0 +1,13 @@ +Next time we do a full pass through files, we should: + +* Use notify for proper error tracking. Bias toward exceptions so developers can choose individualized responses to errors. +* Change member functions (and variables?) to begin with _ +* Cleanup Doxygen? +* Move std::string to emp::String, where possible. +* Cleanup any SFINEA or other template tricks and use constexpr instead. + + +And if implemented +* Simplify emp_asserts to assume that will disentangle tests and print values of components? +* Add in the ability to turn on "EMP_THREADED" options? +* Add in serialization capabilities? diff --git a/ci/impl/generate_boilerplate_file_docstrings.sh b/ci/impl/generate_boilerplate_file_docstrings.sh index a8fe3e8209..38f7d545b6 100755 --- a/ci/impl/generate_boilerplate_file_docstrings.sh +++ b/ci/impl/generate_boilerplate_file_docstrings.sh @@ -13,23 +13,25 @@ for filename in $(cd include && find -- * -name '*.hpp' -type f); do done # stamp in expected boilerplate line-by-line - sed -i '1s|^.*$|/**|' "include/${filename}" - sed -i '2s|^.*$| * @note This file is part of Empirical, https://github.com/devosoft/Empirical|' "include/${filename}" - sed -i '3s|^.*$| * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md|' "include/${filename}" - # only match if a @date isn't currently in place - # if we see @date, "break" (b) sed script for that line + sed -i '1s|^.*$|/*|' "include/${filename}" + sed -i '2s|^.*$| * This file is part of Empirical, https://github.com/devosoft/Empirical|' "include/${filename}" + sed -i '3s|^.*$| * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md|' "include/${filename}" + # only match if a date: isn't currently in place + # if we see date:, "break" (b) sed script for that line # adapted from https://stackoverflow.com/a/5334825 # and https://stackoverflow.com/a/12178023 # and https://stackoverflow.com/a/9053163 - sed -i "/^ \* @date /b; 4s/^.*\$/ * @date $(date +'%Y')/" "include/${filename}" - sed -i '5s/^.*$/ */' "include/${filename}" - sed -i "6s/^.*\$/ * @file $(basename "${filename}")/" "include/${filename}" + sed -i "/^ \* date: /b; 4s/^.*\$/ * date: $(date +'%Y')/" "include/${filename}" + # sed -i '5s/^.*$/ */' "include/${filename}" + sed -i '5s|^.*$|*/|' "include/${filename}" # only match if a @brief isn't currently in place # adapted from https://stackoverflow.com/a/5334825 - sed -i "/^ \* @brief /b; 7s/^.*\$/ * @brief TODO./" "include/${filename}" + sed -i '6s|^.*$|/**|' "include/${filename}" + sed -i '7s|^.*$| * @file|' "include/${filename}" + sed -i "/^ \* @brief /b; 8s/^.*\$/ * @brief TODO./" "include/${filename}" # only match empty lines # add extra * to replace later with */ when constructing fresh - sed -i '8s/^$/ */' "include/${filename}" + sed -i '9s/^$/ */' "include/${filename}" # close boilerplate file docstring # must accomodate possible additional content in docstring diff --git a/ci/impl/generate_license_notices.sh b/ci/impl/generate_license_notices.sh index 01f78eb84b..2264eca653 100755 --- a/ci/impl/generate_license_notices.sh +++ b/ci/impl/generate_license_notices.sh @@ -17,20 +17,23 @@ for filename in $(find . -name '*.cpp' -type f ! -path "./third-party/*") $(find # stamp in expected boilerplate line-by-line # just like file docstrings, but don't require a brief # stamp in expected boilerplate line-by-line - sed -i '1s|^.*$|/**|' "${filename}" - sed -i '2s|^.*$| * @note This file is part of Empirical, https://github.com/devosoft/Empirical|' "${filename}" - sed -i '3s|^.*$| * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md|' "${filename}" + sed -i '1s|^.*$|/*|' "${filename}" + sed -i '2s|^.*$| * This file is part of Empirical, https://github.com/devosoft/Empirical|' "${filename}" + sed -i '3s|^.*$| * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md|' "${filename}" # only match if a @date isn't currently in place # if we see @date, "break" (b) sed script for that line # adapted from https://stackoverflow.com/a/5334825 # and https://stackoverflow.com/a/12178023 # and https://stackoverflow.com/a/9053163 - sed -i "/^ \* @date /b; 4s/^.*\$/ * @date $(date +'%Y')/" "${filename}" - sed -i '5s/^.*$/ */' "${filename}" - sed -i "6s/^.*\$/ * @file $(basename "${filename}")/" "${filename}" + sed -i "/^ \* date: /b; 4s/^.*\$/ * date: $(date +'%Y')/" "${filename}" + sed -i '5s|^.*$|*/|' "${filename}" + # sed -i "6s/^.*\$/ * @file $(basename "${filename}")/" "${filename}" # only match empty lines # add extra * to replace later with */ when constructing fresh - sed -i '7s/^$/ */' "${filename}" + # sed -i '6s/^$/ */' "${filename}" + sed -i '6s|^.*$|/**|' "${filename}" + sed -i '7s|^.*$| * @file|' "${filename}" + sed -i "/^ \* @brief /b; 8s/^.*\$/ * @brief TODO./" "${filename}" # close boilerplate file docstring # must accomodate possible additional content in docstring diff --git a/ci/test_tidy.sh b/ci/test_tidy.sh index 71a54089c6..4dae09a885 100755 --- a/ci/test_tidy.sh +++ b/ci/test_tidy.sh @@ -8,6 +8,7 @@ set -e echo "Running tidyness enforcement tests..." echo "See https://empirical.readthedocs.io/en/latest/dev/guide-to-testing.html#tidyness-enforcement for info on tidyness enforcement." echo "(Including how to automatically generate tidyness fixes if tidyness enforcement detects tidyness issues.)" +git status --ignored ./ci/test_alphabetize_includes.sh && echo "✔ include alphabetization ok" || exit 1 ./ci/test_alphabetize_imports.sh && echo "✔ import alphabetization ok" || exit 1 diff --git a/ci/util/enforce_git_status.sh b/ci/util/enforce_git_status.sh index 488f8f7074..34733a31b0 100755 --- a/ci/util/enforce_git_status.sh +++ b/ci/util/enforce_git_status.sh @@ -10,7 +10,7 @@ fi # refuse to continue if uncommitted changes are present # adapted from https://stackoverflow.com/a/40535565 -if ! [[ -z $(git status -s) ]]; +if [ -n "$(git status --porcelain)" ]; then ./ci/util/print_uncommitted_changes_warning.sh exit 1 diff --git a/ci/util/print_gitignored_files_warning.sh b/ci/util/print_gitignored_files_warning.sh index 60bff93e12..b1f027a73d 100755 --- a/ci/util/print_gitignored_files_warning.sh +++ b/ci/util/print_gitignored_files_warning.sh @@ -1,6 +1,9 @@ #!/bin/bash set -e +git status --ignored +git diff + # adapted from https://stackoverflow.com/a/835561 # and https://stackoverflow.com/a/4327720 # and https://git-scm.com/book/en/v2/Git-Tools-Stashing-and-Cleaning diff --git a/ci/util/print_uncommitted_changes_warning.sh b/ci/util/print_uncommitted_changes_warning.sh index 5d5a7be3aa..52a952a7a9 100755 --- a/ci/util/print_uncommitted_changes_warning.sh +++ b/ci/util/print_uncommitted_changes_warning.sh @@ -1,6 +1,9 @@ #!/bin/bash set -e +git status --ignored +git diff + # adapted from https://stackoverflow.com/a/835561 # and https://stackoverflow.com/a/4327720 echo "=========================================================================" diff --git a/demos/EasyChair/ProcessReviews.cpp b/demos/EasyChair/ProcessReviews.cpp index 4014d101bb..47aad2e74b 100644 --- a/demos/EasyChair/ProcessReviews.cpp +++ b/demos/EasyChair/ProcessReviews.cpp @@ -1,9 +1,11 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file ProcessReviews.cpp + * @file + * @brief TODO. */ #include diff --git a/demos/MAP-Elites-Arm/Makefile b/demos/MAP-Elites-Arm/Makefile index 52b547329f..866d9093ed 100644 --- a/demos/MAP-Elites-Arm/Makefile +++ b/demos/MAP-Elites-Arm/Makefile @@ -3,7 +3,7 @@ PROJECT := MAP-Elites-Arm EMP_DIR := ../../include # Flags to use regardless of compiler -CFLAGS_all := -Wall -Wno-unused-function -std=c++17 -I$(EMP_DIR)/ +CFLAGS_all := -Wall -Wno-unused-function -std=c++20 -I$(EMP_DIR)/ # Native compiler information CXX_nat := g++ @@ -12,7 +12,7 @@ CFLAGS_nat_debug := -g -DEMP_TRACK_MEM -Wnon-virtual-dtor -Wcast-align -Woverloa # Emscripten compiler information CXX_web := emcc -OFLAGS_web_all := -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap']" -s TOTAL_MEMORY=67108864 --js-library $(EMP_DIR)/emp/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=1 #--embed-file configs +OFLAGS_web_all := -s "EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap']" -s TOTAL_MEMORY=67108864 --js-library $(EMP_DIR)/emp/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=1 #--embed-file configs OFLAGS_web := -Oz -DNDEBUG # OFLAGS_web_debug := -g4 -Oz -pedantic -Wno-dollar-in-identifier-extension -s ASSERTIONS=2 OFLAGS_web_debug := -g4 -pedantic -Wno-dollar-in-identifier-extension -s ASSERTIONS=2 diff --git a/demos/MAP-Elites-Arm/source/ArmWorld.hpp b/demos/MAP-Elites-Arm/source/ArmWorld.hpp index 8f3ef64003..54696c340a 100644 --- a/demos/MAP-Elites-Arm/source/ArmWorld.hpp +++ b/demos/MAP-Elites-Arm/source/ArmWorld.hpp @@ -1,10 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file ArmWorld.hpp - * @brief Defines the specialized world for the MAP-Elites app. + * @file */ #ifndef DEMOS_MAP_ELITES_ARM_SOURCE_ARMWORLD_HPP_INCLUDE diff --git a/demos/MAP-Elites-Arm/source/native/MAP-Elites-Arm.cpp b/demos/MAP-Elites-Arm/source/native/MAP-Elites-Arm.cpp index f70a8719bc..574f0652d3 100644 --- a/demos/MAP-Elites-Arm/source/native/MAP-Elites-Arm.cpp +++ b/demos/MAP-Elites-Arm/source/native/MAP-Elites-Arm.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file MAP-Elites-Arm.cpp + * @file * @brief Controller for NATIVE (Command Line) version of MAP-Elites app. */ diff --git a/demos/MAP-Elites-Arm/source/web/MAP-Elites-Arm-web.cpp b/demos/MAP-Elites-Arm/source/web/MAP-Elites-Arm-web.cpp index 54f1bc70c1..a8fc28f6eb 100644 --- a/demos/MAP-Elites-Arm/source/web/MAP-Elites-Arm-web.cpp +++ b/demos/MAP-Elites-Arm/source/web/MAP-Elites-Arm-web.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file MAP-Elites-Arm-web.cpp + * @file * @brief Controller for WEB version of MAP-Elites app. */ diff --git a/demos/NK/Makefile b/demos/NK/Makefile index 85cdf9efb6..5a1f1fdae3 100644 --- a/demos/NK/Makefile +++ b/demos/NK/Makefile @@ -3,7 +3,7 @@ PROJECT := NK EMP_DIR := ../../include # Flags to use regardless of compiler -CFLAGS_all := -Wall -Wno-unused-function -std=c++17 -I$(EMP_DIR)/ +CFLAGS_all := -Wall -Wno-unused-function -std=c++20 -I$(EMP_DIR)/ # Native compiler information CXX_nat := g++ diff --git a/demos/NK/source/NKWorld.hpp b/demos/NK/source/NKWorld.hpp index c7d0c9fe7b..eb3802ced9 100644 --- a/demos/NK/source/NKWorld.hpp +++ b/demos/NK/source/NKWorld.hpp @@ -1,9 +1,11 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file NKWorld.hpp + * @file + * @brief TODO. * */ diff --git a/demos/NK/source/native/NK.cpp b/demos/NK/source/native/NK.cpp index e5a5506041..3229130a09 100644 --- a/demos/NK/source/native/NK.cpp +++ b/demos/NK/source/native/NK.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file NK.cpp + * @file * @brief This file explores the template defined in evo::Population.h with an NK landscape. */ diff --git a/demos/NK/source/web/NK-web.cpp b/demos/NK/source/web/NK-web.cpp index 561138faf4..85aee7373a 100644 --- a/demos/NK/source/web/NK-web.cpp +++ b/demos/NK/source/web/NK-web.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file NK-web.cpp + * @file * @brief This file contains an easy-to-manipulate NK world. */ diff --git a/demos/SelectionAnalyze/Analyze.cpp b/demos/SelectionAnalyze/Analyze.cpp index 64bd52b4c4..6423b88c79 100644 --- a/demos/SelectionAnalyze/Analyze.cpp +++ b/demos/SelectionAnalyze/Analyze.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file Analyze.cpp + * @file * @brief Analyze probabilities for selection using various selection techniques. */ diff --git a/demos/SelectionAnalyze/Lexicase.cpp b/demos/SelectionAnalyze/Lexicase.cpp index 8375fcf720..ab3c62f603 100644 --- a/demos/SelectionAnalyze/Lexicase.cpp +++ b/demos/SelectionAnalyze/Lexicase.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file Lexicase.cpp + * @file * @brief Calculate probabilities for selection using Lexicase Selection. */ diff --git a/demos/SelectionAnalyze/Makefile b/demos/SelectionAnalyze/Makefile index 39543cfd70..3a81bbc910 100644 --- a/demos/SelectionAnalyze/Makefile +++ b/demos/SelectionAnalyze/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/demos/SelectionAnalyze/Roulette.cpp b/demos/SelectionAnalyze/Roulette.cpp index e859df6de9..977f88a7a0 100644 --- a/demos/SelectionAnalyze/Roulette.cpp +++ b/demos/SelectionAnalyze/Roulette.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Roulette.cpp + * @file * @brief Calculate probabilities for selection using Roulette Selection (easy!) */ diff --git a/demos/SelectionAnalyze/SelectionData.hpp b/demos/SelectionAnalyze/SelectionData.hpp index 226ccb33bf..120f0b213d 100644 --- a/demos/SelectionAnalyze/SelectionData.hpp +++ b/demos/SelectionAnalyze/SelectionData.hpp @@ -1,10 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file SelectionData.hpp - * @brief This class maintains all of the fitness data for a population of organisms. + * @file */ #ifndef DEMOS_SELECTIONANALYZE_SELECTIONDATA_HPP_INCLUDE diff --git a/demos/SelectionAnalyze/test_gen.cpp b/demos/SelectionAnalyze/test_gen.cpp index 23242bf576..f78c2a92cd 100644 --- a/demos/SelectionAnalyze/test_gen.cpp +++ b/demos/SelectionAnalyze/test_gen.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file test_gen.cpp + * @file */ #include "emp/config/command_line.hpp" diff --git a/demos/Slideshow.hpp b/demos/Slideshow.hpp index 869cdc945d..0ab0f3a8b5 100644 --- a/demos/Slideshow.hpp +++ b/demos/Slideshow.hpp @@ -1,9 +1,11 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2017 - * - * @file Slideshow.hpp + * @file + * @brief TODO. * */ diff --git a/demos/SpatialCoop2017/Makefile b/demos/SpatialCoop2017/Makefile index dced09e09b..1b61d0e8e9 100644 --- a/demos/SpatialCoop2017/Makefile +++ b/demos/SpatialCoop2017/Makefile @@ -3,7 +3,7 @@ PROJECT := SimplePDWorld EMP_DIR := ../../include # Flags to use regardless of compiler -CFLAGS_all := -Wall -Wno-unused-function -std=c++17 -I$(EMP_DIR)/ +CFLAGS_all := -Wall -Wno-unused-function -std=c++20 -I$(EMP_DIR)/ # Native compiler information CXX_nat := g++ diff --git a/demos/SpatialCoop2017/source/SimplePDWorld.hpp b/demos/SpatialCoop2017/source/SimplePDWorld.hpp index c4c35b7eae..4f35de005d 100644 --- a/demos/SpatialCoop2017/source/SimplePDWorld.hpp +++ b/demos/SpatialCoop2017/source/SimplePDWorld.hpp @@ -1,9 +1,11 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file SimplePDWorld.hpp + * @file + * @brief TODO. */ #ifndef DEMOS_SPATIALCOOP2017_SOURCE_SIMPLEPDWORLD_HPP_INCLUDE diff --git a/demos/SpatialCoop2017/source/native/SimplePDWorld.cpp b/demos/SpatialCoop2017/source/native/SimplePDWorld.cpp index 7edabbae31..075a19b8b0 100644 --- a/demos/SpatialCoop2017/source/native/SimplePDWorld.cpp +++ b/demos/SpatialCoop2017/source/native/SimplePDWorld.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file SimplePDWorld.cpp + * @file */ #include diff --git a/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp b/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp index 4220676634..b538ba1f58 100644 --- a/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp +++ b/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file SimplePDWorld-web.cpp + * @file */ #include "emp/web/web.hpp" diff --git a/demos/Sudoku/Makefile b/demos/Sudoku/Makefile index ff3f1e6658..7d0df6543a 100644 --- a/demos/Sudoku/Makefile +++ b/demos/Sudoku/Makefile @@ -1,5 +1,5 @@ # Flags to use regardless of compiler -CFLAGS_all := -std=c++17 -Wall -Wno-unused-function -I../../include/ +CFLAGS_all := -std=c++20 -Wall -Wno-unused-function -I../../include/ # Emscripten compiler information CXX_web := emcc diff --git a/demos/Sudoku/Sudoku.cpp b/demos/Sudoku/Sudoku.cpp index 4584c45c4f..3f2bc2eae5 100644 --- a/demos/Sudoku/Sudoku.cpp +++ b/demos/Sudoku/Sudoku.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2017 - * - * @file Sudoku.cpp + * @file */ #include "emp/base/array.hpp" diff --git a/demos/utils/data/Histogram.cpp b/demos/utils/data/Histogram.cpp index ebced27c1d..1528f65952 100644 --- a/demos/utils/data/Histogram.cpp +++ b/demos/utils/data/Histogram.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file Histogram.cpp + * @file */ #include "../../../include/emp/base/vector.hpp" diff --git a/demos/utils/data/reorder_cols.cpp b/demos/utils/data/reorder_cols.cpp index 812616da70..b53272d3a3 100644 --- a/demos/utils/data/reorder_cols.cpp +++ b/demos/utils/data/reorder_cols.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file reorder_cols.cpp + * @file * @brief This utility loads in a space-separated file and reorders the columns. * * Identify a file and a set of columns (starting with column 1). Load the diff --git a/demos/utils/data/summarize.cpp b/demos/utils/data/summarize.cpp index e2702612bf..f12a7a45d2 100644 --- a/demos/utils/data/summarize.cpp +++ b/demos/utils/data/summarize.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2020 - * - * @file summarize.cpp + * @file * @brief This file takes in one or more CSV files with values and, for each, calculates the mnimium, * maximum, and average values found in each column. */ diff --git a/demos/utils/edit_dist/edit_dist.cpp b/demos/utils/edit_dist/edit_dist.cpp index c6501a80d8..4542fbbd4a 100644 --- a/demos/utils/edit_dist/edit_dist.cpp +++ b/demos/utils/edit_dist/edit_dist.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file edit_dist.cpp + * @file * @brief Load input from standard in that begins with a value "N" and then contains N pairs of strings. * * Output will be the edit distances between each string pair. diff --git a/demos/utils/edit_dist/string_gen.cpp b/demos/utils/edit_dist/string_gen.cpp index 3a73247be0..e44a1dbff9 100644 --- a/demos/utils/edit_dist/string_gen.cpp +++ b/demos/utils/edit_dist/string_gen.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file string_gen.cpp + * @file * @brief Generate a series of string pairs with a prescribed number of changes between them. */ diff --git a/demos/utils/extras/calc_log.cpp b/demos/utils/extras/calc_log.cpp index e9239bccc8..7398038256 100644 --- a/demos/utils/extras/calc_log.cpp +++ b/demos/utils/extras/calc_log.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file calc_log.cpp + * @file */ #include diff --git a/demos/utils/extras/calc_pow.cpp b/demos/utils/extras/calc_pow.cpp index 45fd5029df..29601a6a4b 100644 --- a/demos/utils/extras/calc_pow.cpp +++ b/demos/utils/extras/calc_pow.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file calc_pow.cpp + * @file */ #include diff --git a/demos/utils/graphs/make_graph.cpp b/demos/utils/graphs/make_graph.cpp index 2e0526007a..e1e58ec3dc 100644 --- a/demos/utils/graphs/make_graph.cpp +++ b/demos/utils/graphs/make_graph.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file make_graph.cpp + * @file * @brief Build graphs of various types in the standard format. * * NOTE: All questions can be answered by providing command-line arguments. diff --git a/demos/utils/graphs/vcover.cpp b/demos/utils/graphs/vcover.cpp index 0fc13cc52c..2ee18f5453 100644 --- a/demos/utils/graphs/vcover.cpp +++ b/demos/utils/graphs/vcover.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file vcover.cpp + * @file * @brief Determine the minimum vertex cover for a graph provided on standard input. */ diff --git a/demos/utils/graphs/web/Makefile b/demos/utils/graphs/web/Makefile index 4725b29149..19ec718967 100644 --- a/demos/utils/graphs/web/Makefile +++ b/demos/utils/graphs/web/Makefile @@ -1,7 +1,7 @@ CXX_web := emcc # OFLAGS_web := -g4 -Wall OFLAGS_web := -oz -DNDEBUG -CFLAGS_web := -std=c++17 $(OFLAGS_web) -s EXPORTED_FUNCTIONS="['_empLoadString']" -I../../../../include/ +CFLAGS_web := -std=c++20 $(OFLAGS_web) -s EXPORTED_FUNCTIONS="['_empLoadString']" -I../../../../include/ default: web diff --git a/demos/utils/graphs/web/vcover.cpp b/demos/utils/graphs/web/vcover.cpp index 4b63f298e2..90804a2b59 100644 --- a/demos/utils/graphs/web/vcover.cpp +++ b/demos/utils/graphs/web/vcover.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file vcover.cpp + * @file */ #include diff --git a/demos/utils/graphs/web/web_UI.cpp b/demos/utils/graphs/web/web_UI.cpp index 8a7007f0e6..165f50b014 100644 --- a/demos/utils/graphs/web/web_UI.cpp +++ b/demos/utils/graphs/web/web_UI.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file web_UI.cpp + * @file */ #include "web_UI.h" diff --git a/demos/utils/graphs/web/web_UI.hpp b/demos/utils/graphs/web/web_UI.hpp index 4641b134e8..305e90b240 100644 --- a/demos/utils/graphs/web/web_UI.hpp +++ b/demos/utils/graphs/web/web_UI.hpp @@ -1,9 +1,11 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file web_UI.hpp + * @file + * @brief TODO. * */ diff --git a/demos/utils/levelize/levelize.cpp b/demos/utils/levelize/levelize.cpp index fa8336fff0..5ada17b546 100644 --- a/demos/utils/levelize/levelize.cpp +++ b/demos/utils/levelize/levelize.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file levelize.cpp + * @file */ #include diff --git a/demos/utils/words/Wordle-simple.cpp b/demos/utils/words/Wordle-simple.cpp new file mode 100644 index 0000000000..9f9c548cce --- /dev/null +++ b/demos/utils/words/Wordle-simple.cpp @@ -0,0 +1,422 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2022 +*/ +/** + * @file + * @brief version of Wordle is a bit simpler than it should be; it does not handle double letters + * correctly. + */ + +#include +#include +#include +#include +#include + +#include "../../../include/emp/base/Ptr.hpp" +#include "../../../include/emp/base/vector.hpp" +#include "../../../include/emp/bits/BitSet.hpp" +#include "../../../include/emp/bits/BitVector.hpp" +#include "../../../include/emp/config/command_line.hpp" +#include "../../../include/emp/datastructs/map_utils.hpp" +#include "../../../include/emp/datastructs/vector_utils.hpp" +#include "../../../include/emp/io/File.hpp" +#include "../../../include/emp/tools/string_utils.hpp" + +enum class Result { NOWHERE=0, ELSEWHERE, HERE }; + +/// A collection of results for a whole word. +struct ResultSet { + emp::vector results; + + static const emp::vector & PlaceValues(const size_t num_results) { + static emp::vector place_values; + if (place_values.size() == 0) { + place_values.resize(num_results); + size_t value = 1; + for (size_t i = 0; i < num_results; ++i) { + place_values[i] = value; + value *= 3; + } + } + return place_values; + } + + ResultSet(const emp::vector & in) : results(in) { } + ResultSet(size_t size, size_t id) : results(size) { + emp::vector place_values = PlaceValues(results.size()); + for (size_t i = results.size()-1; i < results.size(); --i) { + if (id > place_values[i]) { + size_t value = id / place_values[i]; + results[i] = (Result) value; + id -= value * place_values[i]; + } + } + } + ResultSet(const ResultSet &) = default; + + size_t ToID() { + emp::vector place_values = PlaceValues(results.size()); + size_t id = 0; + for (size_t i = 0; i < results.size(); ++i) { + id += place_values[i] * (size_t) results[i]; + } + return id; + } +}; + +// A clue is a given letter, position, and result +struct Clue { + emp::BitVector words; // IDs of words consistent with this clue. +}; + +// All of the clues for a given position. +struct PositionClues { + std::array nowhere; + std::array elsewhere; + std::array here; + + void SetNumWords(size_t num_words) { + for (auto & x : nowhere) x.words.resize(num_words); + for (auto & x : elsewhere) x.words.resize(num_words); + for (auto & x : here) x.words.resize(num_words); + } +}; + +// Trying to build a full tree of solutions... +struct SolveState { + emp::BitVector words; +}; + +struct WordData { + std::string word; + emp::BitSet<26> letters; + size_t max_options = 0; // Maximum number of word options after used as a guess. + double ave_options = 0.0; // Average number of options after used as a guess. + double entropy = 0.0; // What is the entropy (and thus information gained) for this choice? + bool is_active = false; + + WordData(const std::string & in_word) : word(in_word) { + for (char x : word) letters.Set(x - 'a'); + } +}; + +class WordSet { +private: + size_t word_length; + emp::vector words; + emp::vector clues; // A PositionClues object for each position. + std::unordered_map pos_map; // Map of words to their position ids. + emp::BitVector start_options; // Current options. + size_t start_count; // Count of start options (cached) + + bool verbose = true; + + // Get the ID (0-26) associated with a letter. + size_t ID(char letter) { + emp_assert(letter >= 'a' && letter <= 'z'); + return static_cast(letter - 'a'); + } + + char LET(size_t id) { + emp_assert(id < 26); + return (char) (id + 'a'); + } + +public: + WordSet(size_t length=5) : word_length(length) { } + + void AddWord(std::string & in_word) { + size_t id = words.size(); + pos_map[in_word] = id; + words.emplace_back(in_word); + } + + void Load(std::istream & is, std::ostream & os) { + // Load in all of the words. + std::string in_word; + size_t wrong_size_count = 0; + size_t invalid_char_count = 0; + size_t dup_count = 0; + while (is) { + is >> in_word; + // Only keep words of the correct size and all lowercase. + if (in_word.size() != word_length) { wrong_size_count++; continue; } + if (!emp::is_lower(in_word)) { invalid_char_count++; continue; } + if (emp::Has(pos_map, in_word)) { dup_count++; continue; } + AddWord(in_word); + } + + if (wrong_size_count) { + std::cerr << "Warning: eliminated " << wrong_size_count << " words of the wrong size." + << std::endl; + } + if (invalid_char_count) { + std::cerr << "Warning: eliminated " << invalid_char_count << " words with invalid characters." + << std::endl; + } + if (dup_count) { + std::cerr << "Warning: eliminated " << dup_count << " words that were duplicates." + << std::endl; + } + + if (verbose) std::cerr << "Loaded " << words.size() << " valid words." << std::endl; + } + + void ResetOptions() { + start_count = words.size(); + start_options.resize(start_count); + start_options.SetAll(); + } + + // Once the words are loaded, Preprocess will collect info. + void Preprocess() { + // Setup all clue info to know the number of words. + clues.resize(word_length); + for (auto & x : clues) x.SetNumWords(words.size()); + + // Loop through each word, indicating which clues it is consistent with. + for (size_t word_id = 0; word_id < words.size(); ++word_id) { + const std::string & word = words[word_id].word; + + // Figure out which letters are in this word. + emp::BitSet<26> letters = words[word_id].letters; + + // Now figure out what clues it is consistent with. + for (size_t pos=0; pos < word.size(); ++pos) { + const char cur_letter = word[pos]; + // Incorrect letter for alternatives at this position. + for (size_t letter_id = 0; letter_id < 26; ++letter_id) { + if (letter_id == ID(cur_letter)) { // Letter is HERE. + clues[pos].here[letter_id].words.Set(word_id); + } else if (letters.Has(letter_id)) { // Letter is ELSEWHERE + clues[pos].elsewhere[letter_id].words.Set(word_id); + } else { // Letter is NOT IN WORD + clues[pos].nowhere[letter_id].words.Set(word_id); + } + } + } + } + + ResetOptions(); + } + + /// Limit starting options based on a specific clue. + void AddClue(size_t pos, char letter, Result result) { + size_t let_id = ID(letter); + if (result == Result::NOWHERE) { + start_options &= clues[pos].nowhere[let_id].words; + } else if (result == Result::ELSEWHERE) { + start_options &= clues[pos].elsewhere[let_id].words; + } else { + start_options &= clues[pos].here[let_id].words; + } + start_count = start_options.CountOnes(); + } + + void AddClue(std::string word, std::string result) { + for (size_t i = 0; i < word.size(); ++i) { + if (result[i] == 'N') AddClue(i, word[i], Result::NOWHERE); + else if (result[i] == 'E') AddClue(i, word[i], Result::ELSEWHERE); + else if (result[i] == 'H') AddClue(i, word[i], Result::HERE); + } + } + + emp::BitVector AnalyzeGuess(const std::string & guess, const WordData & answer) { + // Loop through all possible answers to see how much a word cuts down choices. + emp::BitVector options(start_options); + + for (size_t pos = 0; pos < word_length; ++pos) { + const size_t guess_letter = ID(guess[pos]); + if (guess[pos] == answer.word[pos]) { // CORRECT GUESS FOR POSITION! + options &= clues[pos].here[guess_letter].words; + } else if (answer.letters.Has(guess_letter)) { // WRONG POSITION + options &= clues[pos].elsewhere[guess_letter].words; + } else { // WRONG CHARACTER + options &= clues[pos].nowhere[guess_letter].words; + } + } + + return options; + } + + // Slow way to manually call on specific words; brute-force find the entires for each. + emp::BitVector AnalyzeGuess(const std::string & guess, const std::string & answer) { + if (!emp::Has(pos_map, answer)) std::cerr << "UNKNOWN WORD: " << answer << std::endl; + return AnalyzeGuess(guess, words[pos_map[answer]]); + } + + void AnalyzeGuess(WordData & guess) { + size_t max_options = 0; + size_t total_options = 0; + double entropy = 0.0; + + // Scan through all possible answers... + for (WordData & answer : words) { + size_t options = AnalyzeGuess(guess.word, answer).CountOnes(); + if (options > max_options) max_options = options; + total_options += options; + const double p = static_cast(options) / static_cast(start_count); + entropy -= p * std::log2(p); + } + guess.max_options = max_options; + guess.ave_options = static_cast(total_options) / static_cast(words.size()); + guess.entropy = entropy; + } + + void Analyze() { + // for (int id = start_options.FindOne(); id >= 0; id = start_options.FindOne(id+1)) { + for (size_t id = 0; id < words.size(); ++id) { + AnalyzeGuess(words[id]); + } + } + + /// Also analyze non-word guesses. + void AnalyzeAll() { + std::string guess(word_length, 'a'); + size_t best_max_options = 10000; + double best_ave_options = 10000.0; + double best_entropy = 0.0; + std::string best_max_options_word = ""; + std::string best_ave_options_word = ""; + std::string best_entropy_word = ""; + + size_t silent_count = 0; // Keep a count of how many loops since out last output. + while (true) { + size_t max_options = 0; + size_t total_options = 0; + double entropy = 0.0; + + // Scan through all possible answers... + for (WordData & answer : words) { + size_t options = AnalyzeGuess(guess, answer).CountOnes(); + if (options > max_options) max_options = options; + total_options += options; + const double p = static_cast(options) / static_cast(start_count); + entropy -= p * std::log2(p); + } + double ave_options = static_cast(total_options) / static_cast(words.size()); + + ++silent_count; + if (max_options < best_max_options) { + best_max_options = max_options; + best_max_options_word = guess; + std::cout << "New best MAX options: " << guess << " : " << max_options << std::endl; + silent_count = 0; + } + if (ave_options < best_ave_options) { + best_ave_options = ave_options; + best_ave_options_word = guess; + std::cout << "New best AVE options: " << guess << " : " << ave_options << std::endl; + silent_count = 0; + } + if (entropy > best_entropy) { + best_entropy = entropy; + best_entropy_word = guess; + std::cout << "New best ENTROPY: " << guess << " : " << entropy << std::endl; + silent_count = 0; + } + if (silent_count >= 10000) { + std::cout << "...processing... ('" << guess << "')" << std::endl; + silent_count = 0; + } + + // Now move on to the next word... + size_t inc_pos = word_length - 1; // find the first non-z letter. + while (inc_pos < word_length && guess[inc_pos] == 'z') { + guess[inc_pos] = 'a'; + --inc_pos; + } + if (inc_pos == word_length) break; + ++guess[inc_pos]; + } + } + + /// Print all of the words with a given set of IDs. + void PrintWords(const emp::BitVector & word_ids) { + size_t count = 0; + for (int id = word_ids.FindOne(); id >= 0; id = word_ids.FindOne(id+1)) { + if (count) std::cout << ","; + std::cout << words[id].word; + ++count; + } + std::cout << " (" << count << " words found)" << std::endl; + } + + /// Print all of the results, sorted by max number of options. + void PrintResults() { + for (size_t i = 0; i < words.size(); ++i) { + words[i].is_active = start_options.Has(i); + } + emp::Sort(words, [](const WordData & w1, const WordData & w2){ + if (w1.is_active == w2.is_active) { + return w1.max_options < w2.max_options; + } + return w2.is_active; + }); + for (auto & word : words) { + std::cout << word.word + << ", " << word.max_options + << ", " << word.ave_options + << ", " << word.is_active + << std::endl; + } + } +}; + +int main(int argc, char* argv[]) +{ + emp::vector args = emp::cl::args_to_strings(argc, argv); + + if (args.size() > 3) { + std::cerr << "May provide am input filename (with the word list to use) and output filename (for results)" + << std::endl; + exit(1); + } + + WordSet word_set(5); + + if (args.size() == 1) word_set.Load(std::cin, std::cout); + else { + std::ifstream in_file{args[1]}; + if (args.size() == 2) word_set.Load(in_file, std::cout); + else { + std::ofstream out_file{args[2]}; + word_set.Load(in_file, out_file); + } + } + + word_set.Preprocess(); + + //word_set.AddClue("aloes", "NNNEN"); + word_set.AddClue("rates", "NENEN"); + // word_set.AddClue("login", "ENNEN"); + // word_set.AddClue("dimly", "NHNHH"); + // word_set.AddClue("finch", "NNNNN"); + + /* + word_set.AddClue(0,'a',Result::NOWHERE); + word_set.AddClue(1,'l',Result::NOWHERE); + word_set.AddClue(2,'o',Result::NOWHERE); + word_set.AddClue(3,'e',Result::ELSEWHERE); + word_set.AddClue(4,'s',Result::NOWHERE); + + word_set.AddClue(0,'d',Result::NOWHERE); + word_set.AddClue(1,'i',Result::ELSEWHERE); + word_set.AddClue(2,'r',Result::NOWHERE); + word_set.AddClue(3,'t',Result::NOWHERE); + word_set.AddClue(4,'y',Result::NOWHERE); + + word_set.AddClue(0,'h',Result::NOWHERE); + word_set.AddClue(1,'e',Result::NOWHERE); + word_set.AddClue(2,'n',Result::NOWHERE); + word_set.AddClue(3,'g',Result::NOWHERE); + word_set.AddClue(4,'e',Result::HERE); + */ + + word_set.Analyze(); + word_set.PrintResults(); +// word_set.AnalyzeAll(); +} diff --git a/demos/utils/words/Wordle/Makefile b/demos/utils/words/Wordle/Makefile new file mode 100644 index 0000000000..71a2bb9d57 --- /dev/null +++ b/demos/utils/words/Wordle/Makefile @@ -0,0 +1,69 @@ +EMP_DIR := ../../../../include + +# Flags to use regardless of compiler +CFLAGS_all := -Wall -Wextra -Wno-unused-function -I$(EMP_DIR)/ +CFLAGS_version := -std=c++20 + +# Emscripten compiler information +CXX_web := emcc +CXX_native := g++ + +OFLAGS_native_opt := -O3 -DNDEBUG +OFLAGS_native_debug := -g -pedantic -DEMP_TRACK_MEM -Wnon-virtual-dtor -Wcast-align +OFLAGS_native_grumpy := -g -pedantic -DEMP_TRACK_MEM -Wnon-virtual-dtor -Wcast-align -Wconversion -Weffc++ + +OFLAGS_web_opt := -Os -DNDEBUG -s TOTAL_MEMORY=67108864 +OFLAGS_web_debug := -g4 -pedantic -Wno-dollar-in-identifier-extension -s TOTAL_MEMORY=67108864 -s ASSERTIONS=2 -s DEMANGLE_SUPPORT=1 # -s SAFE_HEAP=1 + +CFLAGS_native_opt := $(CFLAGS_all) $(OFLAGS_native_opt) +CFLAGS_native_debug := $(CFLAGS_all) $(OFLAGS_native_debug) +CFLAGS_native_grumpy := $(CFLAGS_all) $(OFLAGS_native_grumpy) + +CFLAGS_web_debug := $(CFLAGS_all) $(OFLAGS_web_debug) --js-library $(EMP_DIR)/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s NO_EXIT_RUNTIME=1 +CFLAGS_web_opt := $(CFLAGS_all) $(OFLAGS_web_opt) --js-library $(EMP_DIR)/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s NO_EXIT_RUNTIME=1 +#CFLAGS_web := $(CFLAGS_all) $(OFLAGS_web) --js-library $(EMP_DIR)/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=1 + +TARGETS := Wordle + +default: native + +CXX := $(CXX_native) +CFLAGS := $(CFLAGS_native_opt) + +debug: CFLAGS := $(CFLAGS_native_debug) +debug: all + +grumpy: CFLAGS := $(CFLAGS_native_grumpy) +grumpy: all + +web: CXX := $(CXX_web) +web: CFLAGS := $(CFLAGS_web_opt) +web: all + +web-debug: CXX := $(CXX_web) +web-debug: CFLAGS := $(CFLAGS_web_debug) +web-debug: all + +native: all + +all: $(TARGETS) + +$(TARGETS): % : %.cpp + $(CXX) $(CFLAGS_version) $(CFLAGS) $< -o $@ + +$(JS_TARGETS): %.js : %.cpp + $(CXX_web) $(CFLAGS_web) $< -o $@ + +debug-%: $*.cpp + $(CXX) $(CFLAGS_version) $(CFLAGS_native_debug) $< -o $@ + +clean: + rm -rf debug-* *~ *.dSYM $(TARGETS) +# rm -rf debug-* *~ *.dSYM $(JS_TARGETS) + +new: clean +new: native + +# Debugging information +#print-%: ; @echo $*=$($*) +print-%: ; @echo '$(subst ','\'',$*=$($*))' diff --git a/demos/utils/words/Wordle/Result.hpp b/demos/utils/words/Wordle/Result.hpp new file mode 100644 index 0000000000..4a4f2eacb9 --- /dev/null +++ b/demos/utils/words/Wordle/Result.hpp @@ -0,0 +1,164 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ +/** + * @file + */ + +#ifndef DEMOS_UTILS_WORDS_WORDLE_RESULT_HPP_INCLUDE +#define DEMOS_UTILS_WORDS_WORDLE_RESULT_HPP_INCLUDE + +#include + +#include "emp/base/array.hpp" +#include "emp/base/error.hpp" +#include "emp/bits/BitVector.hpp" +#include "emp/math/math.hpp" + +template +class Result { +public: + enum PositionResult { NOWHERE, ELSEWHERE, HERE }; + static constexpr size_t NUM_IDS = emp::Pow(3, WORD_SIZE); + +private: + using results_t = emp::array; + + results_t results; + size_t id; + + /// Return a result array where each index is an associated (unique) possible result set. + static const results_t & LookupResult(size_t result_id) { + static emp::array result_array; + static bool init = false; + + // If this is our first time requsting the result array, generate it. + if (!init) { + init = true; + for (size_t id = 0; id < NUM_IDS; ++id) { + size_t tmp_id = id; + for (size_t pos = WORD_SIZE-1; pos < WORD_SIZE; --pos) { + const size_t magnitude = emp::Pow(3, pos); + const size_t cur_result = tmp_id / magnitude; + result_array[id][pos] = static_cast(cur_result); + tmp_id -= cur_result * magnitude; + } + } + } + + return result_array[result_id]; + } + + /// Assume that we have results, calculate the associated ID. + void CalcID() { + size_t base = 1; + id = 0; + for (PositionResult r : results) { id += static_cast(r) * base; base *= 3; } + } + + /// Assume that we have an ID, lookup the correct results. + void CalcResults() { results = LookupResult(id); } + + /// Convert a results string of 'N's, 'E's, and 'W's into a Results object. + void FromString(const std::string & result_str) { + emp_assert(result_str.size() == WORD_SIZE); + for (size_t i=0; i < WORD_SIZE; ++i) { + switch (result_str[i]) { + case 'N': case 'n': results[i] = NOWHERE; break; + case 'E': case 'e': results[i] = ELSEWHERE; break; + case 'H': case 'h': results[i] = HERE; break; + default: + emp_error("Invalid character in result string", result_str[i]); + }; + } + } + +public: + /// Create a result by id. + Result(size_t _id) : id(_id) { CalcResults(); } + + /// Create a result by a result array. + Result(const results_t & _results) : results(_results) { CalcID(); } + + /// Create a result by a result string. + Result(const std::string & result_str) { FromString(result_str); } + + /// Create a result by an guess and answer pair. + Result(const std::string & guess, const std::string & answer) { + emp_assert(guess.size() == WORD_SIZE); + emp_assert(answer.size() == WORD_SIZE); + emp::BitVector used(answer.size()); + // Test perfect matches. + for (size_t i = 0; i < guess.size(); ++i) { + if (guess[i] == answer[i]) { results[i] = HERE; used.Set(i); } + } + // Test offset matches. + for (size_t i = 0; i < guess.size(); ++i) { + if (guess[i] == answer[i]) continue; // already matched. + bool found = false; + for (size_t j = 0; j < answer.size(); ++j) { // seek a match elsewhere in answer! + if (!used.Has(j) && guess[i] == answer[j]) { + results[i] = ELSEWHERE; // found letter elsewhere! + used.Set(j); // make sure this letter is noted as used. + found = true; + break; // move on to next letter; we found this one. + } + } + if (!found) results[i] = NOWHERE; + } + CalcID(); // Now that we know the symbols, figure out the ID. + } + + Result(const Result & result) = default; + Result(Result && result) = default; + + Result & operator=(const std::string & result_str) { FromString(result_str); } + Result & operator=(const Result & result) = default; + Result & operator=(Result && result) = default; + + bool operator==(const Result & in) const { return id == in.id; } + bool operator!=(const Result & in) const { return id != in.id; } + bool operator< (const Result & in) const { return id < in.id; } + bool operator<=(const Result & in) const { return id <= in.id; } + bool operator> (const Result & in) const { return id > in.id; } + bool operator>=(const Result & in) const { return id >= in.id; } + + size_t GetID() const { return id; } + size_t GetSize() const { return WORD_SIZE; } + size_t size() const { return WORD_SIZE; } + + PositionResult operator[](size_t id) const { return results[id]; } + + // Test if this result is valid for the given word. + bool IsValid(const std::string & word) const { + // Disallow letters marked "NOWHERE" that are subsequently marked "ELSEWHERE" + // (other order is okay). + for (size_t pos = 0; pos < WORD_SIZE-1; ++pos) { + if (results[pos] == NOWHERE) { + for (size_t pos2 = pos+1; pos2 < WORD_SIZE; ++pos2) { + if (results[pos2] == ELSEWHERE && word[pos] == word[pos2]) return false; + } + } + } + + return true; + } + + std::string ToString( + const std::string & here="H", + const std::string & elsewhere="E", + const std::string & nowhere="N" + ) const { + std::string out; // = emp::to_string(id, "-"); + for (auto x : results) { + if (x == HERE) out += here; + else if (x == ELSEWHERE) out += elsewhere; + else if (x == NOWHERE) out += nowhere; + } + return out; + } +}; + +#endif // #ifndef DEMOS_UTILS_WORDS_WORDLE_RESULT_HPP_INCLUDE diff --git a/demos/utils/words/Wordle/Wordle.cpp b/demos/utils/words/Wordle/Wordle.cpp new file mode 100644 index 0000000000..9b387718cb --- /dev/null +++ b/demos/utils/words/Wordle/Wordle.cpp @@ -0,0 +1,581 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2022 +*/ +/** + * @file + * @brief TODO. + */ + +#include +#include +#include +#include +#include + +#include "emp/base/Ptr.hpp" +#include "emp/base/vector.hpp" +#include "emp/bits/BitSet.hpp" +#include "emp/bits/BitVector.hpp" +#include "emp/config/command_line.hpp" +#include "emp/datastructs/map_utils.hpp" +#include "emp/datastructs/vector_utils.hpp" +#include "emp/io/File.hpp" +#include "emp/tools/string_utils.hpp" + +#include "Result.hpp" + + +template +class WordSet { +private: + static constexpr size_t MAX_LETTER_REPEAT = 4; + using word_list_t = emp::BitVector; + using result_t = Result; + + // Get the ID (0-26) associated with a letter. + static size_t ToID(char letter) { + emp_assert(letter >= 'a' && letter <= 'z'); + return static_cast(letter - 'a'); + } + + static char ToLetter(size_t id) { + emp_assert(id < 26); + return static_cast(id + 'a'); + } + + // All of the clues for a given position. + struct PositionClues { + size_t pos; + std::array here; // Is a given letter at this position? + + void SetNumWords(size_t num_words) { + for (auto & x : here) x.resize(num_words); + } + }; + + // All of the clues for zero or more instances of a given letter. + struct LetterClues { + size_t letter; // [0-25] + std::array at_least; ///< Are there at least x instances of letter? (0 is meaningless) + std::array exactly; ///< Are there exactly x instances of letter? + + void SetNumWords(size_t num_words) { + for (auto & x : at_least) x.resize(num_words); + for (auto & x : exactly) x.resize(num_words); + } + }; + + struct WordData { + std::string word; + // Pre=processed data + emp::BitSet<26> letters; // What letters are in this word? + emp::BitSet<26> multi_letters; // What letters are in this word more than once? + std::array next_words; + + // Collected data + size_t max_options = 0; // Maximum number of word options after used as a guess. + double ave_options = 0.0; // Average number of options after used as a guess. + double entropy = 0.0; // What is the entropy (and thus information gained) for this choice? + + WordData(const std::string & in_word) : word(in_word) { + for (char x : word) { + size_t let_id = ToID(x); + if (letters.Has(let_id)) multi_letters.Set(let_id); + else letters.Set(let_id); + } + } + }; + + emp::vector words; ///< Data about all words in this Wordle + emp::array pos_clues; ///< A PositionClues object for each position. + emp::array let_clues; ///< Clues based off the number of letters. + std::unordered_map pos_map; ///< Map of words to their position ids. + word_list_t start_options; ///< Current options. + size_t start_count; ///< Count of start options (cached) + + std::istream & is; + std::ostream & os; + + bool verbose = true; + +public: + WordSet(std::istream & _is, std::ostream & _os) : is(_is), os(_os) { } + + /// Include a single word into this WordSet. + void AddWord(std::string & in_word) { + size_t id = words.size(); // Set a unique ID for this word. + pos_map[in_word] = id; // Keep track of the ID for this word. + words.emplace_back(in_word); // Setup the word data. + } + + /// Load a whole series for words (from a file) into this WordSet + void Load() { + // Load in all of the words. + std::string in_word; + size_t wrong_size_count = 0; + size_t invalid_char_count = 0; + size_t dup_count = 0; + while (is) { + is >> in_word; + // Only keep words of the correct size and all lowercase. + if (in_word.size() != WORD_SIZE) { wrong_size_count++; continue; } + if (!emp::is_lower(in_word)) { invalid_char_count++; continue; } + if (emp::Has(pos_map, in_word)) { dup_count++; continue; } + AddWord(in_word); + } + + if (wrong_size_count) { + std::cerr << "Warning: eliminated " << wrong_size_count << " words of the wrong size." + << std::endl; + } + if (invalid_char_count) { + std::cerr << "Warning: eliminated " << invalid_char_count << " words with invalid characters." + << std::endl; + } + if (dup_count) { + std::cerr << "Warning: eliminated " << dup_count << " words that were duplicates." + << std::endl; + } + + if (verbose) std::cerr << "Loaded " << words.size() << " valid words." << std::endl; + } + + /// Clear out all prior guess information. + void ResetOptions() { + start_count = words.size(); + start_options.resize(start_count); + start_options.SetAll(); + } + + // Limit the current options based on a single guess and its result. + + word_list_t EvalGuess(const std::string & guess, const result_t & result) { + emp_assert(guess.size() == WORD_SIZE); + emp_assert(result.size() == WORD_SIZE); + + emp::array letter_counts; + std::fill(letter_counts.begin(), letter_counts.end(), 0); + emp::BitSet<26> letter_fail; + word_list_t word_options = start_options; + + // First add letter clues and collect letter information. + for (size_t i = 0; i < WORD_SIZE; ++i) { + const size_t cur_letter = ToID(guess[i]); + if (result[i] == result_t::HERE) { + word_options &= pos_clues[i].here[cur_letter]; + ++letter_counts[cur_letter]; + } else if (result[i] == result_t::ELSEWHERE) { + word_options &= ~pos_clues[i].here[cur_letter]; + ++letter_counts[cur_letter]; + } else { // Must be 'N' + word_options &= ~pos_clues[i].here[cur_letter]; + letter_fail.Set(cur_letter); + } + } + + // Next add letter clues. + for (size_t letter_id = 0; letter_id < 26; ++letter_id) { + const size_t let_count = letter_counts[letter_id]; + if (let_count) { + word_options &= let_clues[letter_id].at_least[let_count]; + } + if (letter_fail.Has(letter_id)) { + word_options &= let_clues[letter_id].exactly[let_count]; + } + } + + return word_options; + } + + + void AnalyzeGuess(WordData & guess, const word_list_t & cur_words) { + size_t max_options = 0; + size_t total_options = 0; + size_t option_count = 0; + double entropy = 0.0; + const double word_count = static_cast(words.size()); + + // Scan through all of the possible result IDs. + for (size_t result_id = 0; result_id < result_t::NUM_IDS; ++result_id) { + word_list_t next_options = guess.next_words[result_id] & cur_words; + size_t num_options = next_options.CountOnes(); + if (num_options > max_options) max_options = num_options; + total_options += num_options * num_options; + option_count++; + double p = static_cast(num_options) / word_count; + if (p > 0.0) entropy -= p * std::log2(p); + } + + guess.max_options = max_options; + guess.ave_options = static_cast(total_options) / static_cast(words.size()); + guess.entropy = entropy; + } + + + /// Once the words are loaded, Preprocess will collect info. + void Preprocess() { + std::cout << "Beginning pre-process phase..." << std::endl; + + // Setup all position clue info to know the number of words. + for (size_t i=0; i < WORD_SIZE; ++i) { + pos_clues[i].pos = i; + pos_clues[i].SetNumWords(words.size()); + } + + // Setup all letter clue information + for (size_t let=0; let < 26; let++) { + let_clues[let].letter = let; + let_clues[let].SetNumWords(words.size()); + } + + // Counters for number of letters. + emp::array letter_counts; + + // Loop through each word, indicating which clues it is consistent with. + for (size_t word_id = 0; word_id < words.size(); ++word_id) { + const std::string & word = words[word_id].word; + + // Figure out which letters are in this word. + std::fill(letter_counts.begin(), letter_counts.end(), 0); // Reset counters to zero. + for (const char letter : word) ++letter_counts[ToID(letter)]; // Count letters. + + // Setup the LETTER clues that word is consistent with. + for (size_t letter_id = 0; letter_id < 26; ++letter_id) { + const size_t cur_count = letter_counts[letter_id]; + let_clues[letter_id].exactly[cur_count].Set(word_id); + for (uint8_t count = 0; count <= cur_count; ++count) { + let_clues[letter_id].at_least[count].Set(word_id); + } + } + + // Now figure out what POSITION clues it is consistent with. + for (size_t pos=0; pos < word.size(); ++pos) { + const size_t cur_letter = ToID(word[pos]); + pos_clues[pos].here[cur_letter].Set(word_id); + } + } + + std::cout << "...clues are initialized..." << std::endl; + + ResetOptions(); + + // Loop through words one more time, filling out result lists and collecting data. + size_t word_count = 0; + const size_t step = words.size() / 100; + for (auto & word_info : words) { + if (++word_count % step == 0) { + std::cout << "."; + std::cout.flush(); + } + for (size_t result_id = 0; result_id < result_t::NUM_IDS; ++result_id) { + Result result(result_id); + if (!result.IsValid(word_info.word)) continue; + word_info.next_words[result_id] = EvalGuess(word_info.word, result_id); + } + AnalyzeGuess(word_info, start_options); + } + + std::cout << "...words are analyzed..." << std::endl; + } + + // /// Also analyze non-word guesses. + // void AnalyzeAll() { + // std::string guess(WORD_SIZE, 'a'); + // size_t best_max_options = 10000; + // double best_ave_options = 10000.0; + // double best_entropy = 0.0; + // std::string best_max_options_word = ""; + // std::string best_ave_options_word = ""; + // std::string best_entropy_word = ""; + + // size_t silent_count = 0; // Keep a count of how many loops since out last output. + // while (true) { + // size_t max_options = 0; + // size_t total_options = 0; + // double entropy = 0.0; + + // // Scan through all possible answers... + // for (WordData & answer : words) { + // size_t options = AnalyzeGuess(guess, answer).CountOnes(); + // if (options > max_options) max_options = options; + // total_options += options; + // const double p = static_cast(options) / static_cast(start_count); + // entropy -= p * std::log2(p); + // } + // double ave_options = static_cast(total_options) / static_cast(words.size()); + + // ++silent_count; + // if (max_options < best_max_options) { + // best_max_options = max_options; + // best_max_options_word = guess; + // std::cout << "New best MAX options: " << guess << " : " << max_options << std::endl; + // silent_count = 0; + // } + // if (ave_options < best_ave_options) { + // best_ave_options = ave_options; + // best_ave_options_word = guess; + // std::cout << "New best AVE options: " << guess << " : " << ave_options << std::endl; + // silent_count = 0; + // } + // if (entropy > best_entropy) { + // best_entropy = entropy; + // best_entropy_word = guess; + // std::cout << "New best ENTROPY: " << guess << " : " << entropy << std::endl; + // silent_count = 0; + // } + // if (silent_count >= 10000) { + // std::cout << "...processing... ('" << guess << "')" << std::endl; + // silent_count = 0; + // } + + // // Now move on to the next word... + // size_t inc_pos = WORD_SIZE - 1; // find the first non-z letter. + // while (inc_pos < WORD_SIZE && guess[inc_pos] == 'z') { + // guess[inc_pos] = 'a'; + // --inc_pos; + // } + // if (inc_pos == WORD_SIZE) break; + // ++guess[inc_pos]; + // } + // } + + /// Print all of the words with a given set of IDs. + void PrintWords(const word_list_t & word_ids, size_t max_count=(size_t)-1) const { + std::cout << "(" << word_ids.CountOnes() << " words) "; + size_t count = 0; + for (int id = word_ids.FindOne(); id >= 0; id = word_ids.FindOne(id+1)) { + if (count) std::cout << ","; + std::cout << words[id].word; + if (++count == max_count) { + if (id > 0) std::cout << " ..."; + break; + } + } + // std::cout << " (" << word_is.CountOnes() << " words)" << std::endl; + } + + void PrintPosClues(size_t pos) const { + const PositionClues & clue = pos_clues[pos]; + std::cout << "Position " << pos << ":\n"; + for (uint8_t i = 0; i < 26; ++i) { + std::cout << " '" << clue.let << "' : "; + PrintWords(clue.here[i], 10); + std::cout << std::endl; + } + } + + void PrintLetterClues(char letter) const { + const LetterClues & clue = let_clues[ToID(letter)]; + std::cout << "Letter '" << clue.letter << "':\n"; + for (size_t i = 0; i <= MAX_LETTER_REPEAT; ++i) { + std::cout << "EXACTLY " << i << ": "; + PrintWords(clue.exactly[i], 20); + std::cout << std::endl; + } + for (size_t i = 0; i <= MAX_LETTER_REPEAT; ++i) { + std::cout << "AT LEAST " << i << ": "; + PrintWords(clue.at_least[i], 20); + std::cout << std::endl; + } + } + + void PrintWordData(const WordData & word) const { + std::cout << "WORD: " << word.word << std::endl; + std::cout << "Letters: " << word.letters << std::endl; + std::cout << "Multi: " << word.multi_letters << std::endl; + std::cout << "MAX Opts: " << word.max_options << std::endl; + std::cout << "AVE Opts: " << word.ave_options << std::endl; + std::cout << "Entropy: " << word.entropy << std::endl; + std::cout << std::endl; + + size_t total_count = 0; + for (size_t result_id = 0; result_id < result_t::NUM_IDS; ++result_id) { + result_t result(result_id); + word_list_t result_words = word.next_words[result_id]; + std::cout << result_id << " - " << result.ToString() << " "; + PrintWords(result_words, 10); + total_count += result_words.CountOnes(); + std::cout << std::endl; + } + std::cout << "Total Count: " << total_count << std::endl; + } + + void PrintWordData(size_t id) const { PrintWordData(words[id]); } + void PrintWordData(const std::string & word) { + PrintWordData(words[pos_map[word]]); + } + + // Reorder words. NOTE: This is destructive to all word_list data! + void SortWords(const std::string & sort_type="max") { + using wd_t = const WordData &; + if (sort_type == "max") { + emp::Sort(words, [](wd_t w1, wd_t w2){ + if (w1.max_options == w2.max_options) return w1.ave_options < w2.ave_options; // tiebreak + return w1.max_options < w2.max_options; + } ); + } else if (sort_type == "ave") { + emp::Sort(words, [](wd_t w1, wd_t w2){ + if (w1.ave_options == w2.ave_options) return w1.max_options < w2.max_options; // tiebreak + return w1.ave_options < w2.ave_options; + } ); + } else if (sort_type == "entropy") { + emp::Sort(words, [](wd_t w1, wd_t w2){ return w1.entropy > w2.entropy; } ); + } else if (sort_type == "word") { + emp::Sort(words, [](wd_t w1, wd_t w2){ return w1.word < w2.word; } ); + } + for (size_t i = 0; i < words.size(); i++) { pos_map[words[i].word] = i; } // Update ID tracking. + } + + /// Print all of the results, sorted by max number of options. + void PrintResults() { + SortWords(); + for (auto & word : words) { + std::cout << word.word + << ", " << word.max_options + << ", " << word.ave_options + << ", " << word.entropy + << std::endl; + } + } + + /// Print out all words as HTML. + void PrintHTMLWord(const WordData & word) const { + std::string filename = emp::to_string("web/words/", word.word, ".html"); + std::ofstream of(filename); + + // const std::string black("⬛"); + static const std::string white("⬜"); + static const std::string green("🟩"); + static const std::string yellow("🟨"); + + of << "\n\n\n Wordle Analysis: '" + << word.word << "'\n\n\n"; + + of << "

Wordle Analysis: " << word.word << "

\n\n"; + of << "Worst case words remaining: " << word.max_options << "
\n"; + of << "Expected words remaining: " << word.ave_options << "
\n"; + of << "Information provided: " << word.entropy << "
\n

\n"; + + // Loop through all possible results. + // for (size_t result_id = 0; result_id < result_t::NUM_IDS; ++result_id) { + for (size_t result_id = result_t::NUM_IDS-1; result_id < result_t::NUM_IDS; --result_id) { + result_t result(result_id); + word_list_t result_words = word.next_words[result_id]; + + of << result.ToString(green, yellow, white) << " (" << result_words.CountOnes() << " words) : "; + + for (int id = result_words.FindOne(); id >= 0; id = result_words.FindOne(id+1)) { + of << "" << words[id].word << " "; + } + + of << "
\n"; + } + + + of << "\n\n"; + + os << "Printed file '" << filename << "'." << std::endl; + } + + void PrintHTMLWordID(int id) const { PrintHTMLWord(words[(size_t) id]); } + void PrintHTMLWord(const std::string & word) { + PrintHTMLWord(words[pos_map[word]]); + } + + void PrintHTMLIndex(const std::string & order) { + SortWords(order); + std::string filename = emp::to_string("web/index-", order, ".html"); + std::ofstream of(filename); + + of << "\n\n\n Wordle Analysis: INDEX" + "\n\n\n" + "

Analysis of Wordle Guesses

\n" + "

\nWhen a guess is made in a game of Wordle, the results limit the set of words for the answer." + " A more useful guess will limit the remaining possibilities to be as small as possible." + " But the question remains: Which word should we choose first?" + " Here are some analyses to help make that decision.\n" + "

\nBelow are a list of 5-letter words " + "(from here)" + " with data on each. The columns are:
\n" + "\n" + " \n" + " \n" + "
ExpectedWords:" + " The average number of possible words if this were your first guess. (smaller is better!)
MaximumWords:" + " The largest possible number of words remaining after this guess. (smaller is better!)
Information:" + " The number of bits of information this guess provides about the final answer. (larger is better!)

\n" + "Click on any column to sort by it. " + "Click on any word to see the exact breakdown of how possible first guesses limit future options.\n" + "

\n"; + + of << "\n"; + for (const auto & word : words) { + of << "\n"; + } + } + + void PrintHTML() { + size_t count = 0; + std::cout << "Printing HTML files..." << std::endl; + size_t step = words.size() / 100; + for (auto & word : words) { + if (count % step == 0) { std::cout << "."; std::cout.flush(); } + PrintHTMLWord(word); + } + PrintHTMLIndex("ave"); + PrintHTMLIndex("entropy"); + PrintHTMLIndex("max"); + PrintHTMLIndex("word"); + } + +}; + +int main(int argc, char* argv[]) +{ + emp::vector args = emp::cl::args_to_strings(argc, argv); + + if (args.size() > 3) { + std::cerr << "May provide am input filename (with the word list to use) and output filename (for results)" + << std::endl; + exit(1); + } + + emp::Ptr is_ptr = &std::cin; + if (args.size() > 1) is_ptr = emp::NewPtr(args[1]); + + emp::Ptr os_ptr = &std::cout; + if (args.size() > 2) os_ptr = emp::NewPtr(args[2]); + + WordSet<5> word_set(*is_ptr, *os_ptr); + word_set.Load(); + word_set.SortWords("word"); + + word_set.Preprocess(); + // word_set.AddClue(0,'a',result_t::ELSEWHERE); + // word_set.AddClue(1,'l',result_t::ELSEWHERE); + // word_set.AddClue(2,'o',result_t::NOWHERE); + // word_set.AddClue(3,'e',result_t::NOWHERE); + // word_set.AddClue(4,'s',result_t::NOWHERE); + + // word_set.PrintLetterClues('x'); + // word_set.PrintPosClues(0); + // word_set.PrintWordData(0); + // word_set.PrintWordData("aloes"); + // word_set.PrintResults(); + // word_set.AnalyzeAll(); + // word_set.PrintHTMLWordID(0); + // word_set.PrintHTMLWord("aloes"); + word_set.PrintHTML(); + + if (args.size() > 1) is_ptr.Delete(); + if (args.size() > 2) os_ptr.Delete(); +} diff --git a/demos/utils/words/annotate-length.cpp b/demos/utils/words/annotate-length.cpp index d9630c3744..fa97b34369 100644 --- a/demos/utils/words/annotate-length.cpp +++ b/demos/utils/words/annotate-length.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file annotate-length.cpp + * @file * @brief Annotate all of the words in an input list with their length. */ diff --git a/demos/utils/words/has-letters.cpp b/demos/utils/words/has-letters.cpp index d103a8267b..5e3ea957fd 100644 --- a/demos/utils/words/has-letters.cpp +++ b/demos/utils/words/has-letters.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file has-letters.cpp + * @file * @brief Find all words in a dictionary with a combination of letters IN ORDER, annotated by length. */ diff --git a/demos/utils/words/wordplay-remove.cpp b/demos/utils/words/wordplay-remove.cpp index 84681ac902..d1de8119ae 100644 --- a/demos/utils/words/wordplay-remove.cpp +++ b/demos/utils/words/wordplay-remove.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file wordplay-remove.cpp + * @file * @brief Test the removal of letters through an alphabet to see if new words are formed. */ diff --git a/demos/utils/words/wordplay-rot.cpp b/demos/utils/words/wordplay-rot.cpp index 113f1c14a2..613236f4cb 100644 --- a/demos/utils/words/wordplay-rot.cpp +++ b/demos/utils/words/wordplay-rot.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file wordplay-rot.cpp + * @file * @brief Test the rotation of letters through an alphabet to see if new words are formed. * * Build graphs of various types in the standard format. diff --git a/demos/utils/words/wordplay-shuffle.cpp b/demos/utils/words/wordplay-shuffle.cpp index bbc85aa67b..c435336e1b 100644 --- a/demos/utils/words/wordplay-shuffle.cpp +++ b/demos/utils/words/wordplay-shuffle.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file wordplay-shuffle.cpp + * @file * @brief Find words that are a shuffle of another word. */ diff --git a/doc/Makefile b/doc/Makefile index 7f97453cb9..d81f3c4435 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -2,7 +2,7 @@ # # You can set these variables from the command line. -SPHINXOPTS = +SPHINXOPTS = -W --keep-going SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build @@ -177,10 +177,10 @@ doctest: "results in $(BUILDDIR)/doctest/output.txt." coverage: - $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage - python3 -m coverxygen --xml-dir doxyoutput/xml --src-dir .. --format json-summary --output $(BUILDDIR)/doc-coverage.json + mkdir -p $(BUILDDIR) + python3 -m coverxygen --xml-dir doxy*/xml --src-dir .. --format json-summary --output $(BUILDDIR)/doc-coverage.json @echo "Testing of coverage in the sources finished, look at the " \ - "results in $(BUILDDIR)/doc-coverage.json." + "results in $(BUILDDIR)/doc-coverage.json." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @@ -195,5 +195,5 @@ pseudoxml: .PHONY: help Makefile clean clean: - rm -rf doxyoutput/ api/ + rm -rf doxygen/ api/ @$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/QuickStartGuides/3-WebTools.md b/doc/QuickStartGuides/3-WebTools.md index 6a4bf8577c..4cb630c074 100644 --- a/doc/QuickStartGuides/3-WebTools.md +++ b/doc/QuickStartGuides/3-WebTools.md @@ -78,13 +78,13 @@ do is compile. The provided Makefile can be run by typing `make Example.js`. This will trigger: ```shell -emcc -std=c++17 -Wall -Wno-unused-function -I../../include/emp/ -Os -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap']" -s TOTAL_MEMORY=67108864 --js-library ../../include/emp/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s NO_EXIT_RUNTIME=1 Example.cc -o Example.js +emcc -std=c++20 -Wall -Wno-unused-function -I../../include/emp/ -Os -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap']" -s TOTAL_MEMORY=67108864 --js-library ../../include/emp/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s NO_EXIT_RUNTIME=1 Example.cc -o Example.js ``` - emscripten uses the `emcc` compiler (or `em++`, since we are using C++). -- `-std=c++17` : Empirical requires c++17. +- `-std=c++20` : Empirical requires c++20. - `-Wall -Wno-unused-function` : turn on all warnings by default except for unused functions, since not all library functions are diff --git a/doc/QuickStartGuides/todos/todo.md b/doc/QuickStartGuides/todos/todo.md index 528da1f6a1..a197deb93e 100644 --- a/doc/QuickStartGuides/todos/todo.md +++ b/doc/QuickStartGuides/todos/todo.md @@ -1,7 +1,7 @@ # Work In Progress The following pages are a work in progress 🚧. -[Contributions](contribution-guidelines-and-review) are welcome! +[Contributions](../../dev/contribution-guidelines-and-review) are welcome! ```{toctree} X-CreateWorld diff --git a/doc/bibliography.bib b/doc/bibliography.bib new file mode 100644 index 0000000000..8ccc44609a --- /dev/null +++ b/doc/bibliography.bib @@ -0,0 +1,189 @@ + +@article{collessReviewPhylogeneticsTheory1982, + title = {Review of Phylogenetics: The Theory and Practice of Phylogenetic Systematics.}, + volume = {31}, + issn = {0039-7989}, + url = {https://www.jstor.org/stable/2413420}, + doi = {10.2307/2413420}, + shorttitle = {Review of Phylogenetics}, + pages = {100--104}, + number = {1}, + journal = {Systematic Zoology}, + author = {Colless, Donald H.}, + editora = {Wiley, E. O.}, + editoratype = {collaborator}, + year = {1982} +} + +@article{dolsonInterpretingTapeLife2020, + title = {Interpreting the Tape of Life: Ancestry-based Analyses Provide Insights and Intuition about Evolutionary Dynamics}, + volume = {26}, + rights = {All rights reserved}, + pages = {1--22}, + number = {1}, + journal = {Artificial Life}, + author = {Dolson, Emily and Lalejini, Alexander and Jorgensen, Steven and Ofria, Charles}, + year = {2020}, + url = {https://direct.mit.edu/artl/article/26/1/58/93272/Interpreting-the-Tape-of-Life-Ancestry-Based} +} + +@article{faithConservationEvaluationPhylogenetic1992, + title = {Conservation evaluation and phylogenetic diversity}, + volume = {61}, + issn = {0006-3207}, + url = {http://www.sciencedirect.com/science/article/pii/0006320792912013}, + doi = {10.1016/0006-3207(92)91201-3}, + pages = {1--10}, + number = {1}, + journal = {Biological Conservation}, + journaltitle = {Biological Conservation}, + shortjournal = {Biological Conservation}, + author = {Faith, Daniel P.}, + year = {1992} +} + +@article{isaacMammalsEDGEConservation2007, + title = {Mammals on the {EDGE}: Conservation Priorities Based on Threat and Phylogeny}, + volume = {2}, + issn = {1932-6203}, + url = {http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0000296}, + doi = {10.1371/journal.pone.0000296}, + shorttitle = {Mammals on the {EDGE}}, + abstract = {Conservation priority setting based on phylogenetic diversity has frequently been proposed but rarely implemented. Here, we define a simple index that measures the contribution made by different species to phylogenetic diversity and show how the index might contribute towards species-based conservation priorities. We describe procedures to control for missing species, incomplete phylogenetic resolution and uncertainty in node ages that make it possible to apply the method in poorly known clades. We also show that the index is independent of clade size in phylogenies of more than 100 species, indicating that scores from unrelated taxonomic groups are likely to be comparable. Similar scores are returned under two different species concepts, suggesting that the index is robust to taxonomic changes. The approach is applied to a near-complete species-level phylogeny of the Mammalia to generate a global priority list incorporating both phylogenetic diversity and extinction risk. The 100 highest-ranking species represent a high proportion of total mammalian diversity and include many species not usually recognised as conservation priorities. Many species that are both evolutionarily distinct and globally endangered ({EDGE} species) do not benefit from existing conservation projects or protected areas. The results suggest that global conservation priorities may have to be reassessed in order to prevent a disproportionately large amount of mammalian evolutionary history becoming extinct in the near future.}, + pages = {e296}, + number = {3}, + journal = {{PLOS} {ONE}}, + shortjournal = {{PLOS} {ONE}}, + author = {Isaac, Nick J. B. and Turvey, Samuel T. and Collen, Ben and Waterman, Carly and Baillie, Jonathan E. M.}, + urldate = {2018-01-09}, + year = {2007}, + langid = {english}, + keywords = {Animal phylogenetics, Conservation genetics, Conservation science, Endangered species, Mammals, Species diversity, Species extinction, Taxonomy} +} + +@article{mirSoundCollesslikeBalance2018, + title = {Sound Colless-like balance indices for multifurcating trees}, + volume = {13}, + issn = {1932-6203}, + url = {https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0203401}, + doi = {10.1371/journal.pone.0203401}, + abstract = {The Colless index is one of the most popular and natural balance indices for bifurcating phylogenetic trees, but it makes no sense for multifurcating trees. In this paper we propose a family of Colless-like balance indices C D , f that generalize the Colless index to multifurcating phylogenetic trees. Each C D , f is determined by the choice of a dissimilarity D and a weight function f : N → R ≥ 0. A balance index is sound when the most balanced phylogenetic trees according to it are exactly the fully symmetric ones. Unfortunately, not every Colless-like balance index is sound in this sense. We prove then that taking f(n) = ln(n + e) or f(n) = en as weight functions, the resulting index C D , f is sound for every dissimilarity D. Next, for each one of these two functions f and for three popular dissimilarities D (the variance, the standard deviation, and the mean deviation from the median), we find the most unbalanced phylogenetic trees according to C D , f with any given number n of leaves. The results show that the growth pace of the function f influences the notion of “balance” measured by the indices it defines. Finally, we introduce our R package “{CollessLike},” which, among other functionalities, allows the computation of Colless-like indices of trees and their comparison to their distribution under Chen-Ford-Winkel's α-γ-model for multifurcating phylogenetic trees. As an application, we show that the trees in {TreeBASE} do not seem to follow either the uniform model for multifurcating trees or the α-γ-model, for any values of α and γ.}, + pages = {e0203401}, + number = {9}, + journal = {{PLOS} {ONE}}, + shortjournal = {{PLOS} {ONE}}, + author = {Mir, Arnau and Rotger, Lucía and Rosselló, Francesc}, + year = {2018}, + langid = {english} +} + +@article{sackinGoodBadPhenograms1972, + title = {“Good” and “Bad” Phenograms}, + volume = {21}, + issn = {1063-5157}, + url = {https://doi.org/10.1093/sysbio/21.2.225}, + doi = {10.1093/sysbio/21.2.225}, + abstract = {“Good” and “Bad” phenograms. Syst. Zool. 21:225-226.—This paper presents a measure for characterizing a phenogram and measuring its usefulness.}, + pages = {225--226}, + number = {2}, + journal = {Systematic Biology}, + shortjournal = {Systematic Biology}, + author = {Sackin, M. J.}, + year = {1972} +} + +@article{shaoTreeBalance1990, + title = {Tree Balance}, + volume = {39}, + issn = {1063-5157}, + url = {https://academic.oup.com/sysbio/article/39/3/266/1727778}, + doi = {10.2307/2992186}, + abstract = {Abstract. Hierarchic classifications can differ with respect to tree balance—the degree to which branches divide the subtended taxa into subsets of equal size.}, + pages = {266--276}, + number = {3}, + journal = {Systematic Biology}, + shortjournal = {Syst Biol}, + author = {Shao, Kwang-Tsao}, + urldate = {2019-04-11}, + year = {1990}, + langid = {english} +} + +@article{tuckerGuidePhylogeneticMetrics2017, + title = {A guide to phylogenetic metrics for conservation, community ecology and macroecology}, + volume = {92}, + issn = {1469-185X}, + url = {http://onlinelibrary.wiley.com/doi/10.1111/brv.12252/abstract}, + doi = {10.1111/brv.12252}, + abstract = {The use of phylogenies in ecology is increasingly common and has broadened our understanding of biological diversity. Ecological sub-disciplines, particularly conservation, community ecology and macroecology, all recognize the value of evolutionary relationships but the resulting development of phylogenetic approaches has led to a proliferation of phylogenetic diversity metrics. The use of many metrics across the sub-disciplines hampers potential meta-analyses, syntheses, and generalizations of existing results. Further, there is no guide for selecting the appropriate metric for a given question, and different metrics are frequently used to address similar questions. To improve the choice, application, and interpretation of phylo-diversity metrics, we organize existing metrics by expanding on a unifying framework for phylogenetic information. Generally, questions about phylogenetic relationships within or between assemblages tend to ask three types of question: how much; how different; or how regular? We show that these questions reflect three dimensions of a phylogenetic tree: richness, divergence, and regularity. We classify 70 existing phylo-diversity metrics based on their mathematical form within these three dimensions and identify 'anchor' representatives: for α-diversity metrics these are {PD} (Faith's phylogenetic diversity), {MPD} (mean pairwise distance), and {VPD} (variation of pairwise distances). By analysing mathematical formulae and using simulations, we use this framework to identify metrics that mix dimensions, and we provide a guide to choosing and using the most appropriate metrics. We show that metric choice requires connecting the research question with the correct dimension of the framework and that there are logical approaches to selecting and interpreting metrics. The guide outlined herein will help researchers navigate the current jungle of indices.}, + pages = {698--715}, + number = {2}, + journal = {Biological Reviews}, + shortjournal = {Biol Rev}, + author = {Tucker, Caroline M. and Cadotte, Marc W. and Carvalho, Silvia B. and Davies, T. Jonathan and Ferrier, Simon and Fritz, Susanne A. and Grenyer, Rich and Helmus, Matthew R. and Jin, Lanna S. and Mooers, Arne O. and Pavoine, Sandrine and Purschke, Oliver and Redding, David W. and Rosauer, Dan F. and Winter, Marten and Mazel, Florent}, + year = {2017}, + langid = {english}, + keywords = {community assembly, biogeography, conservation, range size, biodiversity hotspots, diversity metrics, evolutionary history, phylogenetic diversity, prioritization} +} + +@article{vane-wrightWhatProtectSystematics1991, + title = {What to protect?—Systematics and the agony of choice}, + volume = {55}, + issn = {0006-3207}, + url = {https://www.sciencedirect.com/science/article/pii/000632079190030D}, + doi = {10.1016/0006-3207(91)90030-D}, + shorttitle = {What to protect?}, + abstract = {Politicians and scientists alike now agree that a priority list of global centres for preservationof biological diversity is required. Diversity has generally been measured only in terms of species richness, or in the form of indices combining richness with abundance. Such measures are considered inadequate for the task in hand. A novel index, based on the information content of cladistic classifications and giving a measure of taxonomic distinctness, is introduced. This taxic diversity measure, when coupled with detailed knowledge of distribution, can be used in modified analyses of the type previously developed as 'critical faunas analysis' or 'network analysis'. Central to all such analyses is the concept of complementarity of floras or faunas. By employing complementariry, step-wise procedures can identify optimally efficient, single-site sequences of priority areas for a group, taking existing reserves into account or not, as required. For practical planning it is concluded that two basic rounds of analysis are required: first, recognition of global priority areas by taxic diversity techniques; secondly, within any such area, analysis without taxic weighting (as being developed by Margules and his co-workers) to identify a network of reserves to contain all local taxa and ecosystems. The paper concludes with a brief discussion of some immediate prospects for development of a systematic approach to global conservation evaluation.}, + pages = {235--254}, + number = {3}, + journal = {Biological Conservation}, + shortjournal = {Biological Conservation}, + author = {Vane-Wright, R. I. and Humphries, C. J. and Williams, P. H.}, + year = {1991}, + langid = {english} +} + +@article{warwickTaxonomicDistinctnessEnvironmental1998, + title = {Taxonomic Distinctness and Environmental Assessment}, + volume = {35}, + issn = {0021-8901}, + url = {http://www.jstor.org/stable/2405168}, + abstract = {1. The objectives of this paper are to test the performance of the taxonomic distinctness index, Δ+, in a number of environmental impact scenarios, to examine its relationship with functional diversity and to examine the influence of habitat type and diversity on the index. 2. The index was applied to data on free-living marine nematodes from the coasts of Britain and Chile. 3. The taxonomic distinctness of nematodes from environmentally degraded locations was generally reduced in comparison with that of more pristine locations, often significantly so. 4. Some habitat types may have naturally lower values of taxonomic distinctness than others. However, unless the habitats are degraded in some way the Δ+ values do not generally fall below the lower 95\% confidence limit of the simulated distribution under a null hypothesis that the assemblages behave as if they are a random selection from the regional species pool. This ameliorates the problem encountered with species richness measures of biodiversity, which are much more strongly affected by habitat type and complexity, thus making comparisons difficult between data sets from different habitats or where habitat type is uncontrolled. 5. Taxonomic distinctness in marine nematodes is shown to be related to trophic diversity: a reduction in trophic diversity will lead to a reduction in taxonomic distinctness, although not necessarily to a reduction in species richness. Trophic composition itself is clearly affected by pollution, but is also strongly responsive to the major influence of habitat type. 6. These features of the taxonomic distinctness index, coupled with its lack of dependence on sampling effort or differences in taxonomic rigour between workers and a statistical framework for the assessment of the significance of departure from expectation, suggest that it may prove to be a biologically and ecologically relevant measure of biodiversity. 7. This paper demonstrates that the taxonomic distinctness index has a number of theoretical and logistical advantages over measures of species richness for the purposes of environmental assessment.}, + pages = {532--543}, + number = {4}, + journal = {Journal of Applied Ecology}, + author = {Warwick, R. M. and Clarke, K. R.}, + year = {1998} +} + +@article{webbExploringPhylogeneticStructure2000, + title = {Exploring the Phylogenetic Structure of Ecological Communities: An Example for Rain Forest Trees}, + volume = {156}, + issn = {0003-0147}, + url = {http://www.jstor.org/stable/10.1086/303378}, + doi = {10.1086/303378}, + shorttitle = {Exploring the Phylogenetic Structure of Ecological Communities}, + abstract = {abstract: Because of the correlation expected between the phylogenetic relatedness of two taxa and their net ecological similarity, a measure of the overall phylogenetic relatedness of a community of interacting organisms can be used to investigate the contemporary ecological processes that structure community composition. I describe two indices that use the number of nodes that separate taxa on a phylogeny as a measure of their phylogenetic relatedness. As an example of the use of these indices in community analysis, I compared the mean observed net relatedness of trees (≥10 cm diameter at breast height) in each of 28 plots (each 0.16 ha) in a Bornean rain forest with the net relatedness expected if species were drawn randomly from the species pool (of the 324 species in the 28 plots), using a supertree that I assembled from published sources. I found that the species in plots were more phylogenetically related than expected by chance, a result that was insensitive to various modifications to the basic methodology. I tentatively infer that variation in habitat among plots causes ecologically more similar species to co-occur within plots. Finally, I suggest a range of applications for phylogenetic relatedness measures in community analysis.}, + pages = {145--155}, + number = {2}, + journal = {The American Naturalist}, + author = {Webb, Campbell O. and Losos, Associate Editor: Jonathan B.}, + year = {2000} +} + +@article{winterPhylogeneticDiversityNature2013, + title = {Phylogenetic diversity and nature conservation: where are we?}, + volume = {28}, + issn = {0169-5347}, + url = {http://www.sciencedirect.com/science/article/pii/S0169534712002881}, + doi = {10.1016/j.tree.2012.10.015}, + shorttitle = {Phylogenetic diversity and nature conservation}, + abstract = {To date, there is little evidence that phylogenetic diversity has contributed to nature conservation. Here, we discuss the scientific justification of using phylogenetic diversity in conservation and the reasons for its neglect. We show that, apart from valuing the rarity and richness aspect, commonly quoted justifications based on the usage of phylogenetic diversity as a proxy for functional diversity or evolutionary potential are still based on uncertainties. We discuss how a missing guideline through the variety of phylogenetic diversity metrics and their relevance for conservation might be responsible for the hesitation to include phylogenetic diversity in conservation practice. We outline research routes that can help to ease uncertainties and bridge gaps between research and conservation with respect to phylogenetic diversity.}, + pages = {199--204}, + number = {4}, + journal = {Trends in Ecology \& Evolution}, + journaltitle = {Trends in Ecology \& Evolution}, + shortjournal = {Trends in Ecology \& Evolution}, + author = {Winter, Marten and Devictor, Vincent and Schweiger, Oliver}, + year = {2013} +} diff --git a/doc/bibliography.md b/doc/bibliography.md new file mode 100644 index 0000000000..f0a4db49cf --- /dev/null +++ b/doc/bibliography.md @@ -0,0 +1,4 @@ +# Bibliography + +```{bibliography} bibliography.bib +``` diff --git a/doc/blogs/Binomial.md b/doc/blogs/Binomial.md new file mode 100644 index 0000000000..551b6d85bf --- /dev/null +++ b/doc/blogs/Binomial.md @@ -0,0 +1,43 @@ +# Drawing random values from from non-uniform distributions: A binomial case study + +One common challenge in scientific computing is drawing from specific random distributions. +These can be time-consuming and hard to be acurate, especially when rare events are +important to include. + +There are many different mathematical distributions to consider. For any common distribution, +you should be able to find plenty of information about it on the internet. There are a +handful of specific questions to ask: + +1. Is there a simple, accurate conversion from a uniform [0.0, 1.0) distribution -- like those +produced by most random number generators -- to the distribution I need. + +2. If not, how close of an approximation can I get? Is it good enough? + +3. If not, am I going to be using the same parameters over and over such that I can do some +pre-processing to produce a fast result? (For example, am I using a fair 6-sided die and so I +know each outcome always has a 1/6 chance of showing up?) + +4. If not, how much traditional optimization can I use in the brute-force calculation? + +Here, I am going to focus on *Binomial Distributions* and some other related distributions, +but the logic that I use is applicable elsewhere. + +As a reminder: + +A **Binomial Distribution** asks: If an event is going to occur with probability *p* and we test +for it *N* times, how many times will the event actually occur? *Example*: Each time an +programmer writes a line of code, there is a *p*=0.03 chance that she introduces a bug. How many +bugs does she create after *N*=100 lines of code? + +A **Negative Binomial Distribution** turns this around: If an event is going to occur with +probability *p*, how many times do we need to test for it for it to actually occur *N* times. +*Example*: Given a *p*=0.03 chance of introducing a bug with each line of code, how many lines +would a programmer need to write to reach *N*=10 bugs? + +A **Geometric Distribution** is a special case of the Negative Binomial Distribution where *N*=1. +*Example*: Given a *p*=0.03 chance of introducing a bug, how many lines can a programmer write +before introducing the next bug? + +A **Poisson Distribution** is a continuous version of a Binomial Distribution, used for measuring +the number of independent events that occur in a time period rather than during a specified +number of events. diff --git a/doc/conf.py b/doc/conf.py index 93843550ce..54750792f6 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Empirical documentation build configuration file, created by @@ -18,10 +18,12 @@ # relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. # +import glob import os import sphinx_rtd_theme import subprocess import sys +import textwrap # -- General configuration --------------------------------------------- @@ -32,37 +34,38 @@ # 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.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.mathjax', 'sphinx.ext.todo', 'sphinx_rtd_theme', 'breathe', - 'exhale', 'myst_parser', + 'sphinx_tippy', + 'sphinxcontrib.bibtex' ] +myst_heading_anchors = 4 + +bibtex_bibfiles = ['bibliography.bib'] +bibtex_reference_style = "author_year" +bibtex_default_style = 'unsrt' + # Setup the breathe extension breathe_projects = { "Empirical": "./doxyoutput/xml" } breathe_default_project = "Empirical" -# Setup the exhale extension -exhale_args = { - # These arguments are required - "containmentFolder": "./api", - "rootFileName": "library_root.rst", - "rootFileTitle": "Library API", - "doxygenStripFromPath": "..", - # Suggested optional arguments - "createTreeView": True, - # TIP: if using the sphinx-bootstrap-theme, you need - # "treeViewIsBootstrap": True, - "exhaleExecutesDoxygen": True, - "exhaleDoxygenStdin": "INPUT = ../include" +breathe_projects_source = { + "Empirical" : ( + "../include/emp", glob.glob("**/*.hpp") + ) } +breathe_doxygen_config_options = {'PREDEFINED': 'DOXYGEN_SHOULD_SKIP_THIS'} + +# cpp_index_common_prefix = ["emp", "emp::", "emp::web::", "web", "emp::web"] + # Tell sphinx what the primary language being documented is. primary_domain = 'cpp' @@ -82,7 +85,7 @@ # General information about the project. project = u'Empirical' -copyright = u"2015, Charles Ofria" +copyright = u"2015-2023, Charles Ofria" author = u"Charles Ofria" # The version info for the project you're documenting, acts as replacement @@ -90,15 +93,15 @@ # the built documents. # # The short X.Y version. -version = '0.0.3' +version = '1.0.0' # The full version, including alpha/beta/rc tags. -release = '0.0.3' +release = '1.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 +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -111,6 +114,14 @@ # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True +nitpick_ignore = [ + ('c:identifier', 'int32_t'), + ('c:identifier', 'uint32_t'), + ('c:identifier', 'int64_t'), + ('c:identifier', 'uint64_t'), + ('c:identifier', 'size_t'), + ('c:identifier', 'bool'), +] # -- Options for HTML output ------------------------------------------- @@ -130,7 +141,7 @@ # 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'] +html_static_path = [] html_copy_source = False # -- Options for HTMLHelp output --------------------------------------- diff --git a/doc/dev/adding-documentation.md b/doc/dev/adding-documentation.md index b6c98a47f9..df07ba4ec7 100644 --- a/doc/dev/adding-documentation.md +++ b/doc/dev/adding-documentation.md @@ -98,6 +98,7 @@ like the following: ```{eval-rst} .. doxygenfile:: potato.h :project: Empirical + :no-link: ``` ``` diff --git a/doc/dev/contribution-guidelines-and-review.md b/doc/dev/contribution-guidelines-and-review.md index 84b7452a3c..03bd50e891 100644 --- a/doc/dev/contribution-guidelines-and-review.md +++ b/doc/dev/contribution-guidelines-and-review.md @@ -161,19 +161,29 @@ Specific cases include: ## Checklist -Copy and paste the following into a pull request comment when it is -ready for review: +To submit code to Empirical, open a Pull Request on Github. When you are ready for it to be reviewed, add the "Merge Ready" label. Before requesting a review, you should ensure that all checks on Github pass and confirm that the following automatically-checkable things are true: - - [ ] Is it mergeable? - - [ ] Did it pass the tests? - - [ ] Does the source code follow the Empirical coding standards? - - [ ] Has the code been commented (especially non-intuitive sections) - - [ ] Was a spellchecker run on the source code and documentation after - changes were made? +- The code is merge-able into master (checked by Github) +- All tests pass (checked by Github actions CI) +- The documentation builds successfully (checked by readthedocs action and Github actions CI) + +You should also verify that the following are true. These cannot be manually checked, so copy and paste the following checklist into a pull request comment and check them off as you do them: + + - [ ] The source code follows the Empirical coding standards + - [ ] The code been commented (especially non-intuitive sections) + - [ ] A spellchecker was run on the source code and documentation after changes were made? + - [ ] All newly added/modified code has high-quality tests associated with it (the CodeCov Github Automation will check this, but you should verify it) + - [ ] All newly-added functions and classes have doxygen-compatible docstrings It's expected that before requesting a code review the author of the PR will have checked all these things on their own. It's also expected that whomever reviews the PR will check these individual items as well. -Though the CI runs most of these and will pass/fail the PR accordingly -it is not infallible and the whole point of having a code review process +The CI is not infallible and the whole point of having a code review process is to have human eyes go over the changes to the codebase. + +The code reviewer should verify that the above requirements are met, and also that: + + - The code is well-organized + - The code is commented appropriately + - Any new tests are of acceptable quality + - The addition represents an improvement to Empirical diff --git a/doc/dev/guide-to-testing.md b/doc/dev/guide-to-testing.md index 019b70f47c..68e80bacb8 100644 --- a/doc/dev/guide-to-testing.md +++ b/doc/dev/guide-to-testing.md @@ -122,7 +122,6 @@ which are detailed within [their documentation](https://github.com/philsquared/Catch/blob/master/docs/tutorial.md). ## Running Tests with Docker -------------------------- A [devosoft/empirical](https://hub.docker.com/r/devosoft/empirical) Docker image has been set up to make recreating a development environment on your machine easier. diff --git a/doc/filter_namespace b/doc/filter_namespace new file mode 100755 index 0000000000..97f3d09542 --- /dev/null +++ b/doc/filter_namespace @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +import sys + +with open(sys.argv[1]) as infile: + open_braces = 0 + bracket_counts = [] + for line in infile: + line = line.strip() + if line.startswith("namespace emp {") or \ + line.startswith("namespace web {") or \ + line.startswith("namespace prefab {") or \ + line.startswith("namespace emp::prefab {") or \ + line.startswith("namespace evo {"): + open_braces += 1 + bracket_counts.append(open_braces) + print() + elif bracket_counts and bracket_counts[-1] == open_braces and line == "}": + print() + open_braces -= 1 + bracket_counts.pop() + else: + if bracket_counts: + open_braces += line.count("{") + open_braces -= line.count("}") + line = line.replace("emp::", "") + line = line.replace("web::", "") + line = line.replace("prefab::", "") + print(line) diff --git a/doc/index.md b/doc/index.md index 833e55e9cd..c9c98d30ac 100644 --- a/doc/index.md +++ b/doc/index.md @@ -22,13 +22,29 @@ Empirical library into their own projects. Contents: ```{toctree} -:caption: Using Empirical +:caption: Getting Started :maxdepth: 2 - BuiltWithEmpiricalGallery/index QuickStartGuides/index -library/index -api/library_root +``` + +```{toctree} +:caption: Using Empirical +:maxdepth: 4 +:glob: + +library/*/[!_]* +``` + +- {ref}`genindex` +- {ref}`search` + + +```{toctree} +:caption: Bibliography +:maxdepth: 2 + +bibliography ``` diff --git a/doc/library/Evolve/evolve.md b/doc/library/Evolve/evolve.md new file mode 100644 index 0000000000..664ce6e740 --- /dev/null +++ b/doc/library/Evolve/evolve.md @@ -0,0 +1,30 @@ +# Evolution tools + +## World + +```{eval-rst} +.. doxygenfile:: emp/Evolve/World.hpp + :project: Empirical + :no-link: +``` + +## Systematics + +```{ref} systematics +``` + +## NK + +```{eval-rst} +.. doxygenfile:: emp/Evolve/NK.hpp + :project: Empirical + :no-link: +``` + +## Selection + +```{eval-rst} +.. doxygenfile:: emp/Evolve/World_select.hpp + :project: Empirical + :no-link: +``` diff --git a/doc/library/Evolve/systematics.rst b/doc/library/Evolve/systematics.rst new file mode 100644 index 0000000000..2d1f2b8d9f --- /dev/null +++ b/doc/library/Evolve/systematics.rst @@ -0,0 +1,195 @@ +.. SystematicsDocumentation documentation master file, created by + sphinx-quickstart on Thu May 28 16:40:07 2020. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + + +Documentation for Systematics +==================================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + modules + +Systematics +=========== + +Systematics is a classification of organisms based on evolutionary (phylogenetic) relationships. + +*************** +Systematics.h +*************** + +This file is part of Empirical and is located in ``Empirical/source/Evolve/Systematics.h`` + +The systematics manager is used to track genotypes, species, clades, or lineages of organisms in a world. + +Systematics allows a user to generate data to form phylogenetic trees. + +The program can be run with different levels of abstraction, meaning the data can be generated by position, +phenotype, or even genotype if you have a lot of RAM. + +**Note**: You are responsible for filling in templates! Adding the template just gives you a place to store your data. + +Taxon Specifics +=============== + +* Taxon - a group of species with similar characteristics +* Genotypes are the most commonly used Taxon + +A user can see the type and number of mutations that ocurred to bring about a taxon. + +Some information that can be accessed is: + +* taxon ID# ``GetID()`` +* details of organisms in the taxon ``GetInfo()`` +* pointer to the parent group (will return a null pointer if the species was injected) ``GetParent()`` +* how many organisms currently exist in the group and how many total organisms have ever existed in the group ``GetNumOrgs()`` or ``GetTotOrgs()`` +* how many direct offspring groups exist from this group and how many total extant offspring that exist from this taxa ``GetTotalOffspring()`` +* how deep in the tree the node you are examining is ``GetDepth()`` +* when did this taxon first appear in the population ``GetOriginationTime()`` +* when did the taxon leave the population ``GetDestructionTime()`` + +New organisms are added to the taxon using ``AddOrg()``. +New offspring are added to the taxon with ``AddOffspring()`` . + +Organisms are removed with ``RemoveOrg()``. +Offspring are removed with ``RemoveOffspring()`` . + +If there are no more remaining organisms or offspring the taxon will deactivate. + + +General Systematics Data +========================= + +Things that systematics can tell you about a phylogeny and how to access them: + +* Are we tracking a synchronous population? ``GetTrackSynchronous()`` ``SetTrackSynchronous()`` +* Are we storing all taxa that are still alive in the population? ``GetStoreActive()`` ``SetStoreActive()`` +* Are we storing all taxa that are ancestors of the living organisms in the population? ``GetStoreAncestors()`` ``SetStoreAncestors()`` +* Are we storing all taxa that have died out, as have all of their descendants? ``GetStoreOutside()`` ``SetStoreOutside()`` +* Are we storing any taxa types that have died out? ``GetArchive()`` ``SetArchive()`` +* Are we storing the positions of taxa? ``GetStorePosition()`` ``SetStorePosition()`` +* How many living organisms are currently being tracked? ``GetTotalOrgs()`` +* How many independent trees are being tracked? ``GetNumRoots()`` +* What ID will the next taxon have? ``GetNextID()`` +* What is the average phylogenetic depth of organisms in the population? ``GetAveDepth()`` +* To find the most recent common ancestor (MRCA) use ``GetMRCA()`` or ``GetMRCADepth()`` to find the distance to the MRCA. + +**The systematics class tracks the relationships among all organisms bases on the INFO_TYPE +provided. If an offspring has the same value for INFO_TYPE as its parent, it is grouped into +the same taxon. Otherwise a new Taxon is created and the old one is used as its parent in +the phylogeny. If the provided INFO_TYPE is the organism's genome, a traditional phylogeny +is formed, with genotypes. If the organism's behavior/task set is used, then organisms are +grouped by phenotypes. If the organism's position is used, the evolutionary path through +space is tracked. Any other aspect of organisms can be tracked this way as well.** + + +**Generally, all living organisms' taxa should be tracked and ancestral organisms' taxa should be maintained for lineage. +However, not all dead taxa should be maintained, it gets too big.** + +*************************** +Diversity and Distinction +*************************** + +Systematics.h can also be used to find phylogenetic diversity for all extant taxa in the tree, +assuming all edges from parent to child have a length of one. + +When all branch lengths are equal, the phylogenetic diversity is the number of internal nodes plus the number of +extant taxa minus 1. + +You can also find how distinct a specific taxa is from the rest of the population +based on the amount of unique evolutionary history that it represents. + +***************************** +Synchronous Populations +***************************** + +A synchronous population is a population in which each generation is a discrete time point +and a completely new set of individual organisms is created for each generation. This means that +an organism and its parent can never exist at the same time. + +An asynchronous population is the opposite, where generations overlap and organisms reproduce +when they are ready. + +In the systematics manager, synchronicity is controlled with + +``GetTrackSynchronous()`` which returns true or false and +``SetTrackSynchronous(input true or false)`` which allows you to use a synchronous or asynchronous population. + + +Using the Systematics Manager +============================== + +The systematics.h file alone will not give you any useful information. You must use a test file in conjunction with the systematics manager +in order to see output. + +To retrieve some results we will use the file Systematics.cc +which is located in Empirical/tests/Evolve/Systematics.cc. + +To compile to code use this command in the tests directory:: + + make test-Systematics + + +********** +Output +********** + +Terminal Output:: + + AddOrg 25 (id1, no parent) + + AddOrg -10 (id2; parent id1) + + AddOrg 26 (id3, parent id1) + + AddOrg 27 (id4, parent id2) + +The first line of output shows the first organism in the examined phylogeny. This organism is added with AddOrg +and is assigned an ID of id1. The organism has no parent, as seen in the farthest column of output, meaning that +organism id1 will be the root of the phylogeny and produce offspring. + +If we then look at the first number is parenthesis, we see the second organism with and ID of id2. Id2 is a direct descendant of the id1 organism. + +Lastly, if we look at id4, we see that its parent is id2, meaning that we have created another node in the tree +as the organisms move through generations, producing new offspring. + +The terminal output should also include this section:: + + Active count: 11 [18|1,0|17] [17|1,2|11] [15|1,0|null] [12|1,1|11] [16|1,0|11] [11|1,3|null] [6|1,0|5] [19|1,0|17] [5|1,1|null] [4|1,0|null] [3|1,0|null] + + +The 11 at the front refers to the number of total taxa in the phylogeny. + +If we look at the first set of numbers: ``[18|1, 0|17]`` + +The first number in brackets, 18 in this case, is the taxon of the organism where +a mutation occurred. 1, the next number, is the number of mutations that led to this branch. +0 is the number of offspring from this organism. Lastly, 17 is the id of the parent organism. + +As for the second set ``[17|1, 2|11]`` -- this is taxon 17, one mutation occurred, +id17 had 2 offspring, and its parent is id11. + +The last portion of the output has several lines of 3 numbers. + +It should look like this: :: + + 1 : 0 : -1 + 2 : 0 : -1 + 3 : 0 : 0 + 4 : 0 : 0 + 5 : 0 : 0 + 6 : 0 : 0 + 7 : 0 : 0 + 8 : 0 : 987 + 9 : 0 : 986 + 10 : 0 : 987 + 11 : 0 : 988 + 12 : 0 : 987 + 13 : 0 : 988 + +The first number is the organism number. The second number is the position of the organism. +The third number is the fitness of the organism at position 0. diff --git a/doc/library/base/base.md b/doc/library/base/base.md index 14e7d327fe..6ea101aa5f 100644 --- a/doc/library/base/base.md +++ b/doc/library/base/base.md @@ -36,17 +36,15 @@ emp::array array({1,2,3}); // You can treat this just like an std::array ``` -Empirical asserts ------------------ +## Empirical asserts These asserts function similarly to normal asserts, with a few important -additional features: - If compiled with Emscripten they will provide -pop-up alerts when run in a web browser. - emp_assert can take -additional arguments. If the assert is triggered, those extra arguments -will be evaluated and printed. - if NDEBUG -or- EMP_NDEBUG is defined, -the expression in emp_assert() is not evaluated. - if EMP_TDEBUG is -defined, emp_assert() goes into test mode and records failures, but -does not abort. (useful for unit tests of asserts) +additional features: + +- If compiled with Emscripten they will provide pop-up alerts when run in a web browser. +- emp_assert can take additional arguments. If the assert is triggered, those extra arguments will be evaluated and printed. +- if NDEBUG **or** EMP_NDEBUG is defined, the expression in emp_assert() is not evaluated. +- if EMP_TDEBUG is defined, emp_assert() goes into test mode and records failures, but does not abort. (useful for unit tests of asserts) Example: @@ -60,24 +58,41 @@ emp_assert(a==5, a); When compiled in debug mode (i.e. without the -DNDEBUG flag), this will trigger an assertion error and print the value of a. -### emp_assert API (base/assert.hpp) +Empirical also has an emscripten-specific assert, which will only trigger an error when the code was compiled with Emscripten: -% ```{doxygendefine} emp_assert -% :project: Empirical -% ``` +```cpp +#include "Empirical/include/emp/base/emscripten_assert.hpp" + +int a = 6; +// If compiled in with emscripten in debug mode, +// this will print a warning and the value of a +emp_emscripten_assert(a==5, a); -```{eval-rst} -.. doxygendefine:: emp_assert - :project: Empirical - :no-link: ``` -```{eval-rst} -.. doxygendefine:: emp_emscripten_assert - :project: Empirical - :no-link: +If you want your assert to be triggered outside of debug mode, you can use {c:func}`emp_always_assert`. + +## Empirical Warnings + +These work very similar to Empirical asserts, except they do not throw assertion errors. When compiled in debug mode, they will print a warning (and any desired additional information) on failure but program execution will continue. When compiled outside of debug mode they do nothing. + +```cpp +#include "Empirical/include/emp/base/assert_warning.hpp" +#include + +int a = 6; +// If compiled in debug mode, this will print a +// warning and the value of a +emp_assert_warning(a==5, a); + +// This will get printed because no assertion +// error will be triggered +std::cout << "Still running!" << std::endl; + ``` +If you want your warning to be triggered outside of debug mode, you can use {c:func}`emp_always_assert_warning`. + ## Empirical pointers Ptr objects behave as normal pointers under most conditions. However, if @@ -100,3 +115,14 @@ int_ptr.New(123456); // Store the value 123456 in int_ptr. std::cout << "*int_ptr = " << *int_ptr << std::endl; int_ptr.Delete(); ``` + +## API + + + +```{eval-rst} +.. toctree:: + :glob: + + api/* +``` diff --git a/doc/library/bits/bits.md b/doc/library/bits/bits.md index a32aa087b6..daf71df77a 100644 --- a/doc/library/bits/bits.md +++ b/doc/library/bits/bits.md @@ -1,33 +1,14 @@ # Bits -## BitMatrix +Stuff about bits -```{eval-rst} -.. doxygenfile:: emp/bits/BitMatrix.hpp - :project: Empirical - :no-link: -``` - -## BitSet - -```{eval-rst} -.. doxygenfile:: emp/bits/BitSet.hpp - :project: Empirical - :no-link: -``` - -## BitSet Utilities +## API + + ```{eval-rst} -.. doxygenfile:: emp/bits/bitset_utils.hpp - :project: Empirical - :no-link: -``` +.. toctree:: + :glob: -## BitVector - -```{eval-rst} -.. doxygenfile:: emp/bits/BitVector.hpp - :project: Empirical - :no-link: + api/* ``` diff --git a/doc/library/compiler/compiler.md b/doc/library/compiler/compiler.md index 6db62d8a36..cecdbcf611 100644 --- a/doc/library/compiler/compiler.md +++ b/doc/library/compiler/compiler.md @@ -1,41 +1,12 @@ # Compiler -## Deterministic Finite Automata +## API + + ```{eval-rst} -.. doxygenfile:: emp/compiler/DFA.hpp - :project: Empirical - :no-link: -``` - -## Lexer Utilities - -```{eval-rst} -.. doxygenfile:: emp/compiler/lexer_utils.hpp - :project: Empirical - :no-link: -``` - -## Lexer +.. toctree:: + :glob: -```{eval-rst} -.. doxygenfile:: emp/compiler/Lexer.hpp - :project: Empirical - :no-link: -``` - -## NonDeterministic Finite Automata - -```{eval-rst} -.. doxygenfile:: emp/compiler/NFA.hpp - :project: Empirical - :no-link: -``` - -## Regular Expressions - -```{eval-rst} -.. doxygenfile:: emp/compiler/RegEx.hpp - :project: Empirical - :no-link: + api/* ``` diff --git a/doc/library/data/data.md b/doc/library/data/data.md index a3c282c8d7..9d64cea237 100644 --- a/doc/library/data/data.md +++ b/doc/library/data/data.md @@ -29,34 +29,11 @@ for collecting data over the course of a computational experiment. ## Data Tools API -### DataNodes - -```{eval-rst} -.. doxygenfile:: emp/data/DataNode.hpp - :project: Empirical - :no-link: -``` - -### DataManagers - -```{eval-rst} -.. doxygenfile:: emp/data/DataManager.hpp - :project: Empirical - :no-link: -``` - -### DataInterfaces - + + ```{eval-rst} -.. doxygenfile:: emp/data/DataInterface.hpp - :project: Empirical - :no-link: -``` - -### DataFiles +.. toctree:: + :glob: -```{eval-rst} -.. doxygenfile:: emp/data/DataFile.hpp - :project: Empirical - :no-link: + api/* ``` diff --git a/doc/library/datastructs/datastructs.md b/doc/library/datastructs/datastructs.md index 9323db55c5..910a7f9743 100644 --- a/doc/library/datastructs/datastructs.md +++ b/doc/library/datastructs/datastructs.md @@ -1,89 +1,12 @@ # Data structures -## Cache +## API + + ```{eval-rst} -.. doxygenfile:: emp/datastructs/Cache.hpp - :project: Empirical - :no-link: -``` - -## Dynamic Strings - -```{eval-rst} -.. doxygenfile:: emp/datastructs/DynamicString.hpp - :project: Empirical - :no-link: -``` - -## Graph Utilities - -```{eval-rst} -.. doxygenfile:: emp/datastructs/graph_utils.hpp - :project: Empirical - :no-link: -``` - -## Graphs +.. toctree:: + :glob: -```{eval-rst} -.. doxygenfile:: emp/datastructs/Graph.hpp - :project: Empirical - :no-link: -``` - -## Index Map - -```{eval-rst} -.. doxygenfile:: emp/datastructs/IndexMap.hpp - :project: Empirical - :no-link: -``` - -## Map Utilities - -```{eval-rst} -.. doxygenfile:: emp/datastructs/map_utils.hpp - :project: Empirical - :no-link: -``` - -## RandomAccess Set - -```{eval-rst} -.. doxygenfile:: emp/datastructs/ra_set.hpp - :project: Empirical - :no-link: -``` - -## Set Utilities - -```{eval-rst} -.. doxygenfile:: emp/datastructs/set_utils.hpp - :project: Empirical - :no-link: -``` - -## Tuple Struct - -```{eval-rst} -.. doxygenfile:: emp/datastructs/tuple_struct.hpp - :project: Empirical - :no-link: -``` - -## Tuple Utilities - -```{eval-rst} -.. doxygenfile:: emp/datastructs/tuple_utils.hpp - :project: Empirical - :no-link: -``` - -## Vector Utilities - -```{eval-rst} -.. doxygenfile:: emp/datastructs/vector_utils.hpp - :project: Empirical - :no-link: + api/* ``` diff --git a/doc/library/debug/debug.md b/doc/library/debug/debug.md index 725522d8d6..ef445593e0 100644 --- a/doc/library/debug/debug.md +++ b/doc/library/debug/debug.md @@ -1,33 +1,12 @@ # Debug -## Alert +## API + + ```{eval-rst} -.. doxygenfile:: emp/debug/alert.hpp - :project: Empirical - :no-link: -``` - -## Debugging Tools - -```{eval-rst} -.. doxygenfile:: emp/debug/debug.hpp - :project: Empirical - :no-link: -``` - -## Errors +.. toctree:: + :glob: -```{eval-rst} -.. doxygenfile:: emp/debug/errors.hpp - :project: Empirical - :no-link: -``` - -## Memory Tracking - -```{eval-rst} -.. doxygenfile:: emp/debug/mem_track.hpp - :project: Empirical - :no-link: + api/* ``` diff --git a/doc/library/evolve/evolve.md b/doc/library/evolve/evolve.md new file mode 100644 index 0000000000..289b837e5a --- /dev/null +++ b/doc/library/evolve/evolve.md @@ -0,0 +1,266 @@ +# Evolution tools + +## Phylotracklib (Systematics Manager) + +The systematics manager tracks phylogenetic relationships among organisms within a digital +evolution system. For asexual systems, these relationships forma phylogenetic tree +(phylogeny). Systems with recombination (i.e. sexual reproduction systems) are not +yet supported. One of the major benefits of doing *in silico* evolution experiments (instead of or in addition to laboratory or field experiments) is that they allow perfect measurement of quantities that can only be inferred in nature. Once such property is the precise phylogeny (i.e. ancestry tree) of the population. + +![An example phylogeny](../images/phylogeny.jpg) + +At face value, measuring a phylogeny in *in silico* evolution may seem very straightforward: you just need to keep track of what gives birth to what. However, multiple aspects turn out to be non-trivial (see below). The Empirical systematics manager is designed to handle these challenges in a flexible way such that it can be easily plugged into any digital evolution system. It flexibly handle all aspects of recording phylogenies in *in silico* evolution. + +Note: A python wrapper for systematics manager exists in the form of the [Phylotrackpy library](https://phylotrackpy.readthedocs.io/en/latest/). + +### Features + +#### Flexible taxon definitions + +One of the central decisions when creating a phylogeny is choosing what the taxonomic units (i.e. the nodes in the tree) are. In a traditional phylogeny, these nodes are species. However, the concept of species is so murky that it is impossible to generically apply to computational evolution systems (we'd argue that it's questionable whether it could even be applied to biological data recorded at perfect temporal resolution, but that's a separate conversation). One alternative would be to make a phylogeny in which all nodes are individuals, but these trees are usually so large that they are impractical to work with. + +Increasingly, biologists have embraced the idea of building trees in which the taxonomic units are not species. Often, these are denoted by referring to them as an "X tree", where X is the taxonomic unit of interest. A traditional phylogeny, then, is a species tree. This terminology is particularly common in cancer evolution research, in which species trees are often contrasted with "clone trees" or "gene trees", in which the taxonomic units are genotypes. + +We can generalize this concept - any phylogeny of individuals can be abstracted by lumping individuals together based on a shared feature (see figure). This feature could be something simple like a phenotypic or genotypic trait, or it could be something more complex. For example, to approximate something more like a traditional biological species concept, you could choose to define an individual as being a member of a new taxonomic unit if it fails to produce successful offspring when recombined with an individual prototypical of its parent species (although note that the stochasticity inherent in this definition could have some unexpected side effects). The broader the grouping, the smaller the phylogeny will be (e.g. a genotype tree will be larger than a phenotype tree). + +![Illustration of different ways taxonomic units could be defined](https://raw.githubusercontent.com/emilydolson/interpreting_the_tape_of_life/master/figs/dolson.lineage_metrics_cartoon.png) +(Figure from "Quantifying the tape of life: Ancestry-based metrics provide insights and intuition about evolutionary dynamics" published in the proceedings of [ALIFE 2018](http://2018.alife.org/)) + +So how does the systematics manager handle this problem? By giving you the power to define taxonomic groupings however you want! When you construct a `Systematics` object, you give it a function that it can use to determine the taxonomic unit of an organism. Later, when organisms are born, you will pass them to the `Systematics` object and it will run that function on them. If the result matches the result of calling that function on the new organism's parent, then the organism will be considered to be part of the same taxonomic unit (taxon) as its parent. If the results do not match, the new organism will be considered to be the start of a new taxon descended from the parent's taxon. + +Note that multiple taxa may evolve that are the "same" (i.e. running the function on organisms in each yields the same result); each unique evolutionary origin will be counted as a distinct taxon. For example, let's imagine we are building a phylogeny of real animals in nature and grouping them into taxa based on whether they spend more than 50% of their lives in water. Fish and whales would be parts of two different taxa. Even though they both live their whole lives in the water, there would be a "land" taxon in between them on the line of descent. + +Example: + +```cpp +#include "Systematics.hpp" + +// Assuming that the org_t class has a member variable called genotype that stores +// its genotype, this will create a phylogeny based on genotypes +// The org_t template parameter is the type of the organisms living in your world. +// The info_t template parameter is the type of the piece of information you will +// return to indicate which organisms count as the same taxon. +// e.g. here, info_t should be whatever the type of org.genotype is. +sys = emp::Systematics sys([](const org_t & org){return org.genotype;}); +``` + +#### Pruning + +Phylogenies can get very large. So large that they can cause you program to exceed its available memory. To combat this problem, phylogenies can be "pruned" so they only contain extant (i.e. not extinct) taxa and their ancestors. If the `store_outside` variable for a systematics object is set to `False` (the default), this pruning will happen automatically. If you truly want to keep track of every taxon that ever existed, you can do so by setting `store_outside` to `True`. If you want to keep track of some historical data but can't afford the memory overhead of storing every taxon that ever existed, an intermediate options is to periodically print "snapshot" files containing all taxa currently in the phylogeny. + +#### Phylostatistics calculations + +Phylogenies are very information-dense data structures, but it can sometimes be hard to know how to usefully compare them. A variety of phylogenetic summary statistics (mostly based on topology) have been developed for the purpose of usefully making high-level comparisons. The systematics manager has many of these statistics built-in and can automatically output them. It can even keep running data (mean, variance, maximum, and minimum) on each statistic over time in a highly efficient format. + +Available statistics include: + +- Mean/max/min/sum/variance pairwise distance +- Colless-like index (a variant of the Colless index adjusted for trees with multifurcations) {cite:p}`mirSoundCollesslikeBalance2018` +- Sackin index {cite:p}`sackinGoodBadPhenograms1972` +- Phylogenetic diversity {cite:p}`faithConservationEvaluationPhylogenetic1992` + +#### Efficiency + +Tracking phylogenies can be computationally expensive. We have sought to keep the computational overhead as low as possible. + +We also provide the option to remove all taxa that died before a certain time point (the {cpp:func}`Systematics::RemoveBefore` method). Use this with caution, as it will inhibit the use of many phylogenetic topology metrics. In extreme cases it may be necessary to keep your memory footprint sufficiently low, though. + +If you need substantially higher efficiency (in terms of time or memory) or are working in a distributed computing environment (where having a centralized phylogeny tracker can pose a large bottleneck), check out the [hstrat library](https://github.com/mmore500/hstrat), which lets you sacrifice some precision to achieve lower computational overhead. + +#### Flexible output options + +At any time, you can tell the systematics manager to print out the full contents of its current phylogeny in a "snapshot" file. These files will be formatted according to the [Artificial Life Phylogeny Data Standard format](https://alife-data-standards.github.io/alife-data-standards/phylogeny.html). By default they will contain the following columns for each taxon: 1) unique ID, 2) ancestor list, 3) origin time, and 4) destruction time. However, you can add additional columns with the {cpp:func}`Systematics::AddSnapshotFun` method. + +You can also print information on a single lineage. + +### Useful background information + +There are certain quirks associated with real-time phylogenies that you might not be used to thinking about if you're used to dealing with reconstructed phylogenies. Many of these discrepancies are the result of the very different temporal resolutions on which these types of phylogenies are measured, and the fact that the taxonomic units we work with are often at a finer resolution than species. We document some here so that they don't catch you off guard: + +- **Multifurcations are real**: In phylogenetic reconstructions, there is usually an assumption that any multifurcation/polytomy (i.e. a node that has more than two child nodes) is an artifact of having insufficient data. In real-time phylogenies, however, we often observe multifurcations that we know for sure actually happened. +- **Not all extant taxa are leaf nodes**: In phylogenetic reconstructions, there is usually an assumption that all extant (i.e. still living) taxa are leaf nodes in the phylogeny (i.e. none of them are parents/offspring of each other; similar taxa are descended from a shared common ancestor). In real-time phylogenies it is entirely possible that one taxon gives birth to something that we have defined as a different taxon and then continues to coexist with that child taxon. +- **Not all nodes are branch points**: In phylogenetic reconstructions, we only attempt to infer where branch points (i.e. common ancestors of multiple taxa) occurred. We do not try to infer how many taxa existed on a line of descent between a branch point and an extant taxa. In real-time phylogenies we observe exactly how many taxa exist on this line of descent and we keep a record of them. In practice there are often a lot of them, depending on you define your taxa. It is unclear whether we should include these non-branching nodes when calculating phylogenetic statistics (which is why the systematics manager lets you choose whether you want to). + +![An example of a full digital evolution phylogeny](../images/FullPhylogeny.png) + +The above image represents an actual phylogeny measured from digital evolution. Each rectangle represents a different taxon. It's position along the x axis represents the span of time it existed for. Note that there are often sections along a single branch where multiple taxa coexisted for a period of time. Circles represent extant taxa at the end of this run. + +### Glossary + +Some useful terminology that might be useful in understanding the documentation (and especially the code base) for the systematics manager, particularly in light of the fact that different sub-fields of evolutionary biology tend to use different words in many of these contexts. + +- **Taxon**: a generic word for a specific taxonomic unit. We use "taxon" as a generic term to represent a node in a phylogeny. For example, species are a common type of taxon to use when depicting portions of the phylogeny of life on earth. However, sometimes people choose to use higher-order types of taxa (e.g. genus, family, order, class, etc.) when they are trying to depict a larger swath of the whole phylogeny. +- **Taxa**: Plural of taxon. +- **Multifurcation/polytomy**: A node in a phylogeny that has more than two child nodes +- **Bifurcation**: A node in a phylogeny that has exactly two child nodes. +- **Non-branch node**: A node in a phylogeny with only one child node. +- **Leaf node**: A node in a phylogeny with no children. +- **Most Recent Common Ancestor (MRCA)**: The most recent node in a phylogeny that is a common ancestor of all nodes associated with extant taxa. If the phylogeny is pruned, there won't be any branch points before the MRCA (because any branches not leading to the MRCA would lead to taxa that are now extinct). +- **Coalescence events**: Occur when the most recent common ancestor changes (i.e. all descendants from one side of the deepest branch of the phylogeny have gone extinct). In the absence of diversity-preserving features coalescence events are expected to occur by chance with a frequency dependent on population size and spatial structure (but be careful of distributional assumptions). Observing coalescence less frequently than you would expect by chance can be an indication that ecological interactions are present (we have discussed this more [here](https://direct.mit.edu/artl/article/26/1/58/93272/Interpreting-the-Tape-of-Life-Ancestry-Based) and [here](https://direct.mit.edu/artl/article/25/1/50/2915/The-MODES-Toolbox-Measurements-of-Open-Ended)). + +### Quickstart + +#### Installation + +The Systematics manager is part of Empirical. Because Empirical is header-only, you can include whichever parts of it you want. To just use the Systematics manager, you just need to include the Systematics.hpp header. Note that the Systematics manager depends on the result of Empirical, so you will need to download the entire library. Currently, we recommend using the mabe-systematics branch: + +```bash +git clone --recursive git@github.com:devosoft/Empirical.git +cd Empirical +git checkout mabe-systematics +``` + +Then in your C++ file: + +```cpp +#include "Evolve/Systematics.hpp" +``` + +To compile your code using the systematics manager, you will need to tell you compiler where to find Systematics.hpp with the `-I` flag (the below assumes you are compiling from the directory you cloned Empirical into; if you aren't, you will need to include the full path to Empirical): + +```bash +g++ -IEmpirical/include/emp my_source_file.cc +``` + +#### Usage + +##### Creating a systematics object + +The first step in tracking a phylogeny with the systematics manager is to make a systematics object. The most important decision to make at this point is how to define taxa in your phylogeny (for more information, see the "flexible taxon definition" section under "features"). You can do so by passing a function to the systematics constructor which takes an organism object and returns a string that specifies a taxon. + +For example, to build a phylogeny based on genotypes, you could do the following: + +```cpp +#include "Evolve/Systematics.hpp" + +struct MyOrg { + std::string genotype; +}; + +// The first template argument is the type of your organisms +// The second template argument is the type of the piece of information you +// are using to differentiate taxa (here its a string because the genotype +// member variable of our organism struct is a string) +sys = emp::Systematics sys([](const MyOrg & org){return org.genotype;}); +``` + +There are a couple of other decisions that you also need to make at this point. The first is which set of taxa to store in the systematics manager. The defaults here are most likely what you want to use, but in case they aren't, the systematics manager can be told to store or not store the following sets of taxa: + +- **active**: the taxa that still currently have living members. You almost certainly want to store these (without them you don't really have a phylogeny), but can technically disable them by setting the `store_active` argument in the constructor to false. +- **ancestors**: the taxa that are ancestors of active taxa. You almost certainly want to store these too (without them you don't really have a phylogeny), but can technically disable them by setting the `store_ancestors` argument in the constructor to false. +- **outside**: the taxa that are not in either of the other two groups (i.e. taxa that have gone extinct and all of their ancestors have gone extinct). If you store these, your phylogeny will get very large very fast, so doing so is generally not recommended. It is occasionally useful, though, so you can enable storing these taxa by setting the `store_all` argument in the constructor to true. + +The second decision is slightly trickier. Once you start adding organisms to the systematics manager, it will create {cpp:class}`Taxon` objects associated with each one to keep track of which taxon it is part of. You will need to use these taxon objects when adding future organisms, to specify which taxon their parent was part of. If you have control over your organism class, it is likely that the easiest option is to add a `taxon` member variable and store the taxon there. However, if you cannot add arbitrary data to your organism class, keeping track of taxon objects can get annoying. For this reason, the systematics manager gives you the option of letting it manage them. To do so, it needs a way to map individuals to taxa (since its possible there are duplicate taxa, simply running the organism to taxon function again won't work). It achieves this mapping by keeping track of each organism's position in the population. Thus, to have the systematics manager keep track of taxon objects itself, you must set the `store_pos` argument in the constructor to true. You must also use the position-based versions of add_org and remove_org, and make sure to notify the systematics manager if any organism ever changes position during its lifetime for any reason. + +Once you have created the systematics object, you just need to do two things: 1) notify it when something is born, and 2) notify it when something dies. + +##### Notifying the systematics object of births + +You must notify the systematics manager of births using the {cpp:func}`Systematics::AddOrg` family of functions. These functions require that you provide the newly born organism as well as either the taxon object of its parent or the position of its parent (if the systematics manager is tracking positions). + +Example of tracking taxa as object attributes (assume we're building on our example above, and already have created a {cpp:class}`Systematics` manager called `sys`): + +```cpp +// Do whatever you would normally do to create your first organism +// Here, we're assuming we can just call a constructor called Organism() +MyOrg my_org; + +// Notify systematics manager of this organism's birth +// This is the first org, so it doesn't have a parent +// so we do not pass a second argument/ +// add_org will return a pointer to this organism's taxon object, which we +// store for future reference +emp::Ptr > taxon = sys.AddOrg(my_org); + +// Assume stuff happens here that leads to my_org having offspring +// Here, we'll pretend that our organism class has a Reproduce method that +// returns a new offspring organism. You should handle this however you +// normally would +MyOrg org_2 = my_org.Reproduce(); + +// Notify the systematics manager of org_2's birth. Since it has a parent, +// we pass the taxon of that parent in as the second argument +sys.AddOrg(org_2, taxon) + +``` + +An example of tracking positions is coming soon. For now, feel free to contact us with questions! + +##### Notifying the systematics object of deaths + +You must notify the systematics manager of deaths using the {cpp:func}`Systematics::RemoveOrg` family of functions. + +As an example (again, building on the previous examples): + +```cpp +// Assume stuff happens that causes my_org to die + +// We notify the systematics manager that this has happened by calling RemoveOrg +// Note that remove_org takes the taxon of the dead organism as an argument, not +// the organism itself +sys.RemoveOrg(taxon) + +``` + +### Taxon properties + +{cpp:class}`Taxon` objects maintain the following information: + +- taxon ID {cpp:func}`Taxon::GetID()` +- details of organisms in the taxon {cpp:func}`Taxon::GetInfo()` +- pointer to the parent group (will return a null pointer if the species was injected) {cpp:func}`Taxon::GetParent()` +- how many organisms currently exist in the group and how many total organisms have ever existed in the group {cpp:func}`Taxon::GetNumOrgs()` or {cpp:func}`Taxon::GetTotOrgs()` +- how many direct offspring groups exist from this group and how many total extant offspring that exist from this taxa {cpp:func}`Taxon::GetTotalOffspring()` +- how deep in the tree the node you are examining is {cpp:func}`Taxon::GetDepth()` +- when did this taxon first appear in the population {cpp:func}`Taxon::GetOriginationTime()` +- when did the taxon leave the population {cpp:func}`Taxon::GetDestructionTime()` + +### Systematics manager properties + +A {cpp:class}`Systematics` manager object maintains the following information (plus the phylogeny itself): + +- Are we tracking a synchronous population? {cpp:func}`SystematicsBase::GetTrackSynchronous` {cpp:func}`SystematicsBase::SetTrackSynchronous` +- Are we storing all taxa that are still alive in the population? {cpp:func}`SystematicsBase::GetStoreActive` {cpp:func}`SystematicsBase::SetStoreActive` +- Are we storing all taxa that are ancestors of the living organisms in the population? {cpp:func}`SystematicsBase::GetStoreAncestors()` {cpp:func}`SystematicsBase::SetStoreAncestors()` +- Are we storing all taxa that have died out, as have all of their descendants? {cpp:func}`SystematicsBase::GetStoreOutside()` {cpp:func}`SystematicsBase::SetStoreOutside()` +- Are we storing any taxa types that have died out? {cpp:func}`SystematicsBase::GetArchive()` {cpp:func}`SystematicsBase::SetArchive()` +- Are we storing the positions of taxa? {cpp:func}`SystematicsBase::GetStorePosition()` {cpp:func}`SystematicsBase::SetStorePosition()` +- How many living organisms are currently being tracked? {cpp:func}`SystematicsBase::GetTotalOrgs()` +- How many independent trees are being tracked? {cpp:func}`SystematicsBase::GetNumRoots()` +- What ID will the next taxon have? {cpp:func}`SystematicsBase::GetNextID()` +- What is the average phylogenetic depth of organisms in the population? {cpp:func}`SystematicsBase::GetAveDepth()` +- To find the most recent common ancestor (MRCA) use {cpp:func}`SystematicsBase::GetMRCA()` or {cpp:func}`SystematicsBase::GetMRCADepth()` to find the distance to the MRCA. + +### Phylogeny metrics + +Many different metrics can be used to quantify th topology of a phylogeny. For more information, see {cite:p}`winterPhylogeneticDiversityNature2013,tuckerGuidePhylogeneticMetrics2017,dolsonInterpretingTapeLife2020`. + +The Empirical systematics manager can calculate + +- Phylogenetic diversity {cite:p}`faithConservationEvaluationPhylogenetic1992` +- Taxon Distinctiveness {cite:p}`vane-wrightWhatProtectSystematics1991` +- Evolutionary Distinctiveness {cite:p}`isaacMammalsEDGEConservation2007` (mean, sum, and variance) +- Mean pairwise distance {cite:p}`webbExploringPhylogeneticStructure2000`, which is equivalent to Average Taxonomic Diversity {cite:p}`warwickTaxonomicDistinctnessEnvironmental1998,tuckerGuidePhylogeneticMetrics2017` +- Sum pairwise distance +- Variance pairwise distance +- Out-degree distribution +- Average origination time +- Colless-like Index {cite:p}`mirSoundCollesslikeBalance2018` +- Sackin Index {cite:p}`sackinGoodBadPhenograms1972` (reviewed in {cite:p}`shaoTreeBalance1990`) +- Depth of most recent common ancestor +- Phenotypic volatility {cite:p}`dolsonInterpretingTapeLife2020` +- Unique taxa on lineage {cite:p}`dolsonInterpretingTapeLife2020` +- Mutation count along lineage {cite:p}`dolsonInterpretingTapeLife2020` +- Tree size +- Maximum depth + +## API + + + +```{eval-rst} +.. toctree:: + :glob: + + api/* +``` diff --git a/doc/library/functional/functional.md b/doc/library/functional/functional.md index 3c1915a9c8..0b8ea00cc0 100644 --- a/doc/library/functional/functional.md +++ b/doc/library/functional/functional.md @@ -1,33 +1,12 @@ # Functional -## Flex Functions +## API + + ```{eval-rst} -.. doxygenfile:: tools/flex_function.hpp - :project: Empirical - :no-link: -``` - -## Function Sets - -```{eval-rst} -.. doxygenfile:: emp/functional/FunctionSet.hpp - :project: Empirical - :no-link: -``` - -## Generic Functions +.. toctree:: + :glob: -```{eval-rst} -.. doxygenfile:: emp/functional/GenericFunctions.hpp - :project: Empirical - :no-link: -``` - -## Memoized Functions - -```{eval-rst} -.. doxygenfile:: emp/functional/memo_functions.hpp - :project: Empirical - :no-link: + api/* ``` diff --git a/doc/library/images/FullPhylogeny.png b/doc/library/images/FullPhylogeny.png new file mode 100644 index 0000000000..af33ec5f47 Binary files /dev/null and b/doc/library/images/FullPhylogeny.png differ diff --git a/doc/library/images/phylogeny.jpg b/doc/library/images/phylogeny.jpg new file mode 100644 index 0000000000..3652ff4d6a Binary files /dev/null and b/doc/library/images/phylogeny.jpg differ diff --git a/doc/library/index.md b/doc/library/index.md index e072b1036f..642bfc67e9 100644 --- a/doc/library/index.md +++ b/doc/library/index.md @@ -11,6 +11,7 @@ compiler/compiler data/data datastructs/datastructs debug/debug +Evolve/evolve functional/functional io/io math/math diff --git a/doc/library/io/io.md b/doc/library/io/io.md index c133920b3e..5b63e70782 100644 --- a/doc/library/io/io.md +++ b/doc/library/io/io.md @@ -1,25 +1,12 @@ # IO -## Inmemory Files +## API + + ```{eval-rst} -.. doxygenfile:: emp/io/File.hpp - :project: Empirical - :no-link: -``` - -## Serialization Macros +.. toctree:: + :glob: -```{eval-rst} -.. doxygenfile:: emp/io/serialize_macros.hpp - :project: Empirical - :no-link: -``` - -## Serialization Tools - -```{eval-rst} -.. doxygenfile:: emp/io/serialize.hpp - :project: Empirical - :no-link: + api/* ``` diff --git a/doc/library/math/math.md b/doc/library/math/math.md index af7a163bd2..0b4365b3b7 100644 --- a/doc/library/math/math.md +++ b/doc/library/math/math.md @@ -1,73 +1,12 @@ # Math -## Combinations +## API + + ```{eval-rst} -.. doxygenfile:: emp/math/combos.hpp - :project: Empirical - :no-link: -``` - -## Constants - -```{eval-rst} -.. doxygenfile:: emp/math/constants.hpp - :project: Empirical - :no-link: -``` - -## Information Theory Tools - -```{eval-rst} -.. doxygenfile:: emp/math/info_theory.hpp - :project: Empirical - :no-link: -``` - -## Math - -```{eval-rst} -.. doxygenfile:: emp/math/math.hpp - :project: Empirical - :no-link: -``` - -## Randomness Utilites +.. toctree:: + :glob: -```{eval-rst} -.. doxygenfile:: emp/math/random_utils.hpp - :project: Empirical - :no-link: -``` - -## Random Number Generator - -```{eval-rst} -.. doxygenfile:: emp/math/Random.hpp - :project: Empirical - :no-link: -``` - -## Range - -```{eval-rst} -.. doxygenfile:: emp/math/Range.hpp - :project: Empirical - :no-link: -``` - -## Sequence Utilities - -```{eval-rst} -.. doxygenfile:: emp/math/sequence_utils.hpp - :project: Empirical - :no-link: -``` - -## Statistics Tools - -```{eval-rst} -.. doxygenfile:: emp/math/stats.hpp - :project: Empirical - :no-link: + api/* ``` diff --git a/doc/library/prefab/prefab.rst b/doc/library/prefab/prefab.rst deleted file mode 100644 index 554da916c1..0000000000 --- a/doc/library/prefab/prefab.rst +++ /dev/null @@ -1,355 +0,0 @@ -Prefabricated Web Tools (for use with Emscripten) -================================================= - -These prefabricated tools were created to help you quickly create interesting web applicications without being overwhelmed with the underlying HTML, CSS, and Bootstrap classes required. -These tools use Empirical's web tools to provide structure for the site, and many of the prefab tools inherit from web tools so you can add your own styling and stream them into other web components in a similar way. - -When using these prefab tools be sure to link to the Bootstrap library, jQuery, and the default style stylesheet for this class in the head section of your HTML file. -.. code-block:: html - - - - - - - - -You can view these tools in action `here `_. - -Card -~~~~ -The Card class allows you to define a Bootstrap style card into your project. -A card that is not collapsible will have its state set to :code:`STATIC`. -Cards are static by default. -A card can be collapsible if its state parameter it set to :code:`INIT_OPEN` or :code:`INIT_CLOSED`. -By default, if a card is collapsible, it will have toggle icons in the header, but this can be overridden by setting the :code:`showGlyphs` parameter to :code:`false`. - -Since this class inherits from :code:`web::Div`, you can set styling and attributes with :code:`SetCSS` and :code:`SetAttr` respectively. -You can also stream your Card into other web components with the :code:`<<` operator. - -Example: -******** -.. code-block:: c++ - - #include "web/web.h" - #include "prefab/Card.h" - - emp::web::Document doc("emp_base"); - - emp::prefab::Card my_card("STATIC"); - doc << my_card; - - my_card.AddHeaderContent("Title"); - my_card.AddBodyContent("Content for the card's body"); - // Web components can also be passed as parameters to AddHeaderContent and AddBodyContent - -**Note**: The toggle icons that are avalible for collapsible cards use the `FontAwesome`_ library. -You will need to add the CSS file for this library to the head of your HTML file: - -.. code-block:: html - - - -CodeBlock -~~~~~~~~~ -The CardBlock class provides an interface for the `HighlightJS Library`_ which allows you to display code on web pages with language specific highlighting. -You can find a list of `all languages`_ on their GitHub page. - -To use this class, you need to pass the code you want displayed and the programming language to the constructor. - -Since this class inherits from :code:`web::Element`, you can stream your CodeBlock into other web components with the :code:`<<` operator. - -Example: -******** -.. code-block:: c++ - - #include "web/web.h" - #include "prefab/CodeBlock.h" - - emp::web::Document doc("emp_base"); - - std::string code_str = - R"( - int num = 9; - std::cout << num << " is a square number" << std::endl; - )"; - emp::prefab::CodeBlock code_block(code_str, "c++"); - - doc << code_block; - -**Note**: You will also need to add the following code to the bottom of the body section of your HTML file: - -.. code-block:: html - - - - - - -.. _HighlightJS Library: https://highlightjs.org/ -.. _all languages: https://github.com/highlightjs/highlight.js/blob/master/SUPPORTED_LANGUAGES.md - -Collapse -~~~~~~~~ -The CollapseCouple maintains a group of targets and controllers. -When a controller is clicked on a web page, all the associated targets will change state (expand/collapse). - -By default, the target element will start off closed, but this can be set to open by passing :code:`true` for the :code:`expanded` parameter. - -Since the collapse controller and collapse target element will not necessarily directly neighbor eachother, call :code:`GetControllerDiv()` and :code:`GetTargetDiv()` to obtain a vector of all the asspociated controllers and targets, respectively. -To obtain just one controller or target, pass its index into a get div function call. - -Example: -******** -.. code-block:: cpp - - #include "web/web.h" - #include "web/Div.h" - #include "prefab/CommentBox.h" - - #include "prefab/Collapse.h" - - emp::web::Document doc("emp_base"); - - emp::prefab::CommentBox box1; - box1.AddContent("

Box 1

"); - emp::web::Div btn1; - btn1.SetAttr("class", "btn btn-info"); - btn1 << "Button 1: controls box 1"; - - emp::prefab::CollapseCoupling collapse1(btn1, box1, true); - - doc << collapse1.GetControllerDiv(0); - doc << collapse1.GetTargetDiv(0); - -CommentBox -~~~~~~~~~~ -A CommentBox is a simple grey comment bubble. -Content can be added to it using :code:`AddContent()`. -If there is data you only want to be visible on mobile devices, use :code:`AddMobileContent()`. - -Since this class inherits from :code:`web::Div`, you can set styling and attributes with :code:`SetCSS()` and :code:`SetAttr()` respectively. -You can also stream your CommentBox into other web components with the :code:`<<` operator. - -Example: -******** -.. code-block:: cpp - - #include "web/web.h" - #include "prefab/CommentBox.h" - - emp::web::Document doc("emp_base"); - - emp::prefab::CommentBox my_box; - doc << my_box; - - my_box.AddContent("

Content that shows on all screen sizes

"); - my_box.AddMobileContent("
Content that only shows on small screens"); - // Web components can also be passed as parameters to AddContent and AddMobileContent - -ConfigPanel -~~~~~~~~~~~ -The ConfigPanel allows developers to easily set up a user interface for their configuration options. -It allows web apps to be interactive and dynamic, allowing users to change configuration settings within the applicaiton and providing a better user experiance. - -Using the ConfigPanel class, a configuration panel is constructed when passed a Config file. -It uses other Prefabricated components to add styling and structure to the panel. -Use :code:`GetConfigPanelDiv()` to stream this component into another web component or document. - -It is important to note that ConfigPanel instances are destroyed when they go out of scope. -This causes the form to no longer respond to changes made by the user. -You will need to initialize an instance outside of :code:`main()` if you would like the user to be able to interact with the panel. - -Example: -******** -.. code-block:: cpp - - #include "web/web.h" - #include "prefab/ConfigPanel.h" - #include "config/ArgManager.h" - - #include "SampleConfig.h" // Config file - - emp::web::Document doc("emp_base"); - Config cfg; - - emp::prefab::ConfigPanel config_panel(cfg); - - // apply configuration query params and config files to Config - auto specs = emp::ArgManager::make_builtin_specs(&cfg); - emp::ArgManager am(emp::web::GetUrlParams(), specs); - // cfg.Read("config.cfg"); - am.UseCallbacks(); - if (am.HasUnused()) std::exit(EXIT_FAILURE); - - // setup configuration panel - config_panel.Setup(); - doc << config_panel.GetConfigPanelDiv(); - -FontAwesomeIcon -~~~~~~~~~~~~~~~ -`FontAwesome`_ is a free library of icons that can be used in web pages. - -To use this class: - -1. Find the icon you wish to use in the `FontAwesome library`_ -2. Pass :code:`"fa-" + *icon name*` as a parameter to the constructor. -3. Add the following CSS file to the head of your HTML document. - -.. code-block:: html - - - -Since this class inherits from :code:`web::Element`, you can set styling and attributes with :code:`SetCSS()` and :code:`SetAttr()` respectively. -You can also stream your FontAwesomeIcon into other web components with the :code:`<<` operator. - -Example: -******** -.. code-block:: cpp - - #include "web/web.h" - #include "prefab/FontAwesomeIcon.h" - - emp::web::Document doc("emp_base"); - - emp::prefab::FontAwesomeIcon my_icon("fa-paw"); - doc << my_icon; - - my_icon.AddClass("custom_class"); - -.. _FontAwesome: https://fontawesome.com/v4.7.0/ -.. _FontAwesome library: https://fontawesome.com/v4.7.0/icons/ - -LoadingIcon -~~~~~~~~~~~ -The LoadingIcon class is used to add an animated loading icon. -One possible use for this icon is to be displayed while the contents of a web page is loading. -The icon is provided by `FontAwesome`_, so you will need to add its CSS to your HTML file to use this class. - -.. code-block:: html - - - -Since this class inherits from :code:`web::Element`, you can set styling and attributes with :code:`SetCSS()` and :code:`SetAttr()` respectively. -You can also stream your LoadingIcon into other web components with the :code:`<<` operator. - -Example: -******** -.. code-block:: cpp - - #include "web/web.h" - #include "prefab/LoadingIcon.h" - - emp::web::Document doc("emp_base"); - - emp::prefab::LoadingIcon spinner; - doc << spinner; - -LoadingModal -~~~~~~~~~~~~ -The LoadingModal header file makes adding a loading modal to a web page easy. -It will appear while the content of the page is rendering and will disappear when the page has completed loading. - -This header file is slightly different from the other prefab web tools. -To place the loading modal on your web page, you must import the LoadingModal.js script into your HTML file right after the opening body tag. -To close the modal you must call the :code:`CloseLoadingModal()` function in your .cc file after loading the desired content into the doc. - -Example: -******** -.. code-block:: cpp - - // .cc file - #include "web/web.h" - #include "LoadingModal.h" - - emp::web::Document doc("emp_base"); - - // Add elements to the doc a normal - - emp::prefab::CloseLoadingModal(); - -.. code-block:: html - - - - - - - - - - - - - - - - - - - -Modal -~~~~~ -The Modal class can be used to create Bootstrap modals that pops up in the middle of the screen. - -Since this class inherits from :code:`web::Div`, you can stream your Modal into other web components with the :code:`<<` operator. -You can also set the background color of the Modal with :code:`SetBackground()` passing it a string with a color name or its hex code value. - -Example: -******** -.. code-block:: cpp - - #include "web/web.h" - #include "web/Button.h" - #include "prefab/Modal.h" - - emp::web::Document doc("emp_base"); - - emp::prefab::Modal modal; - doc << modal; - - modal.AddHeaderContent("

Modal Header Section

"); - modal.AddBodyContent("This is the content of the modal"); - - modal.AddFooterContent("Modal Footer Section"); - UI::Button close_btn([](){;}, "Close"); - close_btn.SetAttr("class", "btn btn-secondary"); - modal.AddFooterContent(close_btn); - modal.AddButton(close_btn); - - modal.AddClosingX(); - - UI::Button modal_btn([](){;}, "Show Modal"); - doc << modal_btn; - modal_btn.SetAttr("class", "btn btn-info"); - modal.AddButton(modal_btn); - -ToggleSwitch -~~~~~~~~~~~~ -The ToggleSwitch class wraps checkbox input with Bootstrap custom swtich classes. -If you need to add a CSS class to the Input, do it after the creating the ToggleSwitch instance with :code:`AddClass()`. - - -Since this class inherits from :code:`web::Element`, you can set styling and attributes with :code:`SetCSS()` and :code:`SetAttr()` respectively. -You can also stream your ToggleSwitch into other web components with the :code:`<<` operator. - -Example: -******** -.. code-block:: cpp - - #include "web/web.h" - #include "prefab/ToggleSwitch.h" - - emp::prefab::ToggleSwitch on_switch([](std::string val){}, "Switch Defult On", true, "user_defined_switch_id"); - doc << on_switch; - - doc << "
"; - - emp::prefab::ToggleSwitch off_switch([](std::string val){}, NULL, false); - doc << off_switch; - off_switch.AddLabel("Switch Defult Off"); - -Add the link to Bootstrap in the head of your HTML file: -.. code-block:: html - - diff --git a/doc/library/testing/testing.md b/doc/library/testing/testing.md index 6e1d18e9ad..1e0d9847b0 100644 --- a/doc/library/testing/testing.md +++ b/doc/library/testing/testing.md @@ -1,9 +1,12 @@ # Testing -## Unit Testing +## API + + ```{eval-rst} -.. doxygenfile:: emp/testing/unit_tests.hpp - :project: Empirical - :no-link: +.. toctree:: + :glob: + + api/* ``` diff --git a/doc/library/tools/tools.md b/doc/library/tools/tools.md index 9531d9a11e..fcf1c6eda0 100644 --- a/doc/library/tools/tools.md +++ b/doc/library/tools/tools.md @@ -1,33 +1,12 @@ # Other Tools -## Timing Functions +## API + + ```{eval-rst} -.. doxygenfile:: emp/tools/timing.hpp - :project: Empirical - :no-link: -``` - -## Branch and Bound Solution States - -```{eval-rst} -.. doxygenfile:: emp/tools/SolveState.hpp - :project: Empirical - :no-link: -``` - -## String Utilities +.. toctree:: + :glob: -```{eval-rst} -.. doxygenfile:: emp/tools/string_utils.hpp - :project: Empirical - :no-link: -``` - -## Type Tracker - -```{eval-rst} -.. doxygenfile:: emp/tools/TypeTracker.hpp - :project: Empirical - :no-link: + api/* ``` diff --git a/doc/library/web/d3/d3-intro.md b/doc/library/web/d3/d3-intro.md index 13fb8332c4..1c512ad20b 100644 --- a/doc/library/web/d3/d3-intro.md +++ b/doc/library/web/d3/d3-intro.md @@ -1,9 +1,8 @@ -Using Empirical\'s D3.js Wrapper -================================ +# Using Empirica's D3.js Wrapper -If you\'re writing scientific code that runs on the web, you\'ll +If you're writing scientific code that runs on the web, you'll probably want to visualize the results (either as your program runs or -after it\'s done). To make this as easy as possible, Empirical includes +after it's done). To make this as easy as possible, Empirical includes a C++ wrapper for d3.js, a wildly popular and powerful Javascript data visualization library. Using the wrapper, you can create visualizations directly from C++. @@ -19,8 +18,7 @@ pre-built graph objects in an effort to help those new to Javascript visualization get started fast. This is an ongoing process and we\'re always open to suggestions! -A Minimal Example -================= +## A Minimal Example D3 visualizations run in web browsers. That means that to use this wrapper, you need to compile your C++ code to Javascript, using @@ -38,8 +36,7 @@ easiest way to make a visualization is to use one of the pre-built visualizations. Later we\'ll get into writing your own. Either way, you\'ll need: -C++ File --------- +### C++ File The C++ file that you\'ll compile to Javascript. For this example, we\'ll use the Empirical web module to build the whole web page: @@ -94,8 +91,7 @@ emcc my_program.cc -o my_program.js \ #.js extension tells Emscripten to compile #once you're done debugging) ``` -HTML File ---------- +### HTML File To tell the browser what to do with your Javascript, you need an html file: @@ -135,8 +131,7 @@ file: ``` -CSS File --------- +### CSS File Optionally, a CSS file can be used to make elements look the way you want them to. Here\'s one that includes the necessary styles to make @@ -174,8 +169,7 @@ style\_sheet.css, and is in the same directory as the html file): } ``` -Running your visualization --------------------------- +### Running your visualization Now to open up the page in a browser! Some browsers will let you open the page up directly, but some will complain about the fact that you\'re @@ -199,30 +193,29 @@ python -m http.server ![Using SimpleHTTPServer with Python3](../../images/python3HTTPserver.png){.align-center} You can now open a browser to the server (, -replacing 8000 with whatever number was after \"port\" in the output +replacing 8000 with whatever number was after "port" in the output from the command). You should see a list of file names in the directory your terminal was open to when you ran the HTTP Server command (unless -you happen to have a file named index.html, in which case you\'ll see +you happen to have a file named index.html, in which case you'll see the contents of that file). Assuming you ran this command from the -\"example\" directory in the directory structure shown above, you should +"example" directory in the directory structure shown above, you should see \"my\_html.html\" (or whatever you called your html file) on the list. Click on it. -Ta-da! There\'s your visualization. +Ta-da! There's your visualization. -It\'s convenient to have a visualization of data you\'ve already +It's convenient to have a visualization of data you've already generated, but the real power of D3 visualization objects is that they -can update in real time while your code runs. Here\'s an example C++ +can update in real time while your code runs. Here's an example C++ file that does that: > Example here -So that\'s how you use out-of-the-box D3 visualizations in Empirical. +So that's how you use out-of-the-box D3 visualizations in Empirical. Sometimes, though, you want to do something new and exciting. Which -brings us to the next section\... +brings us to the next section... -Writing Your Own Visualization -============================== +## Writing Your Own Visualization To build your own visualization, you need to understand a bit about how D3 works. Which means you need to understand a bit about how Javascript @@ -262,8 +255,8 @@ D3 C++ wrapper, you\'re doing the same thing, but from C++. Let\'s take a tour of the main components of D3: -Selections ----------- +### Selections + `Selections `{.interpreted-text role="ref"} are a way to work with groups of DOM elements. For instance, @@ -343,8 +336,7 @@ D3::Selection graph_circles = svg.SelectAll("circle"); Advanced note: You can also make selections based on classes with [D3::Select(.classname)]{.title-ref}. -Binding Data ------------- +### Binding Data In D3, you bind data to selections. Usually, you are binding that data because you to visualize it with SVG elements. So, usually the selection @@ -541,8 +533,7 @@ update.EnterAppend("circle") update.ExitRemove(); ``` -Changing Elements\' Traits --------------------------- +### Changing Elements\' Traits There are three types of traits that a DOM element might have: attributes, styles, and properties. For the most part, attributes are @@ -643,8 +634,7 @@ also select transitions, allowing you to choose to have their effects be animated, rather than occuring instantaneously (which can look choppy in many visualizations). -Scales and Axes ---------------- +### Scales and Axes Usually your data is not in units that you can directly draw on the screen. For instance, if you want to plot a variable on the Y axis that @@ -832,8 +822,7 @@ emp::JSWrap(times_two, "times_two"); s.SetAttr("r", "times_two"); ``` -- Advanced users may also wish to write functions directly in - Javascript, which is possible using Emscripten\'s macros. +- Advanced users may also wish to write functions directly in Javascript, which is possible using Emscripten's macros. ``` cpp // Put the function in global scope by adding it @@ -876,8 +865,7 @@ int get_x(JSONData d, int i, int j) {return d.x();}; s.SetAttr("cx", get_x); ``` -Under the Hood (for the curious, developers, and people trying to do weird stuff) -================================================================================= +## Under the Hood (for the curious, developers, and people trying to do weird stuff) For the most part, Empirical\'s d3 wrapper isn\'t that complicated under the hood. All C++ objects in the d3 module have a unique integer id. @@ -907,17 +895,7 @@ have not been conducted. Things to watch out for: -- D3 object creation order - be careful of the order your constructors - for d3 objects get called in. It\'s hard to make this happen, but if - you\'re constructing objects in the constructors for other objects, - it\'s possible for the ids to get mixed up. -- Errors in Javascript usually won\'t show up on compilation - you - need to actually run the code. -- Main is a function that gets run like any other. When main finishes - running, its local variables will go out of scope. This means that - everything needed for an ongoing animation needs to live in global - scope. -- Javascript is designed to work asynchronously in a lot of contexts - (especially when loading outside resources or updating the graphics - on the screen). This can change the way you need to structure your - code. +- D3 object creation order - be careful of the order your constructors for d3 objects get called in. It's hard to make this happen, but if you're constructing objects in the constructors for other objects, it's possible for the ids to get mixed up. +- Errors in Javascript usually won't show up on compilation - you need to actually run the code. +- Main is a function that gets run like any other. When main finishes running, its local variables will go out of scope. This means that everything needed for an ongoing animation needs to live in global scope. +- Javascript is designed to work asynchronously in a lot of contexts (especially when loading outside resources or updating the graphics on the screen). This can change the way you need to structure your code. diff --git a/doc/library/web/web.md b/doc/library/web/web.md index 8648f435fe..aafe7736c9 100644 --- a/doc/library/web/web.md +++ b/doc/library/web/web.md @@ -6,240 +6,13 @@ d3/d3 ``` -## Animations +## API + + ```{eval-rst} -.. doxygenfile:: emp/web/Animate.hpp - :project: Empirical - :no-link: -``` - -## Attributes - -```{eval-rst} -.. doxygenfile:: emp/web/Attributes.hpp - :project: Empirical - :no-link: -``` - -## Buttons - -```{eval-rst} -.. doxygenfile:: emp/web/Button.hpp - :project: Empirical - :no-link: -``` - -## Canvas - -```{eval-rst} -.. doxygenfile:: emp/web/Canvas.hpp - :project: Empirical - :no-link: -``` - -## Canvas Utilities - -```{eval-rst} -.. doxygenfile:: emp/web/canvas_utils.hpp - :project: Empirical - :no-link: -``` - -## Canvas Actions - -```{eval-rst} -.. doxygenfile:: emp/web/CanvasAction.hpp - :project: Empirical - :no-link: -``` - -## Canvas Shapes - -```{eval-rst} -.. doxygenfile:: emp/web/CanvasShape.hpp - :project: Empirical - :no-link: -``` - -## Color maps - -```{eval-rst} -.. doxygenfile:: emp/web/color_map.hpp - :project: Empirical - :no-link: -``` - -## Commands - -```{eval-rst} -.. doxygenfile:: emp/web/commands.hpp - :project: Empirical - :no-link: -``` - -## Divs - -```{eval-rst} -.. doxygenfile:: emp/web/Div.hpp - :project: Empirical - :no-link: -``` - -## Documents - -```{eval-rst} -.. doxygenfile:: emp/web/Document.hpp - :project: Empirical - :no-link: -``` - -## Useful functions for emscripten - -```{eval-rst} -.. doxygenfile:: emp/web/emfunctions.hpp - :project: Empirical - :no-link: -``` - -## Event Handling - -```{eval-rst} -.. doxygenfile:: emp/web/events.hpp - :project: Empirical - :no-link: -``` - -## File Input - -```{eval-rst} -.. doxygenfile:: emp/web/FileInput.hpp - :project: Empirical - :no-link: -``` - -## Font - -```{eval-rst} -.. doxygenfile:: emp/web/Font.hpp - :project: Empirical - :no-link: -``` +.. toctree:: + :glob: -## Images - -```{eval-rst} -.. doxygenfile:: emp/web/Image.hpp - :project: Empirical - :no-link: -``` - -## Initialization - -```{eval-rst} -.. doxygenfile:: emp/web/init.hpp - :project: Empirical - :no-link: -``` - -## Javascript Utilities - -```{eval-rst} -.. doxygenfile:: emp/web/js_utils.hpp - :project: Empirical - :no-link: -``` - -## JSWrap - -```{eval-rst} -.. doxygenfile:: emp/web/JSWrap.hpp - :project: Empirical - :no-link: -``` - -## Keypress Manager - -```{eval-rst} -.. doxygenfile:: emp/web/KeypressManager.hpp - :project: Empirical - :no-link: -``` - -## Listeners - -```{eval-rst} -.. doxygenfile:: emp/web/Listeners.hpp - :project: Empirical - :no-link: -``` - -## Raw Image - -```{eval-rst} -.. doxygenfile:: emp/web/RawImage.hpp - :project: Empirical - :no-link: -``` - -## Selector - -```{eval-rst} -.. doxygenfile:: emp/web/Selector.hpp - :project: Empirical - :no-link: -``` - -## Styles - -```{eval-rst} -.. doxygenfile:: emp/web/Style.hpp - :project: Empirical - :no-link: -``` - -## Tables - -```{eval-rst} -.. doxygenfile:: emp/web/Table.hpp - :project: Empirical - :no-link: -``` - -## Text - -```{eval-rst} -.. doxygenfile:: emp/web/Text.hpp - :project: Empirical - :no-link: -``` - -## Text Areas - -```{eval-rst} -.. doxygenfile:: emp/web/TextArea.hpp - :project: Empirical - :no-link: -``` - -## Tweens - -```{eval-rst} -.. doxygenfile:: emp/web/Tween.hpp - :project: Empirical - :no-link: -``` - -## Widgets - -```{eval-rst} -.. emp/web/Widget.hpp - :project: Empirical - :no-link: -``` - -```{eval-rst} -.. doxygenfile:: emp/web/WidgetExtras.hpp - :project: Empirical - :no-link: + api/* ``` diff --git a/doc/make_md.py b/doc/make_md.py new file mode 100755 index 0000000000..bb7fe3f466 --- /dev/null +++ b/doc/make_md.py @@ -0,0 +1,70 @@ +#!/usr/bin/python + +import glob +import os + +# This script performs preprocessing for the Empirical documentation +# build process, manually filling in the folder tree `library` with stub +# markdown files for the documentation of Empirical header files to be built +# onto. +# +# The script scans the "../include/emp" directory for all ".hpp" files (with +# several explicit exclusions). For each header `emp/subname/filename.hpp` +# it creates a stub `library/subname/api/file.md`. Note that the `library` +# folder already contains skeleton content (i.e., stub pages for each of +# Emprical's subdirectories. Note also that some Empirical subdirectories +# not intended for external use are explicitly excluded. + +def format_directive(filename): + """Create the breathe directive and add the options.""" + directive = f"```{{eval-rst}}\n.. doxygenfile:: {filename}\n :project: Empirical\n```\n" + return directive + + +def format_heading(level, text): + """Create a heading of [1, 2 or 3 supported].""" + symbol = ["# ", "## ", "### ", "#### "][ + level - 1 + ] + return symbol + text + "\n\n" + +# Ensure execution inside of doc/ +script_directory = os.path.dirname(os.path.abspath(__file__)) +os.chdir(script_directory) + +header_files = glob.glob("../include/emp/**/*.hpp", recursive=True) + +for h in header_files: + print(h) + h = h.removeprefix("../include/emp/") + just_dir = "/".join(h.split("/")[:-1]).lower() + last_dir = just_dir.split("/")[-1] + just_file = h.split("/")[-1] + + excluded_prefixes = [ + "_", + "in_progress", + "polyfill", + ] + if any(just_file.startswith(prefix) for prefix in excluded_prefixes): + continue + new_dir = "library/" + just_dir + "/api" + print(new_dir) + toc_text = "\n\n" + format_heading(2, "API") + "```{eval-rst}\n.. toctree::\n :glob:\n\n api/*\n```\n" + os.makedirs(new_dir, exist_ok=True) + toc_file_name = f"library/{just_dir}/{last_dir}.md" + if not os.path.exists(toc_file_name): + toc_text = format_heading(1, last_dir.capitalize()) + toc_text + with open(toc_file_name, "w") as target: + target.write(toc_text) + elif "" not in open(toc_file_name).read(): + with open(toc_file_name, "a") as target: + target.write("\n" + toc_text) + + text = format_heading(1, f"{just_file}") + text += format_directive("emp/" + h) + + filename = just_file.removesuffix(".hpp") + ".md" + + with open(new_dir + "/" + filename, "w") as target: + target.write(text) diff --git a/doc/requirements.in b/doc/requirements.in index af341d1323..f45eb3d066 100644 --- a/doc/requirements.in +++ b/doc/requirements.in @@ -1,10 +1,7 @@ -sphinx==3.2.1 -exhale==0.2.3 -sphinx-rtd-theme==0.5.0 -coverxygen==1.5.0 -breathe==4.22.1 -myst-parser==0.12.9 -# @mmore500 2021-10 -# docutils 0.18.0 crashes docs build due to exception -# AttributeError: 'Values' object has no attribute 'section_self_link' -docutils==0.17.1 +sphinx==7.2.6 +sphinx-rtd-theme==2.0.0 +coverxygen==1.8.1 +breathe==4.35.0 +myst-parser==2.0.0 +sphinx-tippy==0.4.1 +sphinxcontrib-bibtex==2.6.1 diff --git a/doc/requirements.txt b/doc/requirements.txt index ba5ca8f558..8293d91324 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,94 +1,114 @@ # -# This file is autogenerated by pip-compile -# To update, run: +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: # -# pip-compile requirements.in +# pip-compile doc/requirements.in # -alabaster==0.7.12 +alabaster==0.7.13 # via sphinx -attrs==20.3.0 - # via markdown-it-py -babel==2.9.1 +babel==2.13.1 # via sphinx -beautifulsoup4==4.10.0 - # via bs4 -breathe==4.22.1 - # via - # -r requirements.in - # exhale -bs4==0.0.1 - # via exhale -certifi==2021.10.8 +beautifulsoup4==4.12.2 + # via sphinx-tippy +breathe==4.35.0 + # via -r /tmp/requirements.in +certifi==2023.11.17 # via requests -charset-normalizer==2.0.7 +charset-normalizer==3.3.2 # via requests -coverxygen==1.5.0 - # via -r requirements.in -docutils==0.17.1 +coverxygen==1.8.1 + # via -r /tmp/requirements.in +docutils==0.20.1 # via - # -r requirements.in + # -r /tmp/requirements.in # breathe # myst-parser + # pybtex-docutils # sphinx -exhale==0.2.3 - # via -r requirements.in -idna==3.3 + # sphinx-rtd-theme + # sphinxcontrib-bibtex +idna==3.6 # via requests -imagesize==1.2.0 +imagesize==1.4.1 # via sphinx -jinja2==3.0.2 - # via sphinx -lxml==4.6.3 - # via exhale -markdown-it-py==0.5.8 - # via myst-parser -markupsafe==2.0.1 +jinja2==3.1.2 + # via + # myst-parser + # sphinx + # sphinx-tippy +latexcodec==2.0.1 + # via pybtex +markdown-it-py==3.0.0 + # via + # mdit-py-plugins + # myst-parser +markupsafe==2.1.3 # via jinja2 -myst-parser==0.12.9 - # via -r requirements.in -packaging==21.0 - # via sphinx -pygments==2.10.0 - # via sphinx -pyparsing==3.0.2 - # via packaging -pytz==2021.3 - # via babel -pyyaml==6.0 +mdit-py-plugins==0.4.0 # via myst-parser -requests==2.26.0 +mdurl==0.1.2 + # via markdown-it-py +myst-parser==2.0.0 + # via -r /tmp/requirements.in +packaging==23.2 + # via sphinx +pybtex==0.24.0 + # via + # pybtex-docutils + # sphinxcontrib-bibtex +pybtex-docutils==1.0.3 + # via sphinxcontrib-bibtex +pygments==2.17.2 # via sphinx +pyyaml==6.0.1 + # via + # myst-parser + # pybtex +requests==2.31.0 + # via + # sphinx + # sphinx-tippy six==1.16.0 # via - # breathe - # exhale -snowballstemmer==2.1.0 + # latexcodec + # pybtex +snowballstemmer==2.2.0 # via sphinx -soupsieve==2.2.1 +soupsieve==2.5 # via beautifulsoup4 -sphinx-rtd-theme==0.5.0 - # via -r requirements.in -sphinx==3.2.1 +sphinx==7.2.6 # via - # -r requirements.in + # -r /tmp/requirements.in # breathe - # exhale # myst-parser # sphinx-rtd-theme -sphinxcontrib-applehelp==1.0.2 + # sphinx-tippy + # sphinxcontrib-applehelp + # sphinxcontrib-bibtex + # sphinxcontrib-devhelp + # sphinxcontrib-htmlhelp + # sphinxcontrib-jquery + # sphinxcontrib-qthelp + # sphinxcontrib-serializinghtml +sphinx-rtd-theme==2.0.0 + # via -r /tmp/requirements.in +sphinx-tippy==0.4.1 + # via -r /tmp/requirements.in +sphinxcontrib-applehelp==1.0.7 # via sphinx -sphinxcontrib-devhelp==1.0.2 +sphinxcontrib-bibtex==2.6.1 + # via -r /tmp/requirements.in +sphinxcontrib-devhelp==1.0.5 # via sphinx -sphinxcontrib-htmlhelp==2.0.0 +sphinxcontrib-htmlhelp==2.0.4 # via sphinx +sphinxcontrib-jquery==4.1 + # via sphinx-rtd-theme sphinxcontrib-jsmath==1.0.1 # via sphinx -sphinxcontrib-qthelp==1.0.3 +sphinxcontrib-qthelp==1.0.6 # via sphinx -sphinxcontrib-serializinghtml==1.1.5 +sphinxcontrib-serializinghtml==1.1.9 # via sphinx -urllib3==1.26.7 +urllib3==2.1.0 # via requests - -# The following packages are considered to be unsafe in a requirements file: -# setuptools diff --git a/examples/Evolve/AvidaGP-Evo.cpp b/examples/Evolve/AvidaGP-Evo.cpp index 67146dbeb0..c5bfcdb3f5 100644 --- a/examples/Evolve/AvidaGP-Evo.cpp +++ b/examples/Evolve/AvidaGP-Evo.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file AvidaGP-Evo.cpp + * @file * @brief A test of AvidaGP with World; organisms must find squares of values. */ diff --git a/examples/Evolve/AvidaGP-Mancala.cpp b/examples/Evolve/AvidaGP-Mancala.cpp index bdd749eeaf..4f2202b8d7 100644 --- a/examples/Evolve/AvidaGP-Mancala.cpp +++ b/examples/Evolve/AvidaGP-Mancala.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file AvidaGP-Mancala.cpp + * @file */ #include diff --git a/examples/Evolve/AvidaGP-Resource.cpp b/examples/Evolve/AvidaGP-Resource.cpp index 9ab13d70eb..8aeb6fdb89 100644 --- a/examples/Evolve/AvidaGP-Resource.cpp +++ b/examples/Evolve/AvidaGP-Resource.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file AvidaGP-Resource.cpp + * @file * @brief A test of AvidaGP with World; organisms must find squares of values. */ diff --git a/examples/Evolve/AvidaGP-StateGrid.cpp b/examples/Evolve/AvidaGP-StateGrid.cpp index 47d1c0afc8..7f0488ad6f 100644 --- a/examples/Evolve/AvidaGP-StateGrid.cpp +++ b/examples/Evolve/AvidaGP-StateGrid.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file AvidaGP-StateGrid.cpp + * @file * @brief A example of using AvidaGP evolving with a StateGrid. * * Example file of AvidaGP-based organisms (called SGOrg here) moving through a state grid, diff --git a/examples/Evolve/AvidaGP-Test.cpp b/examples/Evolve/AvidaGP-Test.cpp index 9a2b46125a..4d411fc9e8 100644 --- a/examples/Evolve/AvidaGP-Test.cpp +++ b/examples/Evolve/AvidaGP-Test.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file AvidaGP-Test.cpp + * @file * @brief A simple test of AvidaGP with World for copies and mutations. * * A few basic steps to examine AvidaGP organisms in a world. diff --git a/examples/Evolve/DiagnosticNiches.cpp b/examples/Evolve/DiagnosticNiches.cpp index 3857306c26..40aa46cfdf 100644 --- a/examples/Evolve/DiagnosticNiches.cpp +++ b/examples/Evolve/DiagnosticNiches.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file DiagnosticNiches.cpp + * @file */ #include diff --git a/examples/Evolve/EvoSorter.cpp b/examples/Evolve/EvoSorter.cpp index 44c1d2c5a7..127e0b219e 100644 --- a/examples/Evolve/EvoSorter.cpp +++ b/examples/Evolve/EvoSorter.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file EvoSorter.cpp + * @file * @brief This file explores the evolving BitSorter sorting networks. */ diff --git a/examples/Evolve/Fitness_Share_NK.cpp b/examples/Evolve/Fitness_Share_NK.cpp index d3999fc939..cf0e2ad52c 100644 --- a/examples/Evolve/Fitness_Share_NK.cpp +++ b/examples/Evolve/Fitness_Share_NK.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file Fitness_Share_NK.cpp + * @file * @brief This file uses the Fitness Sharing functionality defined in evo::World.hpp */ diff --git a/examples/Evolve/Grid.cpp b/examples/Evolve/Grid.cpp index 2f3112c163..e797e787c4 100644 --- a/examples/Evolve/Grid.cpp +++ b/examples/Evolve/Grid.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file Grid.cpp + * @file * @brief This file explores the grid options for emp::World.hpp */ diff --git a/examples/Evolve/MAP-Elites.cpp b/examples/Evolve/MAP-Elites.cpp index 8fad578d7d..725d839ea3 100644 --- a/examples/Evolve/MAP-Elites.cpp +++ b/examples/Evolve/MAP-Elites.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file MAP-Elites.cpp + * @file * @brief This file explores the MAP-Elites selection scheme. * * In this example, we will be evolving 4-digit integers. diff --git a/examples/Evolve/Makefile b/examples/Evolve/Makefile index ffda44e599..79e9682ea1 100644 --- a/examples/Evolve/Makefile +++ b/examples/Evolve/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/Evolve/NK.cpp b/examples/Evolve/NK.cpp index e6572d8ddd..f5f00ebc94 100644 --- a/examples/Evolve/NK.cpp +++ b/examples/Evolve/NK.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file NK.cpp + * @file * @brief This file explores the template defined in evo::Population.h with an NK landscape. */ diff --git a/examples/Evolve/Pools.cpp b/examples/Evolve/Pools.cpp index 00b67665a8..aa9b5156e1 100644 --- a/examples/Evolve/Pools.cpp +++ b/examples/Evolve/Pools.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Pools.cpp + * @file * @brief This file explores the pool options for emp::World.h */ diff --git a/examples/Evolve/Roulette.cpp b/examples/Evolve/Roulette.cpp index f92bf1d539..f660038c52 100644 --- a/examples/Evolve/Roulette.cpp +++ b/examples/Evolve/Roulette.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file Roulette.cpp + * @file * @brief This file explores the RouletteSelect() function */ diff --git a/examples/Evolve/ShrinkPop.cpp b/examples/Evolve/ShrinkPop.cpp index 34a28eca44..c6b98e36c8 100644 --- a/examples/Evolve/ShrinkPop.cpp +++ b/examples/Evolve/ShrinkPop.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file ShrinkPop.cpp + * @file * @brief This file explores the grid options for emp::World.hpp */ @@ -16,7 +17,7 @@ int main() { constexpr size_t POP_SIZE = 3600; - constexpr size_t GENS = 10000; + // constexpr size_t GENS = 10000; const size_t POP_SIDE = (size_t) std::sqrt(POP_SIZE); emp::Random random; diff --git a/examples/Evolve/Systematics.cpp b/examples/Evolve/Systematics.cpp index 0fa8bccac3..c0c981c498 100644 --- a/examples/Evolve/Systematics.cpp +++ b/examples/Evolve/Systematics.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file Systematics.cpp + * @file * @brief This file explores the grid options for emp::World.hpp */ diff --git a/examples/Evolve/World.cpp b/examples/Evolve/World.cpp index e6454bf79f..4ec0e665b7 100644 --- a/examples/Evolve/World.cpp +++ b/examples/Evolve/World.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file World.cpp + * @file * @brief This file is an example for using the re-vamped World template. */ diff --git a/examples/Evolve/World2.cpp b/examples/Evolve/World2.cpp index 0b1fd4704d..a544c63839 100644 --- a/examples/Evolve/World2.cpp +++ b/examples/Evolve/World2.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file World2.cpp + * @file * @brief This file is an example for using the re-vamped World template. */ diff --git a/examples/OLD/Empower/Empower.cpp b/examples/OLD/Empower/Empower.cpp index 8c16e395d8..39259a77b9 100644 --- a/examples/OLD/Empower/Empower.cpp +++ b/examples/OLD/Empower/Empower.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Empower.cpp + * @file * @brief Some examples code for using emp::Empower */ diff --git a/examples/OLD/Empower/Makefile b/examples/OLD/Empower/Makefile index afbb1ca155..949dc971ef 100644 --- a/examples/OLD/Empower/Makefile +++ b/examples/OLD/Empower/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/OLD/Empower/Struct.cpp b/examples/OLD/Empower/Struct.cpp index ffbbbe2f4e..d1ad900127 100644 --- a/examples/OLD/Empower/Struct.cpp +++ b/examples/OLD/Empower/Struct.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Struct.cpp + * @file * @brief Some examples code for using emp::Struct */ diff --git a/examples/OLD/Empower/Type.cpp b/examples/OLD/Empower/Type.cpp index aa0adf7449..1538c36a42 100644 --- a/examples/OLD/Empower/Type.cpp +++ b/examples/OLD/Empower/Type.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file Type.cpp + * @file * @brief Some examples code for using emp::Type */ diff --git a/examples/OLD/Empower/TypeManager.cpp b/examples/OLD/Empower/TypeManager.cpp index db70574fcf..91d021b5a9 100644 --- a/examples/OLD/Empower/TypeManager.cpp +++ b/examples/OLD/Empower/TypeManager.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file TypeManager.cpp + * @file * @brief Some examples code for using emp::TypeManager */ diff --git a/examples/OLD/Random14.cpp b/examples/OLD/Random14.cpp index 18f901bad5..3e3fb85a31 100644 --- a/examples/OLD/Random14.cpp +++ b/examples/OLD/Random14.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file Random14.cpp + * @file * @brief Some examples code for using emp::Random */ diff --git a/examples/OLD/hardware/bak/AvidaGP-AltInst.cpp b/examples/OLD/hardware/bak/AvidaGP-AltInst.cpp index c5c1afb92c..eeddb71180 100644 --- a/examples/OLD/hardware/bak/AvidaGP-AltInst.cpp +++ b/examples/OLD/hardware/bak/AvidaGP-AltInst.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file AvidaGP-AltInst.cpp + * @file */ #include diff --git a/examples/OLD/hardware/bak/AvidaGP-Evo.cpp b/examples/OLD/hardware/bak/AvidaGP-Evo.cpp index a6b424034e..f21da32f28 100644 --- a/examples/OLD/hardware/bak/AvidaGP-Evo.cpp +++ b/examples/OLD/hardware/bak/AvidaGP-Evo.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file AvidaGP-Evo.cpp + * @file */ #include diff --git a/examples/OLD/hardware/bak/AvidaGP-Interpreter.cpp b/examples/OLD/hardware/bak/AvidaGP-Interpreter.cpp index e01b02a54b..d168483d02 100644 --- a/examples/OLD/hardware/bak/AvidaGP-Interpreter.cpp +++ b/examples/OLD/hardware/bak/AvidaGP-Interpreter.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file AvidaGP-Interpreter.cpp + * @file */ #include diff --git a/examples/OLD/hardware/bak/AvidaGP-Mancala-EcoEA.cpp b/examples/OLD/hardware/bak/AvidaGP-Mancala-EcoEA.cpp index 5dde29fe1c..ba22b74ef4 100644 --- a/examples/OLD/hardware/bak/AvidaGP-Mancala-EcoEA.cpp +++ b/examples/OLD/hardware/bak/AvidaGP-Mancala-EcoEA.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file AvidaGP-Mancala-EcoEA.cpp + * @file */ #include diff --git a/examples/OLD/hardware/bak/AvidaGP-Mancala.cpp b/examples/OLD/hardware/bak/AvidaGP-Mancala.cpp index ce7ec274bd..e3fd3ee63d 100644 --- a/examples/OLD/hardware/bak/AvidaGP-Mancala.cpp +++ b/examples/OLD/hardware/bak/AvidaGP-Mancala.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file AvidaGP-Mancala.cpp + * @file */ #include diff --git a/examples/OLD/hardware/bak/AvidaGP-Sorting.cpp b/examples/OLD/hardware/bak/AvidaGP-Sorting.cpp index 8ffec975ea..f81e8be9dc 100644 --- a/examples/OLD/hardware/bak/AvidaGP-Sorting.cpp +++ b/examples/OLD/hardware/bak/AvidaGP-Sorting.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file AvidaGP-Sorting.cpp + * @file * @brief This is an example file, evolving AvidaGP organisms to sort numbers. */ diff --git a/examples/OLD/hardware/bak/EventDrivenGP-ChgEnv.cpp b/examples/OLD/hardware/bak/EventDrivenGP-ChgEnv.cpp index 61b8216b43..af194778cc 100644 --- a/examples/OLD/hardware/bak/EventDrivenGP-ChgEnv.cpp +++ b/examples/OLD/hardware/bak/EventDrivenGP-ChgEnv.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file EventDrivenGP-ChgEnv.cpp + * @file */ // ------- DEPRECATED ------- diff --git a/examples/OLD/hardware/bak/EventDrivenGP-Roles.cpp b/examples/OLD/hardware/bak/EventDrivenGP-Roles.cpp index 1845b767cc..6d6848d517 100644 --- a/examples/OLD/hardware/bak/EventDrivenGP-Roles.cpp +++ b/examples/OLD/hardware/bak/EventDrivenGP-Roles.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file EventDrivenGP-Roles.cpp + * @file */ // ------- DEPRECATED ------- diff --git a/examples/OLD/hardware/bak/EventDrivenGP.cpp b/examples/OLD/hardware/bak/EventDrivenGP.cpp index b8a5e832cc..3223d4935f 100644 --- a/examples/OLD/hardware/bak/EventDrivenGP.cpp +++ b/examples/OLD/hardware/bak/EventDrivenGP.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file EventDrivenGP.cpp + * @file */ // ------- DEPRECATED ------- diff --git a/examples/OLD/hardware/bak/Play-Mancala.cpp b/examples/OLD/hardware/bak/Play-Mancala.cpp index eed77e90c3..4631ca8cde 100644 --- a/examples/OLD/hardware/bak/Play-Mancala.cpp +++ b/examples/OLD/hardware/bak/Play-Mancala.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file Play-Mancala.cpp + * @file */ #include diff --git a/examples/OLD/hardware/bak/RunTournament.cpp b/examples/OLD/hardware/bak/RunTournament.cpp index 582be8e972..8f0040d3f4 100644 --- a/examples/OLD/hardware/bak/RunTournament.cpp +++ b/examples/OLD/hardware/bak/RunTournament.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file RunTournament.cpp + * @file */ #include diff --git a/examples/ProjectTemplate/Makefile b/examples/ProjectTemplate/Makefile index dc041579d5..7ff537c25f 100644 --- a/examples/ProjectTemplate/Makefile +++ b/examples/ProjectTemplate/Makefile @@ -3,7 +3,7 @@ PROJECT := project_name EMP_DIR := ../../../Empirical/include # Flags to use regardless of compiler -CFLAGS_all := -Wall -Wno-unused-function -std=c++17 -I$(EMP_DIR)/ +CFLAGS_all := -Wall -Wno-unused-function -std=c++20 -I$(EMP_DIR)/ # Native compiler information CXX_nat := g++ diff --git a/examples/ProjectTemplate/source/native/project_name.cpp b/examples/ProjectTemplate/source/native/project_name.cpp index e09d1f96c5..40ad9bb919 100644 --- a/examples/ProjectTemplate/source/native/project_name.cpp +++ b/examples/ProjectTemplate/source/native/project_name.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file project_name.cpp + * @file */ // This is the main function for the NATIVE version of this project. diff --git a/examples/ProjectTemplate/source/web/project_name-web.cpp b/examples/ProjectTemplate/source/web/project_name-web.cpp index 8d3a7b0e57..ecacd71221 100644 --- a/examples/ProjectTemplate/source/web/project_name-web.cpp +++ b/examples/ProjectTemplate/source/web/project_name-web.cpp @@ -1,10 +1,11 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file project_name-web.cpp - * + * @file + * @brief TODO. * WARNING: the Cookiecutter Empirical Project, which you can find at * https://github.com/devosoft/cookiecutter-empirical-project, should be * preferred over ProjectTemplate diff --git a/examples/TO_REPAIR/base/array.cpp b/examples/TO_REPAIR/base/array.cpp index b1a9aff2ee..dab7f1b6ee 100644 --- a/examples/TO_REPAIR/base/array.cpp +++ b/examples/TO_REPAIR/base/array.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file array.cpp + * @file */ #include "emp/base/array.hpp" diff --git a/examples/TO_REPAIR/config/SettingConfig.cpp b/examples/TO_REPAIR/config/SettingConfig.cpp index c8d88fb857..6d2dd0706f 100644 --- a/examples/TO_REPAIR/config/SettingConfig.cpp +++ b/examples/TO_REPAIR/config/SettingConfig.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file SettingConfig.cpp + * @file * @brief Some examples code for using emp::SettingConfig */ diff --git a/examples/TO_REPAIR/data/DataMap.cpp b/examples/TO_REPAIR/data/DataMap.cpp index 5dc14400ed..926d4ac92d 100644 --- a/examples/TO_REPAIR/data/DataMap.cpp +++ b/examples/TO_REPAIR/data/DataMap.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019-2020. - * - * @file DataMap.cpp + * @file * @brief An example file for using DataMaps */ diff --git a/examples/TO_REPAIR/games/Mancala.cpp b/examples/TO_REPAIR/games/Mancala.cpp index 99a7e141fe..a3379a6a2f 100644 --- a/examples/TO_REPAIR/games/Mancala.cpp +++ b/examples/TO_REPAIR/games/Mancala.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file Mancala.cpp + * @file * @brief This is an example file demonstrating Manacala. */ diff --git a/examples/TO_REPAIR/games/Othello.cpp b/examples/TO_REPAIR/games/Othello.cpp index ef2ef7cf39..b11738d83d 100644 --- a/examples/TO_REPAIR/games/Othello.cpp +++ b/examples/TO_REPAIR/games/Othello.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file Othello.cpp + * @file * @brief This is an example file demonstrating Othello. */ diff --git a/examples/TO_REPAIR/timing/short_strings.cpp b/examples/TO_REPAIR/timing/short_strings.cpp index 70e881bf54..92ed7ae92c 100644 --- a/examples/TO_REPAIR/timing/short_strings.cpp +++ b/examples/TO_REPAIR/timing/short_strings.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file short_strings.cpp + * @file * @brief Code comparing various methods of accessing entries in an unsorted_map. * * We are comparing the timings for accessing an unsorted map using: diff --git a/examples/base/Makefile b/examples/base/Makefile index 0540edf6de..9223056b51 100644 --- a/examples/base/Makefile +++ b/examples/base/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/base/Ptr.cpp b/examples/base/Ptr.cpp index 797ba6f111..d9a0bee5d1 100644 --- a/examples/base/Ptr.cpp +++ b/examples/base/Ptr.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file Ptr.cpp + * @file * @brief An example file for using emp::Ptr. */ diff --git a/examples/base/assert.cpp b/examples/base/assert.cpp index 44ab837b07..17ff725b69 100644 --- a/examples/base/assert.cpp +++ b/examples/base/assert.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020-2022 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file assert.cpp + * @file * @brief Some examples code demonstrating use of the error system. */ @@ -11,7 +12,7 @@ int main() { - int x{ 42 }; + [[maybe_unused]] int x{ 42 }; emp_assert(x > 1, "This assert passes in debug mode!", x); diff --git a/examples/base/errors.cpp b/examples/base/errors.cpp index f22623fc56..d88e01a0a0 100644 --- a/examples/base/errors.cpp +++ b/examples/base/errors.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file errors.cpp + * @file * @brief Some examples code demonstrating use of the error system. */ diff --git a/examples/base/map.cpp b/examples/base/map.cpp index 33e89a27ae..47907c08bc 100644 --- a/examples/base/map.cpp +++ b/examples/base/map.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019-2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019-2020 - * - * @file map.cpp + * @file */ #include diff --git a/examples/base/unordered_map.cpp b/examples/base/unordered_map.cpp index a5793065b1..8b618532ee 100644 --- a/examples/base/unordered_map.cpp +++ b/examples/base/unordered_map.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file unordered_map.cpp + * @file */ #include diff --git a/examples/base/vector.cpp b/examples/base/vector.cpp index 155bb9c10e..51de1ded9d 100644 --- a/examples/base/vector.cpp +++ b/examples/base/vector.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file vector.cpp + * @file */ #include diff --git a/examples/bits/BitSet.cpp b/examples/bits/BitSet.cpp index 1ead5b037c..6711e27b19 100644 --- a/examples/bits/BitSet.cpp +++ b/examples/bits/BitSet.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2018 - * - * @file BitSet.cpp + * @file * @brief Some example code for using emp::BitSet */ diff --git a/examples/bits/BitVector.cpp b/examples/bits/BitVector.cpp index d57d8c2db5..f9325c51d8 100644 --- a/examples/bits/BitVector.cpp +++ b/examples/bits/BitVector.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file BitVector.cpp + * @file * @brief Some example code for using emp::Ptr */ @@ -45,6 +46,7 @@ int main() auto set5 = set3 & set4; total += set5.CountOnes(); } + std::cout << "Total = " << total << std::endl; std::clock_t emp_tot_time = std::clock() - emp_start_time; double time = 1000.0 * ((double) emp_tot_time) / (double) CLOCKS_PER_SEC; diff --git a/examples/bits/Makefile b/examples/bits/Makefile index 2fecbac619..58396ca87e 100644 --- a/examples/bits/Makefile +++ b/examples/bits/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/compiler/DFA.cpp b/examples/compiler/DFA.cpp index 17c25a6d1d..04ff42284b 100644 --- a/examples/compiler/DFA.cpp +++ b/examples/compiler/DFA.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file DFA.cpp + * @file * @brief Some examples code for using emp::DFA */ diff --git a/examples/compiler/Lexer.cpp b/examples/compiler/Lexer.cpp index 15c577ae14..efcac45c27 100644 --- a/examples/compiler/Lexer.cpp +++ b/examples/compiler/Lexer.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file Lexer.cpp + * @file * @brief Some examples code for using emp::Lexer */ diff --git a/examples/compiler/Makefile b/examples/compiler/Makefile index 035b3dd1a6..5c03062b0f 100644 --- a/examples/compiler/Makefile +++ b/examples/compiler/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/compiler/NFA.cpp b/examples/compiler/NFA.cpp index e62a729998..abd27eed66 100644 --- a/examples/compiler/NFA.cpp +++ b/examples/compiler/NFA.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file NFA.cpp + * @file * @brief Some examples code for using emp::NFA */ diff --git a/examples/compiler/RegEx.cpp b/examples/compiler/RegEx.cpp index 8d7370b5f4..4822cb3bd3 100644 --- a/examples/compiler/RegEx.cpp +++ b/examples/compiler/RegEx.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file RegEx.cpp + * @file * @brief Some examples code for using emp::RegEx */ diff --git a/examples/compiler/lexer_utils.cpp b/examples/compiler/lexer_utils.cpp index 201cba3c33..800662778e 100644 --- a/examples/compiler/lexer_utils.cpp +++ b/examples/compiler/lexer_utils.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file lexer_utils.cpp + * @file * @brief Example code for converting string pattern representations. */ diff --git a/examples/config/Makefile b/examples/config/Makefile index 837c3a6f7b..534041b3a4 100644 --- a/examples/config/Makefile +++ b/examples/config/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/config/SettingCombos.cpp b/examples/config/SettingCombos.cpp index e5ad2cffcb..ef7e75a5bb 100644 --- a/examples/config/SettingCombos.cpp +++ b/examples/config/SettingCombos.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file SettingCombos.cpp + * @file * @brief Some examples code for using emp::SettingCombos */ diff --git a/examples/config/config.cpp b/examples/config/config.cpp index ec69f5af17..80e1295213 100644 --- a/examples/config/config.cpp +++ b/examples/config/config.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file config.cpp + * @file */ #include diff --git a/examples/config/config_setup.hpp b/examples/config/config_setup.hpp index 915f754a2d..16a1e5ffa9 100644 --- a/examples/config/config_setup.hpp +++ b/examples/config/config_setup.hpp @@ -1,11 +1,11 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file config_setup.hpp - * @brief This file provides an example of how to build a configuration class. - * + * @file + * @brief TODO. * To create a new config from scratch, the format is: * EMP_BUILD_CONFIG( CLASS_NAME, OPTIONS... ) * diff --git a/examples/config/namespaces.cpp b/examples/config/namespaces.cpp index 8a5826f9d6..04c9ff3a1f 100644 --- a/examples/config/namespaces.cpp +++ b/examples/config/namespaces.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file namespaces.cpp + * @file */ #include diff --git a/examples/config/test-macro.hpp b/examples/config/test-macro.hpp index 0f4e20a304..554c3b2251 100644 --- a/examples/config/test-macro.hpp +++ b/examples/config/test-macro.hpp @@ -1,11 +1,11 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file test-macro.hpp - * @brief This is an auto-generated file that defines a set of configuration options. - * To create a new config from scratch, the format is: + * @file + * @brief TODO. * EMP_BUILD_CONFIG( CLASS_NAME, OPTIONS... ) * * To extend an existing config, simply use: diff --git a/examples/control/Action.cpp b/examples/control/Action.cpp index c8d674403d..285094b385 100644 --- a/examples/control/Action.cpp +++ b/examples/control/Action.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file Action.cpp + * @file * @brief Some examples code for using emp::Action */ diff --git a/examples/control/ActionManager.cpp b/examples/control/ActionManager.cpp index 62cefe1d83..fd336a5526 100644 --- a/examples/control/ActionManager.cpp +++ b/examples/control/ActionManager.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file ActionManager.cpp + * @file * @brief Some examples code for using emp::ActionManager */ diff --git a/examples/control/Makefile b/examples/control/Makefile index f20b711572..ecdb48d9d2 100644 --- a/examples/control/Makefile +++ b/examples/control/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/control/Signal.cpp b/examples/control/Signal.cpp index d589616677..949c8de9e3 100644 --- a/examples/control/Signal.cpp +++ b/examples/control/Signal.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file Signal.cpp + * @file * @brief Some examples code for using emp::Signal */ diff --git a/examples/control/SignalControl.cpp b/examples/control/SignalControl.cpp index 3bd7e0ed1f..bc13077dcc 100644 --- a/examples/control/SignalControl.cpp +++ b/examples/control/SignalControl.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file SignalControl.cpp + * @file * @brief Some examples code for using emp::Signal */ diff --git a/examples/data/DataFile.cpp b/examples/data/DataFile.cpp index c4b150eca8..e581fcf9f9 100644 --- a/examples/data/DataFile.cpp +++ b/examples/data/DataFile.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file DataFile.cpp + * @file * @brief Examples for DataFile demonstrating how to generate */ diff --git a/examples/data/DataInterface.cpp b/examples/data/DataInterface.cpp index 2b77cbc308..6c218bb921 100644 --- a/examples/data/DataInterface.cpp +++ b/examples/data/DataInterface.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file DataInterface.cpp + * @file * @brief Examples for DataInterface demonstrating how to track different types of data. */ diff --git a/examples/data/DataManager.cpp b/examples/data/DataManager.cpp index 2dcbeff148..0f30513e5c 100644 --- a/examples/data/DataManager.cpp +++ b/examples/data/DataManager.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file DataManager.cpp + * @file * @brief Examples for DataNode demonstrating how to track different types of data. */ diff --git a/examples/data/DataNode.cpp b/examples/data/DataNode.cpp index e390f7acdd..6448d18719 100644 --- a/examples/data/DataNode.cpp +++ b/examples/data/DataNode.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file DataNode.cpp + * @file * @brief Examples for DataNode demonstrating how to track different types of data. */ diff --git a/examples/data/Makefile b/examples/data/Makefile index 48766adf83..4c0366926b 100644 --- a/examples/data/Makefile +++ b/examples/data/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/data/Trait.cpp b/examples/data/Trait.cpp index 006fc5598e..6ca8b07262 100644 --- a/examples/data/Trait.cpp +++ b/examples/data/Trait.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Trait.cpp + * @file * @brief An example file for Trait and TraitSet classes. */ diff --git a/examples/data/VarMap.cpp b/examples/data/VarMap.cpp index 2a9ade706b..aa53503dfe 100644 --- a/examples/data/VarMap.cpp +++ b/examples/data/VarMap.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file VarMap.cpp + * @file * @brief An example file for using VarMap */ diff --git a/examples/datastructs/Cache.cpp b/examples/datastructs/Cache.cpp index eed0eed7cf..40fd243726 100644 --- a/examples/datastructs/Cache.cpp +++ b/examples/datastructs/Cache.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file Cache.cpp + * @file * @brief Some examples code for using emp::Cache */ diff --git a/examples/datastructs/IndexMap.cpp b/examples/datastructs/IndexMap.cpp index 5c1f5060bb..e6ae66a068 100644 --- a/examples/datastructs/IndexMap.cpp +++ b/examples/datastructs/IndexMap.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file IndexMap.cpp + * @file */ #include diff --git a/examples/datastructs/Makefile b/examples/datastructs/Makefile index a4ca0afc60..9d79199f4c 100644 --- a/examples/datastructs/Makefile +++ b/examples/datastructs/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/datastructs/StringMap.cpp b/examples/datastructs/StringMap.cpp index 24105954ab..3a638ceade 100644 --- a/examples/datastructs/StringMap.cpp +++ b/examples/datastructs/StringMap.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018-2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018-2021 - * - * @file StringMap.cpp + * @file * @brief Some examples code for using emp::StringMap and emp::StringID */ diff --git a/examples/datastructs/TimeQueue.cpp b/examples/datastructs/TimeQueue.cpp index cda4c0a5ed..055f00edc2 100644 --- a/examples/datastructs/TimeQueue.cpp +++ b/examples/datastructs/TimeQueue.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file TimeQueue.cpp + * @file * @brief Some examples code for using emp::TimeQueue */ diff --git a/examples/datastructs/TypeMap.cpp b/examples/datastructs/TypeMap.cpp index c828a06112..01f0bccc5a 100644 --- a/examples/datastructs/TypeMap.cpp +++ b/examples/datastructs/TypeMap.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file TypeMap.cpp + * @file * @brief Some example code for using emp::BitSet */ diff --git a/examples/datastructs/hash_utils.cpp b/examples/datastructs/hash_utils.cpp index 757169bad6..6489b23ff0 100644 --- a/examples/datastructs/hash_utils.cpp +++ b/examples/datastructs/hash_utils.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file hash_utils.cpp + * @file * @brief Some examples code for using hash_utils.hpp */ diff --git a/examples/datastructs/ra_set.cpp b/examples/datastructs/ra_set.cpp index cfde12939b..c2e0d33eb9 100644 --- a/examples/datastructs/ra_set.cpp +++ b/examples/datastructs/ra_set.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file ra_set.cpp + * @file * @brief Some example code for using emp::ra_set */ diff --git a/examples/datastructs/tuple_utils.cpp b/examples/datastructs/tuple_utils.cpp index 034031223b..3870bc4a09 100644 --- a/examples/datastructs/tuple_utils.cpp +++ b/examples/datastructs/tuple_utils.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021 - * - * @file tuple_utils.cpp + * @file * @brief Some examples code for using tuple_utils.hpp */ diff --git a/examples/datastructs/valsort_map.cpp b/examples/datastructs/valsort_map.cpp index a539dbe4f3..bca598d74a 100644 --- a/examples/datastructs/valsort_map.cpp +++ b/examples/datastructs/valsort_map.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file valsort_map.cpp + * @file * @brief Some example code for using emp::valsort_map */ diff --git a/examples/datastructs/vector_utils.cpp b/examples/datastructs/vector_utils.cpp index 0dfb1581da..1847d84b3a 100644 --- a/examples/datastructs/vector_utils.cpp +++ b/examples/datastructs/vector_utils.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2020 - * - * @file vector_utils.cpp + * @file * @brief Some examples code for using vector_utils.hpp */ diff --git a/examples/functional/AnyFunction.cpp b/examples/functional/AnyFunction.cpp index 5b6a424606..d251ae01a0 100644 --- a/examples/functional/AnyFunction.cpp +++ b/examples/functional/AnyFunction.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file AnyFunction.cpp + * @file * @brief Some example code for using emp::AnyFunction */ diff --git a/examples/functional/GenericFunction.cpp b/examples/functional/GenericFunction.cpp index 1652eea3e3..268eb5f77c 100644 --- a/examples/functional/GenericFunction.cpp +++ b/examples/functional/GenericFunction.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file GenericFunction.cpp + * @file * @brief Some example code for using emp::GenericFunction */ diff --git a/examples/functional/Makefile b/examples/functional/Makefile index 72b888fa6a..cb3b2e3280 100644 --- a/examples/functional/Makefile +++ b/examples/functional/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/functional/flex_function.cpp b/examples/functional/flex_function.cpp index e9e705c3ed..a5b7474506 100644 --- a/examples/functional/flex_function.cpp +++ b/examples/functional/flex_function.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file flex_function.cpp + * @file * @brief Some examples code for using emp::flex_function */ diff --git a/examples/functional/memo_function.cpp b/examples/functional/memo_function.cpp index c16450cef5..129236b289 100644 --- a/examples/functional/memo_function.cpp +++ b/examples/functional/memo_function.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file memo_function.cpp + * @file * @brief Some examples code for using emp::memo_function */ diff --git a/examples/games/Makefile b/examples/games/Makefile index c4b7a366c8..0a54a5eaf9 100644 --- a/examples/games/Makefile +++ b/examples/games/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/geometry/Makefile b/examples/geometry/Makefile index d94514c4bb..2aaada367f 100644 --- a/examples/geometry/Makefile +++ b/examples/geometry/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/geometry/Surface.cpp b/examples/geometry/Surface.cpp index 4a5e39161c..527ec5f0a4 100644 --- a/examples/geometry/Surface.cpp +++ b/examples/geometry/Surface.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Surface.cpp + * @file */ #include diff --git a/examples/hardware/AvidaGP.cpp b/examples/hardware/AvidaGP.cpp index 044628b242..2f0728bd79 100644 --- a/examples/hardware/AvidaGP.cpp +++ b/examples/hardware/AvidaGP.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file AvidaGP.cpp + * @file * @brief This is example code for using AvidaGP. */ diff --git a/examples/hardware/BitSorter.cpp b/examples/hardware/BitSorter.cpp index 896ad6cb71..40f75c5330 100644 --- a/examples/hardware/BitSorter.cpp +++ b/examples/hardware/BitSorter.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file BitSorter.cpp + * @file * @brief This is example code for using BitSorter.h */ diff --git a/examples/hardware/LinearCode.cpp b/examples/hardware/LinearCode.cpp index e8deb1002b..a4070142f3 100644 --- a/examples/hardware/LinearCode.cpp +++ b/examples/hardware/LinearCode.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file LinearCode.cpp + * @file * @brief This is example code for using LinearCode.hpp */ diff --git a/examples/hardware/Makefile b/examples/hardware/Makefile index cb73cfa432..f20f3246cc 100644 --- a/examples/hardware/Makefile +++ b/examples/hardware/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -pthread -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/hardware/SignalGP.cpp b/examples/hardware/SignalGP.cpp index 0a48013fa7..2ee7e0d5bf 100644 --- a/examples/hardware/SignalGP.cpp +++ b/examples/hardware/SignalGP.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file SignalGP.cpp + * @file * @brief This is example code for using EventDrivenGP (SignalGP). */ diff --git a/examples/io/ContiguousStream.cpp b/examples/io/ContiguousStream.cpp index 360025810e..304b0fa37c 100644 --- a/examples/io/ContiguousStream.cpp +++ b/examples/io/ContiguousStream.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file ContiguousStream.cpp + * @file */ #include diff --git a/examples/io/File.cpp b/examples/io/File.cpp index 012180b8e7..a77cfb711a 100644 --- a/examples/io/File.cpp +++ b/examples/io/File.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file File.cpp + * @file * @brief An example file for using File.hpp */ @@ -29,31 +30,31 @@ int main() spreadsheet.Write(std::cout); - emp::vector first_col = spreadsheet.ExtractCol(); + emp::vector first_col = spreadsheet.ExtractCol(); std::cout << "\nAfter column is extracted:" << std::endl; spreadsheet.Write(std::cout); - std::cout << "Extracted column: " << emp::to_string(first_col) << std::endl; + std::cout << "Extracted column: " << emp::MakeString(first_col) << std::endl; emp::vector second_col = spreadsheet.ExtractColAs(); std::cout << "\nAfter another column is extracted as size_t:" << std::endl; spreadsheet.Write(std::cout); - std::cout << "Extracted column: " << emp::to_string(second_col) << std::endl; + std::cout << "Extracted column: " << emp::MakeString(second_col) << std::endl; - emp::vector first_row = spreadsheet.ExtractRow(); + emp::vector first_row = spreadsheet.ExtractRow(); std::cout << "\nAfter a row is extracted:" << std::endl; spreadsheet.Write(std::cout); - std::cout << "Extracted row: " << emp::to_string(first_row) << std::endl; + std::cout << "Extracted row: " << emp::MakeString(first_row) << std::endl; emp::vector second_row = spreadsheet.ExtractRowAs(); std::cout << "\nAfter a row is extracted as size_t:" << std::endl; spreadsheet.Write(std::cout); - std::cout << "Extracted row: " << emp::to_string(second_row) << std::endl; + std::cout << "Extracted row: " << emp::MakeString(second_row) << std::endl; spreadsheet.Append("1000,1001,1002,1003"); auto full_data = spreadsheet.ToData(); std::cout << "\nAfter all remaining data is extracted as size_t:" << std::endl; spreadsheet.Write(std::cout); - std::cout << "Extracted data: " << emp::to_string(full_data) << std::endl; + std::cout << "Extracted data: " << emp::MakeString(full_data) << std::endl; } diff --git a/examples/io/Makefile b/examples/io/Makefile index ab2d1f9620..12d64df3e5 100644 --- a/examples/io/Makefile +++ b/examples/io/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -pthread -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/io/StreamManager.cpp b/examples/io/StreamManager.cpp index 42e2b88653..67238c5d62 100644 --- a/examples/io/StreamManager.cpp +++ b/examples/io/StreamManager.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file StreamManager.cpp + * @file * @brief An example file for using StreamManager.hpp */ diff --git a/examples/math/CombinedBinomialDistribution.cpp b/examples/math/CombinedBinomialDistribution.cpp new file mode 100644 index 0000000000..3e43fab36c --- /dev/null +++ b/examples/math/CombinedBinomialDistribution.cpp @@ -0,0 +1,36 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2022 +*/ +/** + * @file + * @brief Some examples code for using emp::CombinedBinomialDistribution + */ + + +#include "emp/math/CombinedBinomialDistribution.hpp" +#include "emp/math/Random.hpp" + +int main(int argc, char* argv[]) +{ + if(argc != 4){ + std::cout << "Error! Expecting exactly three command line arguments: " + << "p n num_trials" << std::endl; + emp_assert(false); + } + double p = std::stod(argv[1]); + size_t n = std::stoi(argv[2]); + size_t num_trials = std::stoi(argv[3]); + + emp::Random random; + emp::CombinedBinomialDistribution distribution(p, 1); + + double mean = 0; + + for(size_t i = 0; i < num_trials; i++){ + mean += (double)distribution.PickRandom(n, random) / num_trials; + } + std::cout << "Mean after " << num_trials << " trials: " << mean << std::endl; + return 0; +} diff --git a/examples/math/Distribution.cpp b/examples/math/Distribution.cpp index f6fd090659..f15dda9900 100644 --- a/examples/math/Distribution.cpp +++ b/examples/math/Distribution.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018-2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018-2020 - * - * @file Distribution.cpp + * @file * @brief Some examples code for using emp::Distribution and derived classes. */ diff --git a/examples/math/Makefile b/examples/math/Makefile index 0273c03544..d582e906cd 100644 --- a/examples/math/Makefile +++ b/examples/math/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -pthread -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc @@ -19,7 +19,7 @@ CFLAGS_web_debug := $(CFLAGS_all) $(OFLAGS_web_debug) --js-library ../../include CFLAGS_web_opt := $(CFLAGS_all) $(OFLAGS_web_opt) --js-library ../../include/emp/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s NO_EXIT_RUNTIME=1 #CFLAGS_web := $(CFLAGS_all) $(OFLAGS_web) --js-library ../../include/emp/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=1 -TARGETS := combos constants Distribution info_theory math Random Range stats +TARGETS := combos constants Distribution info_theory math Random Range stats CombinedBinomialDistribution default: native diff --git a/examples/math/Random.cpp b/examples/math/Random.cpp index 45be7dffb1..7f99022976 100644 --- a/examples/math/Random.cpp +++ b/examples/math/Random.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file Random.cpp + * @file * @brief Some examples code for using emp::Random */ @@ -20,6 +21,8 @@ int main() std::cout << "Digits in random orders:" << std::endl; size_t num_samples = 10; + + std::cout << "Permutations: " << std::endl; for (size_t s = 0; s < num_samples; s++) { emp::vector permut = emp::GetPermutation(random, 10); for (size_t i = 0; i < 10; i++) { @@ -27,4 +30,17 @@ int main() } std::cout << std::endl; } + + std::cout << "Exponentials: " << std::endl; + double p = 0.5; + std::cout << "p = " << p << std::endl; + for (size_t s = 0; s < num_samples; s++) std::cout << random.GetExponential(p) << std::endl; + + p = 0.1; + std::cout << "\np = " << p << std::endl; + for (size_t s = 0; s < num_samples; s++) std::cout << random.GetExponential(p) << std::endl; + + p = 0.9; + std::cout << "\np = " << p << std::endl; + for (size_t s = 0; s < num_samples; s++) std::cout << random.GetExponential(p) << std::endl; } diff --git a/examples/math/Range.cpp b/examples/math/Range.cpp index c9ed443b97..6a62e57987 100644 --- a/examples/math/Range.cpp +++ b/examples/math/Range.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file Range.cpp + * @file */ #include @@ -18,7 +19,7 @@ int main() std::cout << "Upper = " << range.GetUpper() << std::endl; for (int i = 10; i < 40; i += 5) { - std::cout << "Value " << i << " valid = " << range.Valid(i) << std::endl; + std::cout << "Value " << i << " valid = " << range.Has(i) << std::endl; } for (size_t s = 4; s <= 8; s++) { diff --git a/examples/math/combos.cpp b/examples/math/combos.cpp index d65709a586..5731417daa 100644 --- a/examples/math/combos.cpp +++ b/examples/math/combos.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file combos.cpp + * @file * @brief Example code for emp::ComboIDs class, emp::ComboGenerator class, and emp::combos function. */ diff --git a/examples/math/constants.cpp b/examples/math/constants.cpp index 9ca1e511fb..fa13464aec 100644 --- a/examples/math/constants.cpp +++ b/examples/math/constants.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file constants.cpp + * @file */ #include diff --git a/examples/math/info_theory.cpp b/examples/math/info_theory.cpp index 02092df03b..d6df8a7190 100644 --- a/examples/math/info_theory.cpp +++ b/examples/math/info_theory.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file info_theory.cpp + * @file * @brief Some examples code for using info theory tools. */ diff --git a/examples/math/math.cpp b/examples/math/math.cpp index 3fbf096160..ab376eb444 100644 --- a/examples/math/math.cpp +++ b/examples/math/math.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file math.cpp + * @file * @brief Some examples code for using math functions. */ diff --git a/examples/math/stats.cpp b/examples/math/stats.cpp index f39fc4b941..67e754c89d 100644 --- a/examples/math/stats.cpp +++ b/examples/math/stats.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file stats.cpp + * @file */ #include diff --git a/examples/meta/ConceptWrapper.cpp b/examples/meta/ConceptWrapper.cpp index 00d11f4a07..0a9d4b3a1b 100644 --- a/examples/meta/ConceptWrapper.cpp +++ b/examples/meta/ConceptWrapper.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file ConceptWrapper.cpp + * @file * @brief Some examples code for using emp::ConceptWrapper template */ diff --git a/examples/meta/Makefile b/examples/meta/Makefile index 8b534f319b..d162e47b45 100644 --- a/examples/meta/Makefile +++ b/examples/meta/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/meta/TypePack.cpp b/examples/meta/TypePack.cpp index 87b8d43ccb..134a3f0f0a 100644 --- a/examples/meta/TypePack.cpp +++ b/examples/meta/TypePack.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2019 - * - * @file TypePack.cpp + * @file * @brief Some examples of code using reflection techniques. */ diff --git a/examples/meta/ValPack.cpp b/examples/meta/ValPack.cpp index ee5aaac084..3f693c4d71 100644 --- a/examples/meta/ValPack.cpp +++ b/examples/meta/ValPack.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file ValPack.cpp + * @file * @brief Some example code for using ValPack */ diff --git a/examples/meta/macros.cpp b/examples/meta/macros.cpp index a0ced5e11b..4445e24e13 100644 --- a/examples/meta/macros.cpp +++ b/examples/meta/macros.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021 - * - * @file macros.cpp + * @file */ #include diff --git a/examples/meta/meta.cpp b/examples/meta/meta.cpp index 0061e8c6a6..ddced80d41 100644 --- a/examples/meta/meta.cpp +++ b/examples/meta/meta.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021 - * - * @file meta.cpp + * @file */ #include diff --git a/examples/meta/reflection.cpp b/examples/meta/reflection.cpp index 1a834b459b..5988839d57 100644 --- a/examples/meta/reflection.cpp +++ b/examples/meta/reflection.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file reflection.cpp + * @file * @brief Some examples of code using reflection techniques. */ diff --git a/examples/prefab/Card.cpp b/examples/prefab/Card.cpp index ffe857cf6f..936c6905f3 100644 --- a/examples/prefab/Card.cpp +++ b/examples/prefab/Card.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file Card.cpp + * @file */ #include diff --git a/examples/prefab/CodeBlock.cpp b/examples/prefab/CodeBlock.cpp index 550447de4e..86d3df46d2 100644 --- a/examples/prefab/CodeBlock.cpp +++ b/examples/prefab/CodeBlock.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file CodeBlock.cpp + * @file */ #include diff --git a/examples/prefab/Collapse.cpp b/examples/prefab/Collapse.cpp index fc75853a11..89b0a4a473 100644 --- a/examples/prefab/Collapse.cpp +++ b/examples/prefab/Collapse.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file Collapse.cpp + * @file */ #include diff --git a/examples/prefab/CommentBox.cpp b/examples/prefab/CommentBox.cpp index 1bc5324c75..5b9aba862e 100644 --- a/examples/prefab/CommentBox.cpp +++ b/examples/prefab/CommentBox.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file CommentBox.cpp + * @file */ #include diff --git a/examples/prefab/ConfigPanel.cpp b/examples/prefab/ConfigPanel.cpp index 46aab2197d..b93b4cfcf7 100644 --- a/examples/prefab/ConfigPanel.cpp +++ b/examples/prefab/ConfigPanel.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file ConfigPanel.cpp + * @file */ #include diff --git a/examples/prefab/FontAwesomeIcon.cpp b/examples/prefab/FontAwesomeIcon.cpp index b8cb828d01..1ba170baa9 100644 --- a/examples/prefab/FontAwesomeIcon.cpp +++ b/examples/prefab/FontAwesomeIcon.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file FontAwesomeIcon.cpp + * @file */ #include diff --git a/examples/prefab/LoadingIcon.cpp b/examples/prefab/LoadingIcon.cpp index e6ae836fee..d1f8b2753e 100644 --- a/examples/prefab/LoadingIcon.cpp +++ b/examples/prefab/LoadingIcon.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file LoadingIcon.cpp + * @file */ #include diff --git a/examples/prefab/LoadingModal.cpp b/examples/prefab/LoadingModal.cpp index e93293c64e..7cdc3bac86 100644 --- a/examples/prefab/LoadingModal.cpp +++ b/examples/prefab/LoadingModal.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file LoadingModal.cpp + * @file */ #include diff --git a/examples/prefab/Makefile b/examples/prefab/Makefile index e2d39568f5..37cff3b33f 100644 --- a/examples/prefab/Makefile +++ b/examples/prefab/Makefile @@ -7,7 +7,7 @@ # WebAssembly. # Flags to use regardless of compiler -CFLAGS_all := -std=c++17 -Wall -Wno-unused-function -I../../include/ +CFLAGS_all := -std=c++20 -Wall -Wno-unused-function -I../../include/ # Emscripten compiler information CXX_web := emcc diff --git a/examples/prefab/Modal.cpp b/examples/prefab/Modal.cpp index 3de710d89a..61f761217a 100644 --- a/examples/prefab/Modal.cpp +++ b/examples/prefab/Modal.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file Modal.cpp + * @file */ #include diff --git a/examples/prefab/ReadoutPanel.cpp b/examples/prefab/ReadoutPanel.cpp index c34a1620a9..5da9683d1a 100644 --- a/examples/prefab/ReadoutPanel.cpp +++ b/examples/prefab/ReadoutPanel.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file ReadoutPanel.cpp + * @file */ #include "emp/math/Random.hpp" diff --git a/examples/prefab/ToggleSwitch.cpp b/examples/prefab/ToggleSwitch.cpp index d0c9038a3f..332f2d6d14 100644 --- a/examples/prefab/ToggleSwitch.cpp +++ b/examples/prefab/ToggleSwitch.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: ,020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date ,020 - * - * @file ToggleSwitch.cpp + * @file */ #include diff --git a/examples/prefab/assets/SampleConfig.hpp b/examples/prefab/assets/SampleConfig.hpp index f4bfaa44b7..7789620c81 100644 --- a/examples/prefab/assets/SampleConfig.hpp +++ b/examples/prefab/assets/SampleConfig.hpp @@ -1,9 +1,11 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file SampleConfig.hpp + * @file + * @brief TODO. * @note Adapted from Emily's memic_model project * https://github.com/emilydolson/memic_model */ diff --git a/examples/scholar/Citation.cpp b/examples/scholar/Citation.cpp index 46a1eeda5e..df35b48f83 100644 --- a/examples/scholar/Citation.cpp +++ b/examples/scholar/Citation.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file Citation.cpp + * @file */ #include diff --git a/examples/scholar/Makefile b/examples/scholar/Makefile index f32e235068..16e119f2df 100644 --- a/examples/scholar/Makefile +++ b/examples/scholar/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/testing/ExampleFail.cpp b/examples/testing/ExampleFail.cpp index cb8f9d8b06..15f50c5d14 100644 --- a/examples/testing/ExampleFail.cpp +++ b/examples/testing/ExampleFail.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file ExampleFail.cpp + * @file * @brief An example file to show what FAILING unit tests look like, with commentary. */ diff --git a/examples/testing/ExampleFail2.cpp b/examples/testing/ExampleFail2.cpp index 27d722da48..131e62fc0d 100644 --- a/examples/testing/ExampleFail2.cpp +++ b/examples/testing/ExampleFail2.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file ExampleFail2.cpp + * @file * @brief An second example file to show what a single FAILING unit test looks like. */ diff --git a/examples/testing/ExamplePass.cpp b/examples/testing/ExamplePass.cpp index 573c156248..b6b2b6933b 100644 --- a/examples/testing/ExamplePass.cpp +++ b/examples/testing/ExamplePass.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file ExamplePass.cpp + * @file * @brief An example file to show what PASSING unit tests look like, with commentary. * * Unit tests can be built easily, with a good bit of flexibility, as described below. diff --git a/examples/testing/Makefile b/examples/testing/Makefile index 9cdb06c2db..09e5b3b8aa 100644 --- a/examples/testing/Makefile +++ b/examples/testing/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -pthread -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/testing/Template.cpp b/examples/testing/Template.cpp index 1ce482aa7c..11c49f3081 100644 --- a/examples/testing/Template.cpp +++ b/examples/testing/Template.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Template.cpp + * @file * @brief A starting point for writing unit tests. */ diff --git a/examples/timing/Binomial.cpp b/examples/timing/Binomial.cpp new file mode 100644 index 0000000000..e5e31b1bb8 --- /dev/null +++ b/examples/timing/Binomial.cpp @@ -0,0 +1,198 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2022 +*/ +/** + * @file + */ + +#include +#include + +#include "../../include/emp/math/Distribution.hpp" +#include "../../include/emp/math/Random.hpp" +#include "../../include/emp/tools/string_utils.hpp" + +void TestGeometric(emp::Random & random, const double p, const size_t num_tests=1000000) { + std::cout << emp::ANSI_GreenBG() << emp::ANSI_Black() + << "---- Geometric Tests: p = " << p << " ----" + << emp::ANSI_Reset() + << std::endl; + + ////////- Pre-processed distribution + emp::NegativeBinomial dist(p, 1); + + std::clock_t start_time = std::clock(); + + double total = 0; + for (size_t i = 0; i < num_tests; i++) { + total += dist.PickRandom(random); + } + + std::clock_t tot_time = std::clock() - start_time; + double result = ((double) tot_time) / (double) CLOCKS_PER_SEC; + + std::cout << "Negative Binomial Distribution with p = " << p << " (and N=1)\n" + << " time = " << emp::ANSI_Bold() << result << " seconds." << emp::ANSI_NoBold() << "\n" + << " dist size = " << dist.GetSize() << "\n" + << " average = " << (total / num_tests) << "\n" + << std::endl; + + ////////- Random call (no pre-process) + start_time = std::clock(); + + total = 0; + for (size_t i = 0; i < num_tests; i++) { + total += random.GetGeometric(p); + } + + tot_time = std::clock() - start_time; + result = ((double) tot_time) / (double) CLOCKS_PER_SEC; + + std::cout << "random.GetGeometric(p) with p = " << p << "\n" + << " time = " << emp::ANSI_Bold() << result << " seconds." << emp::ANSI_NoBold() << "\n" + << " average = " << (total / num_tests) << "\n" + << std::endl; + +} + +void TestNegBinomial( + emp::Random & random, + const double p, + const size_t N, + const size_t num_tests=1000000) +{ + std::cout << emp::ANSI_BrightBlueBG() + << "---- Negative Binomial Tests: p = " << p << " ; N = " << N << " ----" + << emp::ANSI_Reset() + << std::endl; + + ////////- Pre-processed distribution + emp::NegativeBinomial dist(p, N); + + std::clock_t start_time = std::clock(); + + double total = 0; + for (size_t i = 0; i < num_tests; i++) { + total += dist.PickRandom(random); + } + + std::clock_t tot_time = std::clock() - start_time; + double result = ((double) tot_time) / (double) CLOCKS_PER_SEC; + + std::cout << "Negative Binomial Distribution with p = " << p << " and N = " << N << "\n" + << " time = " << emp::ANSI_Bold() << result << " seconds." << emp::ANSI_NoBold() << "\n" + << " dist size = " << dist.GetSize() << "\n" + << " average = " << (total / num_tests) << "\n" + << std::endl; + + ////////- Random call (no pre-process) + start_time = std::clock(); + + total = 0; + for (size_t i = 0; i < num_tests; i++) { + for (size_t n = 0; n < N; ++n) { + total += random.GetGeometric(p); + } + } + + tot_time = std::clock() - start_time; + result = ((double) tot_time) / (double) CLOCKS_PER_SEC; + + std::cout << "N = " << N << " calls to random.GetGeometric(p) with p = " << p << "\n" + << " time = " << emp::ANSI_Bold() << result << " seconds." << emp::ANSI_NoBold() << "\n" + << " average = " << (total / num_tests) << "\n" + << std::endl; +} + +void TestBinomial( + emp::Random & random, + const double p, + const size_t N, + const size_t num_tests=1000000) +{ + std::cout << emp::ANSI_MagentaBG() + << "---- Binomial Tests: p = " << p << " ; N = " << N << " ----" + << emp::ANSI_Reset() + << std::endl; + + ////////- Pre-processed distribution + emp::Binomial dist(p, N); + + std::clock_t start_time = std::clock(); + + double total = 0; + for (size_t i = 0; i < num_tests; i++) { + total += dist.PickRandom(random); + } + + std::clock_t tot_time = std::clock() - start_time; + double result = ((double) tot_time) / (double) CLOCKS_PER_SEC; + + std::cout << "Binomial Distribution with p = " << p << " and N = " << N << "\n" + << " time = " << emp::ANSI_Bold() << result << " seconds." << emp::ANSI_NoBold() << "\n" + << " dist size = " << dist.GetSize() << "\n" + << " average = " << (total / num_tests) << "\n" + << std::endl; + + ////////- Random call (no pre-process) + start_time = std::clock(); + + total = 0; + for (size_t i = 0; i < num_tests; i++) { + size_t pos = 0; + while( (pos += random.GetGeometric(p)) < N ) { + total++; + } + } + + tot_time = std::clock() - start_time; + result = ((double) tot_time) / (double) CLOCKS_PER_SEC; + + std::cout << "N = " << N << " calls to random.GetGeometric(p) with p = " << p << "\n" + << " time = " << emp::ANSI_Bold() << result << " seconds." << emp::ANSI_NoBold() << "\n" + << " average = " << (total / num_tests) << "\n" + << std::endl; +} + +int main() +{ + size_t num_tests = 1000000; + emp::Random random; + + TestGeometric(random, 0.9, num_tests); + TestGeometric(random, 0.5, num_tests); + TestGeometric(random, 0.1, num_tests); + TestGeometric(random, 0.01, num_tests); + TestGeometric(random, 0.001, num_tests); + TestGeometric(random, 0.0001, num_tests); + + TestNegBinomial(random, 0.9, 10, num_tests); + TestNegBinomial(random, 0.5, 10, num_tests); + TestNegBinomial(random, 0.1, 10, num_tests); + TestNegBinomial(random, 0.01, 10, num_tests); + TestNegBinomial(random, 0.001, 10, num_tests); + TestNegBinomial(random, 0.0001, 10, num_tests); + + TestNegBinomial(random, 0.9, 100, num_tests); + TestNegBinomial(random, 0.5, 100, num_tests); + TestNegBinomial(random, 0.1, 100, num_tests); + TestNegBinomial(random, 0.01, 100, num_tests); + TestNegBinomial(random, 0.001, 100, num_tests); + TestNegBinomial(random, 0.0001, 100, num_tests); + + TestBinomial(random, 0.9, 100, num_tests); + TestBinomial(random, 0.5, 100, num_tests); + TestBinomial(random, 0.1, 100, num_tests); + TestBinomial(random, 0.01, 100, num_tests); + TestBinomial(random, 0.001, 100, num_tests); + TestBinomial(random, 0.0001, 100, num_tests); + + TestBinomial(random, 0.9, 1000, num_tests); + TestBinomial(random, 0.5, 1000, num_tests); + TestBinomial(random, 0.1, 1000, num_tests); + TestBinomial(random, 0.01, 1000, num_tests); + TestBinomial(random, 0.001, 1000, num_tests); + TestBinomial(random, 0.0001, 1000, num_tests); +} diff --git a/examples/timing/IndexMap.cpp b/examples/timing/IndexMap.cpp index 3748288922..080dab3ffc 100644 --- a/examples/timing/IndexMap.cpp +++ b/examples/timing/IndexMap.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file IndexMap.cpp + * @file * @brief Comparing the ordered versus unordered versions of IndexMap */ diff --git a/examples/timing/Makefile b/examples/timing/Makefile index c1ae00ec39..f5a8deb74b 100644 --- a/examples/timing/Makefile +++ b/examples/timing/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/timing/Othello.cpp b/examples/timing/Othello.cpp index a89544b996..b88f18b45c 100644 --- a/examples/timing/Othello.cpp +++ b/examples/timing/Othello.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Othello.cpp + * @file * @brief Code examining the speed of an Othello board. */ diff --git a/examples/timing/Random_timings.cpp b/examples/timing/Random_timings.cpp index a56ba0808d..c768c6b221 100644 --- a/examples/timing/Random_timings.cpp +++ b/examples/timing/Random_timings.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file Random_timings.cpp + * @file * @brief Some code testing the speed of random operations. */ diff --git a/examples/timing/bit_timings.cpp b/examples/timing/bit_timings.cpp index c6a62a011c..1874dfbd35 100644 --- a/examples/timing/bit_timings.cpp +++ b/examples/timing/bit_timings.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020-2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020-2021 - * - * @file bit_timings.cpp + * @file * @brief Some code testing the speed of operations on BitSet and BitVector. */ @@ -61,8 +62,8 @@ struct SpeedTester_impl : public SpeedTester_impl, OBJ_COUNT > bs_objs; - emp::array< emp::BitVector, OBJ_COUNT > bv_objs; + emp::array< emp::old::BitSet, OBJ_COUNT > bs_objs; + emp::array< emp::old::BitVector, OBJ_COUNT > bv_objs; using base_t = SpeedTester_impl; diff --git a/examples/timing/pointers.cpp b/examples/timing/pointers.cpp index aea6ca11c2..9f82aa9191 100644 --- a/examples/timing/pointers.cpp +++ b/examples/timing/pointers.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file pointers.cpp + * @file * @brief Some code comparing T*, emp::Ptr, and std::shared_ptr * * We are comparing the timings and code complexity for creating a series of N pointers, diff --git a/examples/tools/Makefile b/examples/tools/Makefile index 60251f1d80..f714250b0f 100644 --- a/examples/tools/Makefile +++ b/examples/tools/Makefile @@ -1,6 +1,6 @@ # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -I../../include/ -CFLAGS_version := -std=c++17 +CFLAGS_version := -std=c++20 # Emscripten compiler information CXX_web := emcc diff --git a/examples/tools/TypeTracker.cpp b/examples/tools/TypeTracker.cpp index 0c8255a8df..00c341b286 100644 --- a/examples/tools/TypeTracker.cpp +++ b/examples/tools/TypeTracker.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file TypeTracker.cpp + * @file * @brief Some example code for using TypeTracker */ diff --git a/examples/tools/attrs.cpp b/examples/tools/attrs.cpp index 81743fdb66..d16b25c25a 100644 --- a/examples/tools/attrs.cpp +++ b/examples/tools/attrs.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file attrs.cpp + * @file */ #include diff --git a/examples/web/Animate.cpp b/examples/web/Animate.cpp index f2d9ce8c84..de506c46e8 100644 --- a/examples/web/Animate.cpp +++ b/examples/web/Animate.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2018 - * - * @file Animate.cpp + * @file */ #include "emp/math/Random.hpp" diff --git a/examples/web/Animate2.cpp b/examples/web/Animate2.cpp index cf5f6f1457..ca707d9df3 100644 --- a/examples/web/Animate2.cpp +++ b/examples/web/Animate2.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2018 - * - * @file Animate2.cpp + * @file */ #include "emp/math/Random.hpp" diff --git a/examples/web/Attributes.cpp b/examples/web/Attributes.cpp index 744758e2c5..b5797e0ac0 100644 --- a/examples/web/Attributes.cpp +++ b/examples/web/Attributes.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2017 - * - * @file Attributes.cpp + * @file */ #include "emp/web/web.hpp" diff --git a/examples/web/Canvas.cpp b/examples/web/Canvas.cpp index e4cfbf14be..dcd619ef50 100644 --- a/examples/web/Canvas.cpp +++ b/examples/web/Canvas.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2018 - * - * @file Canvas.cpp + * @file */ #include "emp/math/Random.hpp" diff --git a/examples/web/DP.cpp b/examples/web/DP.cpp index 044d86175e..ad2248fc44 100644 --- a/examples/web/DP.cpp +++ b/examples/web/DP.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file DP.cpp + * @file */ #include diff --git a/examples/web/Div.cpp b/examples/web/Div.cpp index e1b5d5ee7d..48a9a983c1 100644 --- a/examples/web/Div.cpp +++ b/examples/web/Div.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2017 - * - * @file Div.cpp + * @file */ #include "emp/web/commands.hpp" diff --git a/examples/web/Example.cpp b/examples/web/Example.cpp index c67b8e24e8..83ad6e28e1 100644 --- a/examples/web/Example.cpp +++ b/examples/web/Example.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file Example.cpp + * @file */ #include "emp/web/web.hpp" diff --git a/examples/web/Font.cpp b/examples/web/Font.cpp index 04b0a7befb..a324fcd3cd 100644 --- a/examples/web/Font.cpp +++ b/examples/web/Font.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file Font.cpp + * @file */ #include "emp/web/web.hpp" diff --git a/examples/web/Graph.cpp b/examples/web/Graph.cpp index dba2229b2b..23f698104a 100644 --- a/examples/web/Graph.cpp +++ b/examples/web/Graph.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file Graph.cpp + * @file */ #include "emp/base/vector.hpp" diff --git a/examples/web/Hello.cpp b/examples/web/Hello.cpp index 272676a5ff..69278aeca2 100644 --- a/examples/web/Hello.cpp +++ b/examples/web/Hello.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file Hello.cpp + * @file */ #include "emp/web/web.hpp" diff --git a/examples/web/Image.cpp b/examples/web/Image.cpp index 5d9e8da769..2d1ab1469b 100644 --- a/examples/web/Image.cpp +++ b/examples/web/Image.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Image.cpp + * @file */ #include "emp/math/Random.hpp" diff --git a/examples/web/Makefile b/examples/web/Makefile index ecc3c5871a..42404c97a7 100644 --- a/examples/web/Makefile +++ b/examples/web/Makefile @@ -7,7 +7,7 @@ # WebAssembly. # Flags to use regardless of compiler -CFLAGS_all := -std=c++17 -Wall -Wno-unused-function -I../../include/ +CFLAGS_all := -std=c++20 -Wall -Wno-unused-function -I../../include/ # Emscripten compiler information CXX_web := emcc diff --git a/examples/web/RPS.cpp b/examples/web/RPS.cpp index 374031113f..5a848f91e1 100644 --- a/examples/web/RPS.cpp +++ b/examples/web/RPS.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2018 - * - * @file RPS.cpp + * @file */ #include "emp/geometry/Surface.hpp" diff --git a/examples/web/Sudoku.cpp b/examples/web/Sudoku.cpp index def1a8c3eb..664296e47a 100644 --- a/examples/web/Sudoku.cpp +++ b/examples/web/Sudoku.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2017 - * - * @file Sudoku.cpp + * @file */ #include "emp/base/vector.hpp" @@ -83,7 +84,7 @@ class SudokuBoard : public UI::Div { for (size_t r = 0; r < 9; r++) { for (size_t c = 0; c < 9; c++) { auto cell = table.GetCell(r,c); - cell.On("mousedown", [cell,r,c]() mutable { + cell.On("mousedown", [cell/*,r,c*/]() mutable { // doc.Div("table_bg").SetBackground("red"); // cell.SetCSS("BackgroundColor", "grey"); cell.Clear(); diff --git a/examples/web/Table.cpp b/examples/web/Table.cpp index f1622a96fc..5df3900c90 100644 --- a/examples/web/Table.cpp +++ b/examples/web/Table.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2017 - * - * @file Table.cpp + * @file */ #include "emp/web/web.hpp" diff --git a/examples/web/TextArea.cpp b/examples/web/TextArea.cpp index 7abbc1f692..4855d6ae84 100644 --- a/examples/web/TextArea.cpp +++ b/examples/web/TextArea.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2017 - * - * @file TextArea.cpp + * @file */ #include "emp/web/web.hpp" diff --git a/examples/web/Tween.cpp b/examples/web/Tween.cpp index e60fafb468..e603ae2604 100644 --- a/examples/web/Tween.cpp +++ b/examples/web/Tween.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2017 - * - * @file Tween.cpp + * @file */ #include "emp/web/Tween.hpp" diff --git a/examples/web/Web.cpp b/examples/web/Web.cpp index df55f48ec5..e4e7474664 100644 --- a/examples/web/Web.cpp +++ b/examples/web/Web.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2017 - * - * @file Web.cpp + * @file */ #include "emp/debug/alert.hpp" diff --git a/examples/web/assert.cpp b/examples/web/assert.cpp index d023d395bc..4bfc6e36f5 100644 --- a/examples/web/assert.cpp +++ b/examples/web/assert.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020-2022 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file assert.cpp + * @file */ #include "emp/base/assert.hpp" @@ -11,7 +12,7 @@ int main() { - int x{ 42 }; + [[maybe_unused]] int x{ 42 }; emp_assert(x > 1, "This assert passes in debug mode!", x); diff --git a/examples/web/keypress.cpp b/examples/web/keypress.cpp index 30a53ecd6e..c5528fd09b 100644 --- a/examples/web/keypress.cpp +++ b/examples/web/keypress.cpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2017 - * - * @file keypress.cpp + * @file */ #include "emp/web/KeypressManager.hpp" diff --git a/include/emp/Evolve/NK-const.hpp b/include/emp/Evolve/NK-const.hpp index 868116ead8..3ec437c9c4 100644 --- a/include/emp/Evolve/NK-const.hpp +++ b/include/emp/Evolve/NK-const.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file NK-const.hpp + * @file * @brief This file provides code to build NK landscapes, setup at compile time.. * * Knowing the size of N and K at compile time allow for slightly more optimized code, at the @@ -14,6 +15,7 @@ #define EMP_EVOLVE_NK_CONST_HPP_INCLUDE #include +#include #include "../base/assert.hpp" #include "../bits/BitSet.hpp" @@ -92,7 +94,7 @@ namespace evo { /// Get the fitness of a whole bitstring double GetFitness(const BitSet & genome) const { // Create a double-length genome to easily handle wrap-around. - BitSet genome2( genome.template Export() ); + BitSet genome2( genome.template ExportArray() ); genome2 |= (genome2 << N); double total = 0.0; diff --git a/include/emp/Evolve/NK.hpp b/include/emp/Evolve/NK.hpp index a19fa8cece..099b17f799 100644 --- a/include/emp/Evolve/NK.hpp +++ b/include/emp/Evolve/NK.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file NK.hpp + * @file * @brief This file provides code to build NK-based algorithms. * * Two version of landscapes are provided. NKLandscape pre-calculates the entire landscape, for @@ -19,6 +20,7 @@ #define EMP_EVOLVE_NK_HPP_INCLUDE #include +#include #include "../base/vector.hpp" #include "../bits/BitVector.hpp" diff --git a/include/emp/Evolve/OEE.hpp b/include/emp/Evolve/OEE.hpp index ceb9f619a9..1342dedf91 100644 --- a/include/emp/Evolve/OEE.hpp +++ b/include/emp/Evolve/OEE.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file OEE.hpp + * @file * @brief TODO. */ @@ -11,6 +12,7 @@ #define EMP_EVOLVE_OEE_HPP_INCLUDE #include +#include #include "../base/Ptr.hpp" #include "../base/vector.hpp" diff --git a/include/emp/Evolve/OrgInterface.hpp b/include/emp/Evolve/OrgInterface.hpp index a7ebf9945f..513bd4bfca 100644 --- a/include/emp/Evolve/OrgInterface.hpp +++ b/include/emp/Evolve/OrgInterface.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file OrgInterface.hpp + * @file * @brief An interface between an organism and the outside world. * @note Status: PLANNING */ diff --git a/include/emp/Evolve/Resource.hpp b/include/emp/Evolve/Resource.hpp index 3d4028a504..36bd2dd031 100644 --- a/include/emp/Evolve/Resource.hpp +++ b/include/emp/Evolve/Resource.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Resource.hpp + * @file * @brief Implement resource-based selection. * * @@ -15,6 +16,7 @@ #ifndef EMP_EVOLVE_RESOURCE_HPP_INCLUDE #define EMP_EVOLVE_RESOURCE_HPP_INCLUDE +#include #include "World.hpp" diff --git a/include/emp/Evolve/StateGrid.hpp b/include/emp/Evolve/StateGrid.hpp index a779e9fbce..202e6a7a6b 100644 --- a/include/emp/Evolve/StateGrid.hpp +++ b/include/emp/Evolve/StateGrid.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2018. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2018. - * - * @file StateGrid.hpp + * @file * @brief StateGrid maintains a rectilinear grid that agents can traverse. * * State grids are a matrix of values, representing states of a 2D environment that an organism @@ -21,6 +22,7 @@ #include +#include #include #include "../base/assert.hpp" diff --git a/include/emp/Evolve/Systematics.hpp b/include/emp/Evolve/Systematics.hpp index 323b6d780a..c246e3619e 100644 --- a/include/emp/Evolve/Systematics.hpp +++ b/include/emp/Evolve/Systematics.hpp @@ -1,12 +1,12 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2018 - * - * @file Systematics.hpp + * @file * @brief Track genotypes, species, clades, or lineages of organisms in a world. * - * * @todo Technically, we don't need to keep the ancestors in a set in order to track a lineage... * If we delete all of their descendants they should automaticaly be deleted. * @todo We should provide an option to back up systematics data to a file so that it doesn't all @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "../base/Ptr.hpp" diff --git a/include/emp/Evolve/SystematicsAnalysis.hpp b/include/emp/Evolve/SystematicsAnalysis.hpp index 4574207135..185a224eb7 100644 --- a/include/emp/Evolve/SystematicsAnalysis.hpp +++ b/include/emp/Evolve/SystematicsAnalysis.hpp @@ -1,10 +1,12 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018-2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file SystematicsAnalysis.hpp + * @file * @brief TODO. + * */ #ifndef EMP_EVOLVE_SYSTEMATICSANALYSIS_HPP_INCLUDE @@ -12,13 +14,12 @@ #include "../base/Ptr.hpp" -// Mutation info functions. Assumes each taxon has a struct containing an unordered map -// with keys that are strings indicating types of mutations and keys that are numbers -// indicating the number of that type of mutation that occurred to make this taxon from -// the parent. - namespace emp { + /// @returns the taxon with the highest fitness out of any active taxon + /// in the given systematics manager. + /// @tparam systematics_t The type of the systematics manager containing the phylogeny to analyze. + /// @param s the systematics manager to search in. Must have more than 0 active taxa. template Ptr FindDominant(systematics_t & s) { double best = -999999; @@ -33,10 +34,9 @@ namespace emp { return best_tax; } - /// Returns the total number of times a mutation of type @param type - /// that along @param taxon 's lineage. (Different from CountMuts in - /// that CountMuts sums them whereas CountMutSteps would count two - /// simultaneous mutations of the same type as one event) + /// Returns the total number of ancestor taxa in \c taxon 's lineage. + /// Requires that taxon is a member of a systematics manager that + /// has ancestor storing turned on template int LineageLength(Ptr taxon) { int count = 0; @@ -49,10 +49,20 @@ namespace emp { return count; } - /// Returns the total number of times a mutation of type @param type - /// that along @param taxon 's lineage. (Different from CountMuts in + /// Returns the total number of times a mutation of type \c type + /// occurred along \c taxon 's lineage. (Different from CountMuts in /// that CountMuts sums them whereas CountMutSteps would count two /// simultaneous mutations of the same type as one event) + /// Assumes each taxon has a struct containing an unordered map + /// with keys that are strings indicating types of mutations and keys that are numbers + /// indicating the number of that type of mutation that occurred to make this taxon from + /// the parent. + /// @param type string corresponding to a type of mutation. + /// Must be in the mut_counts dictionary (i.e. the dictionary + /// passed in when datastruct::mut_landscape_info::RecordMutation was called) + /// @param taxon a pointer to a taxon to count mutation steps for. + /// Must have a DATA_TYPE that supports mutation tracking + /// (e.g. mut_landscape_info) template int CountMutSteps(Ptr taxon, std::string type="substitution") { int count = 0; @@ -65,8 +75,8 @@ namespace emp { return count; } - /// Returns the total number of times a mutation of the types @param types - /// that along @param taxon 's lineage. (Different from CountMuts in + /// Returns the total number of times a mutation of the types \c types + /// that along the given taxon 's lineage. (Different from CountMuts in /// that CountMuts sums them whereas CountMutSteps would count two /// simultaneous mutations of the same type as one event) template @@ -83,8 +93,8 @@ namespace emp { return count; } - /// Returns the total number of mutations of type @param type that occurred - /// along @param taxon 's lineage. + /// Returns the total number of mutations of type \c type that occurred + /// along \c taxon 's lineage. template int CountMuts(Ptr taxon, std::string type="substitution") { int count = 0; @@ -97,8 +107,8 @@ namespace emp { return count; } - /// Returns the total number of mutations of the types @param types that occurred - /// along @param taxon 's lineage. + /// Returns the total number of mutations of the types in \c types that occurred + /// along the given taxon 's lineage. template int CountMuts(Ptr taxon, emp::vector types) { int count = 0; @@ -114,7 +124,7 @@ namespace emp { } /// Returns the total number of deleterious mutational steps that occurred - /// along @param taxon 's lineage. (a change from parent to child taxon counts + /// along the given taxon's lineage. (a change from parent to child taxon counts /// as a single step, regardless of the number of mutations that happened at /// that time point) template @@ -134,7 +144,7 @@ namespace emp { } /// Returns the total number of changes in phenotype that occurred - /// along @param taxon 's lineage. + /// along the given taxon's lineage. template int CountPhenotypeChanges(Ptr taxon) { int count = 0; // Start with current phenotype @@ -152,7 +162,7 @@ namespace emp { } /// Returns the total number of unique phenotypes that occurred - /// along @param taxon 's lineage. + /// along the given taxon's lineage. template int CountUniquePhenotypes(Ptr taxon) { std::setGetData().phenotype)> seen; diff --git a/include/emp/Evolve/World.hpp b/include/emp/Evolve/World.hpp index 7e869bdee8..762ddca409 100644 --- a/include/emp/Evolve/World.hpp +++ b/include/emp/Evolve/World.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2018 - * - * @file World.hpp + * @file * @brief Definition of a base class for a World template for use in evolutionary algorithms. * * A definition of the emp::World template, linking in specialized file handling, iterators, @@ -14,8 +15,6 @@ * whether or not they also affect injected organisms. (Right now they always do!!) * @todo We should Specialize World so that ANOTHER world can be used as an ORG, with proper * delegation to facilitate demes, pools, islands, etc. - * @todo We should be able to have any number of systematics managers, based on various type_trait - * information a that we want to track. * @todo Add a signal for DoBirth() for when a birth fails. * @todo Add a signal for population Reset() (and possibly Clear?) * @todo Add a feature to maintain population sorted by each phenotypic trait. This will allow @@ -28,6 +27,7 @@ #include #include +#include #include #include "../base/Ptr.hpp" @@ -367,8 +367,8 @@ namespace emp { return *(pop[id]); } - /// Retrieve a const reference to the organsim as the specified x,y coordinates. - /// @CAO: Technically, we should set this up with any number of coordinates. + /// Retrieve a const reference to the organism as the specified x,y coordinates. + // @CAO: Technically, we should set this up with any number of coordinates. ORG & GetOrg(size_t x, size_t y) { return GetOrg(x+y*GetWidth()); } /// Retrive a pointer to the contents of a specified cell; will be nullptr if the cell is @@ -1734,7 +1734,6 @@ namespace emp { os << std::endl; } } - } #endif // #ifndef EMP_EVOLVE_WORLD_HPP_INCLUDE diff --git a/include/emp/Evolve/World_iterator.hpp b/include/emp/Evolve/World_iterator.hpp index 852a61f0bb..aff4cc2015 100644 --- a/include/emp/Evolve/World_iterator.hpp +++ b/include/emp/Evolve/World_iterator.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2021. - * - * @file World_iterator.hpp + * @file * @brief This file defines iterators for use with emp::World objects. * * @note Originally called PopulationIterator.h @@ -15,6 +16,8 @@ #ifndef EMP_EVOLVE_WORLD_ITERATOR_HPP_INCLUDE #define EMP_EVOLVE_WORLD_ITERATOR_HPP_INCLUDE +#include + #include "../base/Ptr.hpp" namespace emp { diff --git a/include/emp/Evolve/World_output.hpp b/include/emp/Evolve/World_output.hpp index 79b6cb1949..61ecb81364 100644 --- a/include/emp/Evolve/World_output.hpp +++ b/include/emp/Evolve/World_output.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file World_output.hpp + * @file * @brief TODO. * * This file contains functions for adding additional data files to Worlds. @@ -12,6 +13,8 @@ #ifndef EMP_EVOLVE_WORLD_OUTPUT_HPP_INCLUDE #define EMP_EVOLVE_WORLD_OUTPUT_HPP_INCLUDE +#include + #include "../base/vector.hpp" #include "../data/DataFile.hpp" // Helper to determine when specific events should occur. #include "../tools/string_utils.hpp" diff --git a/include/emp/Evolve/World_reflect.hpp b/include/emp/Evolve/World_reflect.hpp index ffb76badd9..768dd1596d 100644 --- a/include/emp/Evolve/World_reflect.hpp +++ b/include/emp/Evolve/World_reflect.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2018 - * - * @file World_reflect.hpp + * @file * @brief Handle reflection on organisms to setup reasonable defaults in World. * * @note None of the functions defined here should be called from outside the world object; @@ -45,7 +46,7 @@ namespace emp { template void SetDefaultFitFun_impl(WORLD & world, ... ) { - world.SetFitFun( [](ORG & org){ + world.SetFitFun( [](ORG & /* org */){ emp_assert(false, "No default fitness function available"); return 0.0; } ); @@ -71,7 +72,7 @@ namespace emp { template void SetDefaultMutFun_impl(WORLD & world, ... ) { - world.SetMutFun( [](ORG & org, Random & random) { + world.SetMutFun( [](ORG & /* org */, Random & /* random */ ) { emp_assert(false, "No default DoMutations available"); return 0; } ); @@ -103,7 +104,7 @@ namespace emp { template void SetDefaultPrintFun_impl(WORLD & world, ... ) { - world.SetPrintFun( [](ORG & org, std::ostream & os){ + world.SetPrintFun( [](ORG & /* org */, std::ostream & /* os */){ emp_assert(false, "No default Print function available"); } ); } diff --git a/include/emp/Evolve/World_select.hpp b/include/emp/Evolve/World_select.hpp index 65214f7356..01afdfa53a 100644 --- a/include/emp/Evolve/World_select.hpp +++ b/include/emp/Evolve/World_select.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2021. - * - * @file World_select.hpp + * @file * @brief Functions for popular selection methods applied to worlds. */ @@ -12,6 +13,7 @@ #include #include +#include #include "../base/array.hpp" #include "../base/assert.hpp" @@ -186,7 +188,7 @@ namespace emp { /// EACH offspring produced. /// @param world The emp::World object with the organisms to be selected. /// @param fit_funs The set of fitness functions to shuffle for each organism reproduced. - /// @param repro_count How many rounds of repliction should we do. (default 1) + /// @param repro_count How many rounds of replication should we do. (default 1) /// @param max_funs The maximum number of fitness functions to use. (use 0 for all; default) template void LexicaseSelect(World & world, @@ -226,9 +228,9 @@ namespace emp { // Step through the functions in the proper order. cur_orgs = all_orgs; // Start with all of the organisms. - int depth = -1; +// int depth = -1; for (size_t fit_id : order) { - depth++; +// depth++; double max_fit = fitnesses[fit_id][cur_orgs[0]]; next_orgs.push_back(cur_orgs[0]); diff --git a/include/emp/Evolve/World_structure.hpp b/include/emp/Evolve/World_structure.hpp index eb2e439835..bdf469a5d8 100644 --- a/include/emp/Evolve/World_structure.hpp +++ b/include/emp/Evolve/World_structure.hpp @@ -1,16 +1,19 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2022 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2018 - * - * @file World_structure.hpp + * @file * @brief Functions for popular world structure methods. */ #ifndef EMP_EVOLVE_WORLD_STRUCTURE_HPP_INCLUDE #define EMP_EVOLVE_WORLD_STRUCTURE_HPP_INCLUDE +#include #include +#include #include "../base/array.hpp" #include "../base/assert.hpp" @@ -45,13 +48,15 @@ namespace emp { } WorldPosition(const WorldPosition &) = default; + WorldPosition & operator=(const WorldPosition &) = default; + uint32_t GetIndex() const { return index; } uint32_t GetPopID() const { return pop_id; } bool IsActive() const { return pop_id == 0; } bool IsValid() const { return index != invalid_id; } - WorldPosition & SetActive(bool _active=true) { pop_id = 0; return *this; } + WorldPosition & SetActive(bool /*_active*/=true) { pop_id = 0; return *this; } WorldPosition & SetPopID(size_t _id) { emp_assert(_id <= invalid_id); pop_id = (uint32_t) _id; return *this; } WorldPosition & SetIndex(size_t _id) { emp_assert(_id <= invalid_id); index = (uint32_t) _id; return *this; } WorldPosition & MarkInvalid() { index = invalid_id; pop_id = invalid_id; return *this; } @@ -94,7 +99,7 @@ namespace emp { }; /// Set the population to be a set of pools that are individually well mixed, but with limited - /// migtation. Arguments are the number of pools, the size of each pool, and whether the + /// migration. Arguments are the number of pools, the size of each pool, and whether the /// generations should be synchronous (true) or not (false, default). template void SetPools(World & world, size_t num_pools, @@ -105,7 +110,7 @@ namespace emp { // -- Setup functions -- // Inject in an empty pool -or- randomly if none empty - world.SetAddInjectFun( [&world,pool_size](Ptr new_org) { + world.SetAddInjectFun( [&world,pool_size](Ptr /*new_org*/) { for (size_t id = 0; id < world.GetSize(); id += pool_size) { if (world.IsOccupied(id) == false) return WorldPosition(id); } @@ -126,7 +131,7 @@ namespace emp { if (synchronous_gen) { // Place births in the next open spot in the new pool (or randomly if full!) - world.SetAddBirthFun( [&world,pool_size](Ptr new_org, WorldPosition parent_pos) { + world.SetAddBirthFun( [&world,pool_size]([[maybe_unused]] Ptr new_org, WorldPosition parent_pos) { emp_assert(new_org); // New organism must exist. const size_t parent_id = parent_pos.GetIndex(); const size_t pool_id = parent_id / pool_size; @@ -142,7 +147,7 @@ namespace emp { world.SetAttribute("SynchronousGen", "True"); } else { // Asynchronous: always go to a neighbor in current population. - world.SetAddBirthFun( [&world](Ptr new_org, WorldPosition parent_pos) { + world.SetAddBirthFun( [&world]([[maybe_unused]] Ptr new_org, WorldPosition parent_pos) { auto pos = world.GetRandomNeighborPos(parent_pos); return pos; // Place org in existing population. }); @@ -172,7 +177,7 @@ namespace emp { world.MarkSpaceStructured(false).MarkPhenoStructured(true); // -- Setup functions -- - // Inject into the appropriate positon based on phenotype. Note that an inject will fail + // Inject into the appropriate position based on phenotype. Note that an inject will fail // if a more fit organism is already in place; you must run clear first if you want to // ensure placement. world.SetAddInjectFun( [&world,traits,trait_counts](Ptr new_org) { @@ -270,7 +275,7 @@ namespace emp { emp::vector distance; ///< And what is their distance? World & world; ///< World object being tracked. - TraitSet traits; ///< Traits we are tryng to spread + TraitSet traits; ///< Traits we are trying to spread emp::vector min_vals; ///< Smallest value found for each trait. emp::vector max_vals; ///< Largest value found for each trait. emp::vector bin_width; ///< Largest value found for each trait. @@ -323,10 +328,10 @@ namespace emp { size_t bin_id = org_bins[refresh_id]; Refresh_AgainstBin(refresh_id, bin_id); - // Then check all neighbor bins. Ignoring diagnols for now since they could be expensive... + // Then check all neighbor bins. Ignoring diagonals for now since they could be expensive... // (though technically we need them...) size_t trait_offset = 1; - for (size_t trait_id = 0; trait_id < traits.GetSize(); trait_id++) { + for (size_t trait_id = start_id; trait_id < traits.GetSize(); trait_id++) { size_t prev_bin_id = bin_id - trait_offset; if (prev_bin_id < num_total_bins) { Refresh_AgainstBin(refresh_id, prev_bin_id); @@ -396,7 +401,7 @@ namespace emp { is_setup = false; } - /// Find the best organism to kill in the popualtion. In this case, find the two closest organisms + /// Find the best organism to kill in the population. In this case, find the two closest organisms /// and kill the one with the lower fitness. size_t FindKill() { if (!is_setup) Setup(); // The first time we run out of space and need to kill, setup structure! @@ -485,7 +490,7 @@ namespace emp { emp_assert(org_bins[i] < num_total_bins, i, org_bins[i], num_total_bins, world.GetNumOrgs()); } - size_t org_count = 0; + [[maybe_unused]] size_t org_count = 0; for (size_t i = 0; i < num_total_bins; i++) { org_count += bin_ids[i].size(); for (size_t org_id : bin_ids[i]) { @@ -514,10 +519,10 @@ namespace emp { world.OnPlacement( [info_ptr](size_t pos) mutable { info_ptr->Update(pos); } ); // -- Setup functions -- - // Inject into the appropriate positon based on phenotype. Note that an inject will fail + // Inject into the appropriate position based on phenotype. Note that an inject will fail // if a more fit organism is already in place; you must run clear first if you want to // ensure placement. - world.SetAddInjectFun( [&world, traits, world_size, info_ptr](Ptr new_org) { + world.SetAddInjectFun( [/*&world, traits,*/ world_size, info_ptr]([[maybe_unused]] Ptr new_org) { size_t pos = info_ptr->GetBirthPos(world_size); return WorldPosition(pos); }); @@ -527,7 +532,7 @@ namespace emp { world.SetGetNeighborFun( [](WorldPosition pos) { emp_assert(false); return pos; }); // Find the two closest organisms and kill the lower fit one. (Killing sparsely...) - // Must unsetup population for next birth to work. + // Must un-setup population for next birth to work. world.SetKillOrgFun( [&world, info_ptr](){ const size_t last_id = world.GetSize() - 1; world.Swap(info_ptr->FindKill(), last_id); @@ -538,7 +543,7 @@ namespace emp { }); // Birth is effectively the same as inject. - world.SetAddBirthFun( [&world, traits, world_size, info_ptr](Ptr new_org, WorldPosition parent_pos) { + world.SetAddBirthFun( [/*&world, traits,*/ world_size, info_ptr]([[maybe_unused]] Ptr new_org, WorldPosition parent_pos) { (void) parent_pos; size_t pos = info_ptr->GetBirthPos(world_size); return WorldPosition(pos); diff --git a/include/emp/base/MapProxy.hpp b/include/emp/base/MapProxy.hpp index 2cb39e889c..446a28ac15 100644 --- a/include/emp/base/MapProxy.hpp +++ b/include/emp/base/MapProxy.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file MapProxy.hpp + * @file * @brief A proxy for indecies returned from any map type to ensure they are initialized. * @note Status: ALPHA */ @@ -107,7 +108,7 @@ namespace emp { operator const T&() const { emp_assert(is_init); return value; } }; - + #ifndef DOXYGEN_SHOULD_SKIP_THIS // Doxygen is getting tripped up by the enable_ifs /// A type trait to determine if a class is a MapProxy template struct is_MapProxy : public std::false_type { }; @@ -115,8 +116,7 @@ namespace emp { struct is_MapProxy> : public std::true_type { }; - // Build externaly binary operators with MapProxy as the second argument. - #ifndef DOXYGEN_SHOULD_SKIP_THIS // Doxygen is getting tripped up by the enable_ifs + // Build external binary operators with MapProxy as the second argument. template () == false>::type* = nullptr> auto operator + (T1 v1, const MapProxy & v2) { return v1 + v2.emp_GetValue(); } template () == false>::type* = nullptr> @@ -158,6 +158,7 @@ namespace emp { #endif /*DOXYGEN_SHOULD_SKIP_THIS*/ } +#ifndef DOXYGEN_SHOULD_SKIP_THIS // A crude, generic printing function for emp::MapProxy. template std::ostream & operator<<(std::ostream & out, const typename emp::MapProxy & p) { @@ -170,5 +171,6 @@ std::istream & operator>>(std::istream & is, typename emp::MapProxy & p) { is >> p.emp_GetValue(); return is; } +#endif // DOXYGEN_SHOULD_SKIP_THIS #endif // #ifndef EMP_BASE_MAPPROXY_HPP_INCLUDE diff --git a/include/emp/base/Ptr.hpp b/include/emp/base/Ptr.hpp index 3b32c353d8..d5af258de9 100644 --- a/include/emp/base/Ptr.hpp +++ b/include/emp/base/Ptr.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2022. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2020. - * - * @file Ptr.hpp + * @file * @brief A wrapper for pointers that does careful memory tracking (but only in debug mode). * @note Status: BETA * @@ -15,8 +16,11 @@ * intentionally) you can define EMP_NO_PTR_TO_PTR * * If you trip an assert, you can re-do the run a track a specific pointer by defining - * EMP_ABORT_PTR_NEW or EMP_ABORT_PTR_DELETE to the ID of the pointer in question. This will - * allow you to track the pointer more easily in a debugger. + * EMP_ABORT_PTR_NEW or EMP_ABORT_PTR_DELETE to the ID of the pointer in question. + * + * For example: -DEMP_ABORT_PTR_NEW=1691 + * + * This will allow you to track the pointer more easily in a debugger. * * @todo Track information about emp::vector and emp::array objects to make sure we don't * point directly into them? (A resize() could make such pointers invalid!) Or better, warn @@ -28,6 +32,7 @@ #define EMP_BASE_PTR_HPP_INCLUDE #include +#include #include #include "assert.hpp" @@ -82,8 +87,8 @@ namespace emp { } PtrInfo(const PtrInfo &) = default; PtrInfo(PtrInfo &&) = default; - PtrInfo & operator=(const PtrInfo &) = default; - PtrInfo & operator=(PtrInfo &&) = default; + PtrInfo & operator=(const PtrInfo &) & = default; + PtrInfo & operator=(PtrInfo &&) & = default; ~PtrInfo() { if (internal::ptr_debug) std::cout << "Deleted info for pointer " << ptr << std::endl; @@ -179,7 +184,7 @@ namespace emp { } if (undeleted_info.size()) { - std::cerr << undeleted_info.size() << " undeleted pointers at end of exectution.\n"; + std::cerr << undeleted_info.size() << " undeleted pointers at end of execution.\n"; for (size_t i = 0; i < undeleted_info.size() && i < 10; ++i) { const auto & info = undeleted_info[i]; std::cerr << " PTR=" << info.GetPtr() @@ -215,7 +220,7 @@ namespace emp { return ptr_id.find(ptr) != ptr_id.end(); } - /// Retrive the ID associated with a pointer. + /// Retrieve the ID associated with a pointer. size_t GetCurID(const void * ptr) { emp_assert(HasPtr(ptr)); return ptr_id[ptr]; } /// Lookup how many pointers are being tracked. @@ -270,7 +275,7 @@ namespace emp { } #endif if (internal::ptr_debug) std::cout << "New: " << id << " (" << ptr << ")" << std::endl; - // Make sure pointer is not already stored -OR- hase been deleted (since re-use is possible). + // Make sure pointer is not already stored -OR- has been deleted (since re-use is possible). emp_assert(!HasPtr(ptr) || IsDeleted(GetCurID(ptr)), id); id_info.emplace_back(ptr); ptr_id[ptr] = id; @@ -285,14 +290,14 @@ namespace emp { return id; } - /// Increment the nuber of Pointers associated with an ID + /// Increment the number of Pointers associated with an ID void IncID(size_t id) { if (id == UNTRACKED_ID) return; // Not tracked! if (internal::ptr_debug) std::cout << "Inc: " << id << std::endl; id_info[id].Inc(id); } - /// Decrement the nuber of Pointers associated with an ID + /// Decrement the number of Pointers associated with an ID void DecID(size_t id) { if (id == UNTRACKED_ID) return; // Not tracked! auto & info = id_info[id]; @@ -328,7 +333,7 @@ namespace emp { namespace { // @CAO: Build this for real! template - bool PtrIsConvertable(FROM * ptr) { (void) ptr; return true; } + bool PtrIsConvertible(FROM * ptr) { (void) ptr; return true; } // emp_assert( (std::is_same() || dynamic_cast(in_ptr)) ); // Debug information provided for each pointer type. @@ -436,12 +441,14 @@ namespace emp { Tracker().IncID(id); } - /// Construct from a raw pointer of campatable type. + /// Construct from a raw pointer of compatable type. template Ptr(T2 * in_ptr, bool track=false) : BasePtr(in_ptr, UNTRACKED_ID) { - if (internal::ptr_debug) std::cout << "raw construct: " << ptr << ". track=" << track << std::endl; - emp_assert( (PtrIsConvertable(in_ptr)) ); + if (internal::ptr_debug) { + std::cout << "raw construct: " << ((void *) ptr) << ". track=" << track << std::endl; + } + emp_assert( (PtrIsConvertible(in_ptr)) ); // If this pointer is already active, link to it. if (Tracker().IsActive(ptr)) { @@ -463,7 +470,7 @@ namespace emp { if (internal::ptr_debug) std::cout << "raw ARRAY construct: " << ptr << ". size=" << array_size << "(" << array_bytes << " bytes); track=" << track << std::endl; - emp_assert( (PtrIsConvertable(_ptr)) ); + emp_assert( (PtrIsConvertible(_ptr)) ); // If this pointer is already active, link to it. if (Tracker().IsActive(ptr)) { @@ -482,7 +489,7 @@ namespace emp { template Ptr(Ptr _in) : BasePtr(_in.Raw(), _in.GetID()) { if (internal::ptr_debug) std::cout << "inexact copy construct: " << ptr << std::endl; - emp_assert( (PtrIsConvertable(_in.Raw())), id ); + emp_assert( (PtrIsConvertible(_in.Raw())), id ); Tracker().IncID(id); } @@ -495,7 +502,7 @@ namespace emp { ~Ptr() { if (internal::ptr_debug) { std::cout << "destructing Ptr instance "; - if (ptr) std::cout << id << " (" << ptr << ")\n"; + if (ptr) std::cout << id << " (" << ((void *) ptr) << ")\n"; else std::cout << "(nullptr)\n"; } Tracker().DecID(id); @@ -591,7 +598,7 @@ namespace emp { /// Delete this pointer (must NOT be an array). void Delete() { emp_assert(ptr, "Trying to delete null Ptr."); - emp_assert(id < Tracker().GetNumIDs(), id, "Trying to delete Ptr that we are not resposible for."); + emp_assert(id < Tracker().GetNumIDs(), id, "Trying to delete Ptr that we are not responsible for."); emp_assert(Tracker().IsArrayID(id) == false, id, "Trying to delete array pointer as non-array."); emp_assert(Tracker().IsActive(ptr), id, "Trying to delete inactive pointer (already deleted!)"); if (internal::ptr_debug) std::cout << "Ptr::Delete() : " << ptr << std::endl; @@ -602,8 +609,8 @@ namespace emp { /// Delete this pointer to an array (must be an array). void DeleteArray() { - emp_assert(id < Tracker().GetNumIDs(), id, "Trying to delete Ptr that we are not resposible for."); emp_assert(ptr, "Trying to delete null Ptr."); + emp_assert(id < Tracker().GetNumIDs(), id, "Trying to delete Ptr that we are not responsible for."); emp_assert(Tracker().IsArrayID(id), id, "Trying to delete non-array pointer as array."); emp_assert(Tracker().IsActive(ptr), id, "Trying to delete inactive pointer (already deleted!)"); if (internal::ptr_debug) std::cout << "Ptr::DeleteArray() : " << ptr << std::endl; @@ -621,7 +628,7 @@ namespace emp { struct hash_t { size_t operator()(const Ptr & t) const noexcept { return t.Hash(); } }; /// Copy assignment - Ptr & operator=(const Ptr & _in) { + Ptr & operator=(const Ptr & _in) & { if (internal::ptr_debug) { std::cout << "copy assignment from id " << _in.id << " to id " << id << std::endl; @@ -642,9 +649,9 @@ namespace emp { /// Assign to a raw pointer of the correct type; if this is already tracked, hooked in /// correctly, otherwise don't track. template - Ptr & operator=(T2 * _in) { + Ptr & operator=(T2 * _in) & { if (internal::ptr_debug) std::cout << "raw assignment" << std::endl; - emp_assert( (PtrIsConvertable(_in)) ); + emp_assert( (PtrIsConvertible(_in)) ); Tracker().DecID(id); // Decrement references to former pointer at this position. ptr = _in; // Update to new pointer. @@ -662,11 +669,11 @@ namespace emp { return *this; } - /// Assign to a convertable Ptr + /// Assign to a convertible Ptr template - Ptr & operator=(Ptr _in) { + Ptr & operator=(Ptr _in) & { if (internal::ptr_debug) std::cout << "convert-copy assignment" << std::endl; - emp_assert( (PtrIsConvertable(_in.Raw())), _in.id ); + emp_assert( (PtrIsConvertible(_in.Raw())), _in.id ); emp_assert(Tracker().IsDeleted(_in.id) == false, _in.id, "Do not copy deleted pointers."); Tracker().DecID(id); ptr = _in.Raw(); @@ -692,42 +699,28 @@ namespace emp { /// Does this const pointer exist? operator bool() const { return ptr != nullptr; } - /// Does this Ptr point to the same memory position? - bool operator==(const Ptr & in_ptr) const { return ptr == in_ptr.ptr; } - - /// Does this Ptr point to different memory positions? - bool operator!=(const Ptr & in_ptr) const { return ptr != in_ptr.ptr; } - - /// Does this Ptr point to a memory position before another? - bool operator<(const Ptr & in_ptr) const { return ptr < in_ptr.ptr; } - - /// Does this Ptr point to a memory position before or equal to another? - bool operator<=(const Ptr & in_ptr) const { return ptr <= in_ptr.ptr; } - - /// Does this Ptr point to a memory position after another? - bool operator>(const Ptr & in_ptr) const { return ptr > in_ptr.ptr; } - - /// Does this Ptr point to a memory position after or equal to another? - bool operator>=(const Ptr & in_ptr) const { return ptr >= in_ptr.ptr; } - - - /// Does this Ptr point to the same memory position as a raw pointer? - bool operator==(const TYPE * in_ptr) const { return ptr == in_ptr; } - - /// Does this Ptr point to different memory positions as a raw pointer? - bool operator!=(const TYPE * in_ptr) const { return ptr != in_ptr; } - - /// Does this Ptr point to a memory position before a raw pointer? - bool operator<(const TYPE * in_ptr) const { return ptr < in_ptr; } - - /// Does this Ptr point to a memory position before or equal to a raw pointer? - bool operator<=(const TYPE * in_ptr) const { return ptr <= in_ptr; } - - /// Does this Ptr point to a memory position after a raw pointer? - bool operator>(const TYPE * in_ptr) const { return ptr > in_ptr; } + template bool operator==(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr == in_ptr.ptr; } + else { return ptr == in_ptr; } + } + template bool operator!=(const T & in_ptr) const { return !operator==(in_ptr); } - /// Does this Ptr point to a memory position after or equal to a raw pointer? - bool operator>=(const TYPE * in_ptr) const { return ptr >= in_ptr; } + template bool operator<(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr < in_ptr.ptr; } + else { return ptr < in_ptr; } + } + template bool operator>(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr > in_ptr.ptr; } + else { return ptr > in_ptr; } + } + template bool operator<=(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr <= in_ptr.ptr; } + else { return ptr <= in_ptr; } + } + template bool operator>=(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr >= in_ptr.ptr; } + else { return ptr >= in_ptr; } + } [[nodiscard]] Ptr operator+(int value) const { return ptr + value; } [[nodiscard]] Ptr operator-(int value) const { return ptr - value; } @@ -884,11 +877,11 @@ namespace emp { struct hash_t { size_t operator()(const Ptr & t) const noexcept { return t.Hash(); } }; // Copy assignments - Ptr & operator=(const Ptr & _in) { ptr = _in.ptr; return *this; } + Ptr & operator=(const Ptr & _in) & { ptr = _in.ptr; return *this; } // Assign to compatible Ptr or raw (non-managed) pointer. - template Ptr & operator=(T2 * _in) { ptr = _in; return *this; } - template Ptr & operator=(Ptr _in) { ptr = _in.Raw(); return *this; } + template Ptr & operator=(T2 * _in) & { ptr = _in; return *this; } + template Ptr & operator=(Ptr _in) & { ptr = _in.Raw(); return *this; } // Auto-cast to raw pointer type. operator TYPE *() { return ptr; } @@ -896,21 +889,28 @@ namespace emp { operator bool() { return ptr != nullptr; } operator bool() const { return ptr != nullptr; } - // Comparisons to other Ptr objects - bool operator==(const Ptr & in_ptr) const { return ptr == in_ptr.ptr; } - bool operator!=(const Ptr & in_ptr) const { return ptr != in_ptr.ptr; } - bool operator<(const Ptr & in_ptr) const { return ptr < in_ptr.ptr; } - bool operator<=(const Ptr & in_ptr) const { return ptr <= in_ptr.ptr; } - bool operator>(const Ptr & in_ptr) const { return ptr > in_ptr.ptr; } - bool operator>=(const Ptr & in_ptr) const { return ptr >= in_ptr.ptr; } - - // Comparisons to raw pointers. - bool operator==(const TYPE * in_ptr) const { return ptr == in_ptr; } - bool operator!=(const TYPE * in_ptr) const { return ptr != in_ptr; } - bool operator<(const TYPE * in_ptr) const { return ptr < in_ptr; } - bool operator<=(const TYPE * in_ptr) const { return ptr <= in_ptr; } - bool operator>(const TYPE * in_ptr) const { return ptr > in_ptr; } - bool operator>=(const TYPE * in_ptr) const { return ptr >= in_ptr; } + template bool operator==(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr == in_ptr.ptr; } + else { return ptr == in_ptr; } + } + template bool operator!=(const T & in_ptr) const { return !operator==(in_ptr); } + + template bool operator<(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr < in_ptr.ptr; } + else { return ptr < in_ptr; } + } + template bool operator>(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr > in_ptr.ptr; } + else { return ptr > in_ptr; } + } + template bool operator<=(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr <= in_ptr.ptr; } + else { return ptr <= in_ptr; } + } + template bool operator>=(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr >= in_ptr.ptr; } + else { return ptr >= in_ptr; } + } [[nodiscard]] Ptr operator+(int value) const { return ptr + value; } [[nodiscard]] Ptr operator-(int value) const { return ptr - value; } @@ -1014,7 +1014,7 @@ namespace emp { /// Fill an array with the provided fill_value. /// If fill_value is a function, repeatedly call function. template - void FillMemory(emp::Ptr mem_ptr, const size_t num_bytes, T fill_value) { + void FillMemory(emp::Ptr mem_ptr, const size_t num_bytes, T fill_value) { // If the fill value is a function, call that function for each memory position. if constexpr (std::is_invocable_v) { FillMemoryFunction(mem_ptr, num_bytes, std::forward(fill_value)); @@ -1060,6 +1060,19 @@ namespace emp { } } + /// Copy an array from the provided memory. + template + void CopyMemory( + emp::Ptr from_ptr, + emp::Ptr to_ptr, + const size_t num_items) + { + constexpr size_t FILL_CHUNK = sizeof(T); + const size_t num_bytes = num_items * FILL_CHUNK; + + std::memcpy(to_ptr.Raw(), from_ptr.Raw(), num_bytes); + } + } // namespace emp #endif // #ifndef EMP_BASE_PTR_HPP_INCLUDE diff --git a/include/emp/base/_assert_macros.hpp b/include/emp/base/_assert_macros.hpp index 9292c4e2ff..a403b06b4c 100644 --- a/include/emp/base/_assert_macros.hpp +++ b/include/emp/base/_assert_macros.hpp @@ -1,11 +1,12 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021. - * - * @file _assert_macros.hpp + * @file * @brief Helper macros for building proper assert commands. - * @note Status: RELEASE + * Status: RELEASE * */ diff --git a/include/emp/base/_assert_trigger.hpp b/include/emp/base/_assert_trigger.hpp index aaf9316ea4..a586af1e3f 100644 --- a/include/emp/base/_assert_trigger.hpp +++ b/include/emp/base/_assert_trigger.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020. - * - * @file _assert_trigger.hpp + * @file * @brief Assert trigger implementation selector. * @note For internal use. */ diff --git a/include/emp/base/_emscripten_assert_trigger.hpp b/include/emp/base/_emscripten_assert_trigger.hpp index 7dcfd867fa..13ed6c6696 100644 --- a/include/emp/base/_emscripten_assert_trigger.hpp +++ b/include/emp/base/_emscripten_assert_trigger.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020. - * - * @file _emscripten_assert_trigger.hpp + * @file * @brief Assert trigger implementation. * @note For internal use. */ @@ -13,12 +14,15 @@ #include #include +#include #include #include #include "_is_streamable.hpp" +#ifndef DOXYGEN_SHOULD_SKIP_THIS + namespace emp { static int TripAssert() { @@ -63,4 +67,6 @@ namespace emp { } // namespace emp +#endif /*DOXYGEN_SHOULD_SKIP_THIS*/ + #endif // #ifndef EMP_BASE__EMSCRIPTEN_ASSERT_TRIGGER_HPP_INCLUDE diff --git a/include/emp/base/_emscripten_error_trigger.hpp b/include/emp/base/_emscripten_error_trigger.hpp index cc32c45873..0fbc9feed3 100644 --- a/include/emp/base/_emscripten_error_trigger.hpp +++ b/include/emp/base/_emscripten_error_trigger.hpp @@ -1,18 +1,20 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021. - * - * @file _emscripten_error_trigger.hpp + * @file * @brief Terminating error trigger implementation. * @note For internal use. - * @TODO Reflect error message to browser (e.g., as an alert) + * @todo Reflect error message to browser (e.g., as an alert) */ #ifndef EMP_BASE__EMSCRIPTEN_ERROR_TRIGGER_HPP_INCLUDE #define EMP_BASE__EMSCRIPTEN_ERROR_TRIGGER_HPP_INCLUDE - +#include +#include #include namespace emp { diff --git a/include/emp/base/_error_trigger.hpp b/include/emp/base/_error_trigger.hpp index 2e3ca72a1c..b0815eb83d 100644 --- a/include/emp/base/_error_trigger.hpp +++ b/include/emp/base/_error_trigger.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021. - * - * @file _error_trigger.hpp + * @file * @brief Error trigger implementation selector * @note For internal use. */ diff --git a/include/emp/base/_is_streamable.hpp b/include/emp/base/_is_streamable.hpp index b4215cca9a..ee5aa908ac 100644 --- a/include/emp/base/_is_streamable.hpp +++ b/include/emp/base/_is_streamable.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020. - * - * @file _is_streamable.hpp + * @file * @brief Test at compile time whether a type can be streamed. * @note This header is for internal use to preserve levelization. * Include meta/type_traits.hpp to use is_streamable. diff --git a/include/emp/base/_native_assert_trigger.hpp b/include/emp/base/_native_assert_trigger.hpp index a52c695ae6..205b767499 100644 --- a/include/emp/base/_native_assert_trigger.hpp +++ b/include/emp/base/_native_assert_trigger.hpp @@ -1,10 +1,11 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020. - * - * @file _native_assert_trigger.hpp - * @brief Native asssert trigger implementation. + * @file + * @brief Native assert trigger implementation. * @note For internal use. */ @@ -13,6 +14,7 @@ #include #include +#include #include #include "_is_streamable.hpp" diff --git a/include/emp/base/_native_error_trigger.hpp b/include/emp/base/_native_error_trigger.hpp index d92305a9bc..0e4951076c 100644 --- a/include/emp/base/_native_error_trigger.hpp +++ b/include/emp/base/_native_error_trigger.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021. - * - * @file _native_error_trigger.hpp + * @file * @brief Terminating error trigger implementation. * @note For internal use. */ @@ -12,6 +13,7 @@ #define EMP_BASE__NATIVE_ERROR_TRIGGER_HPP_INCLUDE +#include #include namespace emp { diff --git a/include/emp/base/_tdebug_assert_trigger.hpp b/include/emp/base/_tdebug_assert_trigger.hpp index 10233817c2..25a82aebc7 100644 --- a/include/emp/base/_tdebug_assert_trigger.hpp +++ b/include/emp/base/_tdebug_assert_trigger.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020. - * - * @file _tdebug_assert_trigger.hpp + * @file * @brief Non-terminating assert trigger implementation for unit testing. * @note For internal use. */ @@ -11,6 +12,7 @@ #ifndef EMP_BASE__TDEBUG_ASSERT_TRIGGER_HPP_INCLUDE #define EMP_BASE__TDEBUG_ASSERT_TRIGGER_HPP_INCLUDE +#include #include namespace emp { diff --git a/include/emp/base/_tdebug_error_trigger.hpp b/include/emp/base/_tdebug_error_trigger.hpp index 3c174e60c5..b882cbaafe 100644 --- a/include/emp/base/_tdebug_error_trigger.hpp +++ b/include/emp/base/_tdebug_error_trigger.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021. - * - * @file _tdebug_error_trigger.hpp + * @file * @brief Non-terminating error trigger implementation for unit testing. * @note For internal use. */ @@ -12,6 +13,7 @@ #define EMP_BASE__TDEBUG_ERROR_TRIGGER_HPP_INCLUDE +#include #include namespace emp { diff --git a/include/emp/base/always_assert.hpp b/include/emp/base/always_assert.hpp index 96dde4422b..e5ec365210 100644 --- a/include/emp/base/always_assert.hpp +++ b/include/emp/base/always_assert.hpp @@ -1,11 +1,12 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020-2021. - * - * @file always_assert.hpp + * @file * @brief A more dynamic replacement for standard library asserts. - * @note Status: RELEASE + * Status: RELEASE * * A replacement for the system-level assert.h, called "emp_always_assert" * Added functionality: @@ -21,7 +22,7 @@ * int a = 6; * emp_always_assert(a==5, a); * - * Unlinke "emp_assert", "emp_always_assert" will trigger an assertion error + * Unlike "emp_assert", "emp_always_assert" will trigger an assertion error * whether compiled in debug mode or not. * */ diff --git a/include/emp/base/always_assert_warning.hpp b/include/emp/base/always_assert_warning.hpp index a80bb1e83e..2e24bb4ff2 100644 --- a/include/emp/base/always_assert_warning.hpp +++ b/include/emp/base/always_assert_warning.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020. - * - * @file always_assert_warning.hpp + * @file * @brief A more dynamic replacement for standard library asserts. * @note Status: RELEASE * @@ -20,7 +21,7 @@ * int a = 6; * emp_always_assert(a==5, a); * - * Unlinke "emp_assert_warning", "emp_always_assert_warning" will trigger an + * Unlike "emp_assert_warning", "emp_always_assert_warning" will trigger an * assertion error whether compiled in debug mode or not. */ diff --git a/include/emp/base/array.hpp b/include/emp/base/array.hpp index 054fd0300f..6659165d76 100644 --- a/include/emp/base/array.hpp +++ b/include/emp/base/array.hpp @@ -1,11 +1,12 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2022. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021. - * - * @file array.hpp + * @file * @brief A drop-in wrapper for std::array; adds on bounds checking in debug mode. - * @note Status: RELEASE + * Status: RELEASE * * If EMP_NDEBUG is set, emp::array is just an alias for std::array. * Otherwise, every time an array is accessed, tests are done to make sure that the @@ -20,8 +21,12 @@ #include #include +#include +#include #include +#include "../../../third-party/cereal/include/cereal/cereal.hpp" + #include "assert.hpp" #ifdef EMP_NDEBUG @@ -35,130 +40,139 @@ namespace emp { namespace emp { + // Pre-declaration of array type. + template struct array; + + /// Setup an iterator wrapper to check validity. + template + struct array_iterator { + using this_t = array_iterator; + using array_t = ARRAY_T; + + // Iterator traits + using iterator_category = typename std::iterator_traits::iterator_category; + using value_type = typename std::iterator_traits::value_type; + using difference_type = typename std::iterator_traits::difference_type; + using pointer = typename std::iterator_traits::pointer; + using reference = typename std::iterator_traits::reference; + + ITERATOR_T it; + const array_t * arr_ptr { nullptr }; // Which array was iterator created from? + + array_iterator() { ; } + + array_iterator(ITERATOR_T _in, const array_t * _v) : it(_in), arr_ptr (_v) { ; } + array_iterator(const this_t &) = default; + array_iterator(this_t &&) = default; + ~array_iterator() { ; } + + // Debug tools to make sure this iterator is okay. + bool OK(bool begin_ok=true, bool end_ok=true) const { + if (arr_ptr == nullptr) return false; // Invalid array + if (it < arr_ptr->begin()) return false; // Iterator before array start. + if (it > arr_ptr->end()) return false; // Iterator after array end. + if (!begin_ok && it == arr_ptr->begin()) return false; // Iterator not allowed at start. + if (!end_ok && it == arr_ptr->end()) return false; // Iterator not allowed at end. + return true; + } + + this_t & operator=(const this_t &) = default; + this_t & operator=(this_t &&) = default; + + operator ITERATOR_T() { return it; } + operator const ITERATOR_T() const { return it; } + + auto & operator*() { + emp_assert(OK(true, false)); // Ensure array is being pointed to properly. + return *it; + } + const auto & operator*() const { + emp_assert(OK(true, false)); // Ensure array is being pointed to properly. + return *it; + } + + auto operator->() { + emp_assert(OK(true, false)); // Ensure array is being pointed to properly. + return it; + } + auto operator->() const { + emp_assert(OK(true, false)); // Ensure array is being pointed to properly. + return it; + } + + this_t & operator++() { emp_assert(OK(true,false)); ++it; return *this; } + this_t operator++(int /*x*/) { emp_assert(OK(true,false)); return this_t(it++, arr_ptr); } + this_t & operator--() { emp_assert(OK(false,true)); --it; return *this; } + this_t operator--(int /*x*/) { emp_assert(OK(false,true)); return this_t(it--, arr_ptr); } + + this_t operator+(int in) { emp_assert(OK()); return this_t(it + in, arr_ptr); } + this_t operator-(int in) { emp_assert(OK()); return this_t(it - in, arr_ptr); } + ptrdiff_t operator-(const this_t & in) { emp_assert(OK()); return it - in.it; } + + this_t & operator+=(int in) { emp_assert(OK()); it += in; return *this; } + this_t & operator-=(int in) { emp_assert(OK()); it -= in; return *this; } + + auto & operator[](int index) { emp_assert(OK()); return it[index]; } + const auto & operator[](int index) const { emp_assert(OK()); return it[index]; } + }; + /// We are in debug mode, so emp::array has the same interface as std::array, but with extra /// bounds checking. Using vector as our base since it has the right pieces and is dynamic. - template - class array : public std::vector { - private: + template + struct array { + static constexpr size_t N = NUM_ELEMENTS; using this_t = emp::array; - using base_t = std::vector; - - public: - bool valid; - - /// Setup an iterator wrapper to make sure that they're valid. - template - struct iterator_wrapper : public ITERATOR_T { - using this_t = iterator_wrapper; - using wrapped_t = ITERATOR_T; - using vec_t = emp::array; - - /// What vector was this iterator created from? - const vec_t * v_ptr{ nullptr }; - - iterator_wrapper() { ; } - - iterator_wrapper(const ITERATOR_T & _in, const vec_t * _v) : ITERATOR_T(_in), v_ptr(_v) { ; } - iterator_wrapper(const this_t &) = default; - iterator_wrapper(this_t &&) = default; - ~iterator_wrapper() { ; } - - // Debug tools to make sure this iterator is okay. - bool OK(bool begin_ok=true, bool end_ok=true) const { - if (v_ptr == nullptr) return false; // Invalid vector - if (!v_ptr->valid) return false; // Vector has been deleted! - size_t pos = (size_t) (*this - v_ptr->begin()); - if (pos > v_ptr->size()) return false; // Iterator out of range. - if (!begin_ok && pos == 0) return false; // Iterator not allowed at beginning. - if (!end_ok && pos == v_ptr->size()) return false; // Iterator not allowed at end. - return true; - } - - this_t & operator=(const this_t &) = default; - this_t & operator=(this_t &&) = default; - - operator ITERATOR_T() { return *this; } - operator const ITERATOR_T() const { return *this; } - - auto & operator*() { - emp_assert(OK(true, false)); // Ensure array is being pointed to properly. - return wrapped_t::operator*(); - } - const auto & operator*() const { - emp_assert(OK(true, false)); // Ensure array is being pointed to properly. - return wrapped_t::operator*(); - } - - auto operator->() { - emp_assert(OK(true, false)); // Ensure array is being pointed to properly. - return wrapped_t::operator->(); - } - auto operator->() const { - emp_assert(OK(true, false)); // Ensure array is being pointed to properly. - return wrapped_t::operator->(); - } - - this_t & operator++() { emp_assert(OK(true,false)); wrapped_t::operator++(); return *this; } - this_t operator++(int x) { emp_assert(OK(true,false)); return this_t(wrapped_t::operator++(x), v_ptr); } - this_t & operator--() { emp_assert(OK(false,true)); wrapped_t::operator--(); return *this; } - this_t operator--(int x) { emp_assert(OK(false,true)); return this_t(wrapped_t::operator--(x), v_ptr); } - - auto operator+(int in) { emp_assert(OK()); return this_t(wrapped_t::operator+(in), v_ptr); } - auto operator-(int in) { emp_assert(OK()); return this_t(wrapped_t::operator-(in), v_ptr); } - auto operator-(const this_t & in) { emp_assert(OK()); return ((wrapped_t) *this) - (wrapped_t) in; } - - this_t & operator+=(int in) { emp_assert(OK()); wrapped_t::operator+=(in); return *this; } - this_t & operator-=(int in) { emp_assert(OK()); wrapped_t::operator-=(in); return *this; } - auto & operator[](int offset) { emp_assert(OK()); return wrapped_t::operator[](offset); } - }; - - using iterator = iterator_wrapper< typename base_t::iterator >; - using const_iterator = iterator_wrapper< typename base_t::const_iterator >; - using reverse_iterator = iterator_wrapper< typename base_t::reverse_iterator >; - using const_reverse_iterator = iterator_wrapper< typename base_t::const_reverse_iterator >; + + T _data[ N ? N : 1 ]; + + using iterator = array_iterator< T*, this_t >; + using const_iterator = array_iterator< const T *, this_t >; + using reverse_iterator = array_iterator< std::reverse_iterator, this_t >; + using const_reverse_iterator = array_iterator< std::reverse_iterator, this_t >; using value_type = T; - using size_type = typename base_t::size_type; - using reference = typename base_t::reference; - using const_reference = typename base_t::const_reference; + using size_type = std::size_t; + using reference = value_type&; + using const_reference = const value_type&; - array() : base_t(N), valid(true) {}; - array(const this_t & _in) : base_t(_in), valid(true) { emp_assert(_in.size() == N); }; - array(std::initializer_list in_list) : base_t(in_list), valid(true) { emp_assert(size() == N); } - template - array(InputIt first, InputIt last) : base_t(first, last), valid(true) { emp_assert(size() == N); } - ~array() { valid=false; } // No longer valid when array is deleted. + // -- No constructors, destructors, or assignment operators to preserve aggregate type. + int operator<=>(const array &) const = default; + + // Allow automatic conversion to regular array type. operator std::array() { std::array ar; - for (size_t i = 0; i < N; i++) ar[i] = base_t::operator[](i); + for (size_t i = 0; i < N; i++) ar[i] = _data[i]; return ar; } constexpr size_t size() const { return N; } - iterator begin() noexcept { return iterator(base_t::begin(), this); } - const_iterator begin() const noexcept { return const_iterator(base_t::begin(), this); } - iterator end() noexcept { return iterator(base_t::end(), this); } - const_iterator end() const noexcept { return const_iterator(base_t::end(), this); } + auto & data() { return _data; } + const auto & data() const { return _data; } - this_t & operator=(const this_t &) = default; + iterator begin() noexcept { return iterator(_data, this); } + const_iterator begin() const noexcept { return const_iterator(_data, this); } + iterator end() noexcept { return iterator(_data + N, this); } + const_iterator end() const noexcept { return const_iterator(_data + N, this); } T & operator[](size_t pos) { emp_assert(pos < N, pos, N); - return base_t::operator[](pos); + return _data[pos]; } const T & operator[](size_t pos) const { emp_assert(pos < N, pos, N); - return base_t::operator[](pos); + return _data[pos]; } - T & back() { emp_assert(N > 0); return base_t::back(); } - const T & back() const { emp_assert(N > 0); return base_t::back(); } - T & front() { emp_assert(N > 0); return base_t::front(); } - const T & front() const { emp_assert(N > 0); return base_t::front(); } + T & back() { emp_assert(N > 0); return _data[N-1]; } + const T & back() const { emp_assert(N > 0); return _data[N-1]; } + T & front() { emp_assert(N > 0); return _data[0]; } + const T & front() const { emp_assert(N > 0); return _data[0]; } - void fill(const T & val) { this->assign(N, val); } + void fill(const T & val) { + for (size_t i = 0; i < N; ++i) _data[i] = val; + } // Functions to make sure to throw an error on: @@ -170,27 +184,30 @@ namespace emp { void pop_back() { emp_assert(false, "invalid operation for array!"); } template - iterator insert(ARGS &&... args) { + iterator insert(ARGS &&... /* args */) { emp_assert(false, "invalid operation for array!"); - return iterator( base_t::insert(std::forward(args)...), this ); + return end(); } template - iterator erase(ARGS &&... args) { + iterator erase(ARGS &&... /* args */) { emp_assert(false, "invalid operation for array!"); - return iterator( base_t::erase(std::forward(args)...), this ); + return end(); } template - iterator emplace(ARGS &&... args) { + iterator emplace(ARGS &&... /* args */) { emp_assert(false, "invalid operation for array!"); - return iterator( base_t::emplace(std::forward(args)...), this ); + return end(); } template void emplace_back(ARGS &&... /* args */) { emp_assert(false, "invalid operation for array!"); } + + template + void serialize( Archive & ar ) { ar(_data); } }; @@ -203,7 +220,7 @@ struct std::tuple_size> : public integral_constant { #endif // NDEBUG off - +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace std { // A crude, generic printing function for arrays. template @@ -217,7 +234,7 @@ namespace std { for (T & x : v) is >> x; return is; } - + #endif // DOXYGEN_SHOULD_SKIP_THIS } #endif // #ifndef EMP_BASE_ARRAY_HPP_INCLUDE diff --git a/include/emp/base/assert.hpp b/include/emp/base/assert.hpp index 2a6a8f5e0c..73e1e92bec 100644 --- a/include/emp/base/assert.hpp +++ b/include/emp/base/assert.hpp @@ -1,11 +1,12 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2020. - * - * @file assert.hpp + * @file * @brief A more dynamic replacement for standard library asserts. - * @note Status: RELEASE + * Status: RELEASE * * A replacement for the system-level assert.h, called "emp_assert" * Added functionality: @@ -43,6 +44,10 @@ // #define emp_assert(EXPR) ((void) sizeof(EXPR) ) // #define emp_assert(EXPR, ...) { constexpr bool __emp_assert_tmp = false && (EXPR); (void) __emp_assert_tmp; } + namespace emp { + static constexpr bool is_debug_mode = false; + } + #else /// Require a specified condition to be true. If it is false, immediately /// halt execution. Print also extra information on any variables or @@ -51,6 +56,10 @@ /// information will not be printed when compiling with MSVC. #define emp_assert(...) emp_always_assert(__VA_ARGS__) + namespace emp { + static constexpr bool is_debug_mode = true; + } + #endif diff --git a/include/emp/base/assert_warning.hpp b/include/emp/base/assert_warning.hpp index bdc7363b07..92935f65f5 100644 --- a/include/emp/base/assert_warning.hpp +++ b/include/emp/base/assert_warning.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020. - * - * @file assert_warning.hpp + * @file * @brief A non-terminating replacement for standard library asserts. * * A supplement for the system-level assert.h, called "emp_assert_warning" diff --git a/include/emp/base/emscripten_assert.hpp b/include/emp/base/emscripten_assert.hpp index a0cfd21048..6f985b42ae 100644 --- a/include/emp/base/emscripten_assert.hpp +++ b/include/emp/base/emscripten_assert.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020. - * - * @file emscripten_assert.hpp + * @file * @brief Assert evaluated only in debug mode with Emscripten. */ @@ -19,6 +20,9 @@ /// emp_emscripten_assert() will not do anything. #define emp_emscripten_assert(...) emp_assert(__VA_ARGS__) #else + /// Require a specified condition to be true if this program was compiled to + /// Javascript with Emscripten. Note: If NDEBUG is defined, + /// emp_emscripten_assert() will not do anything. #define emp_emscripten_assert(...) #endif diff --git a/include/emp/base/error.hpp b/include/emp/base/error.hpp index e2d62f8682..d5ba39fc32 100644 --- a/include/emp/base/error.hpp +++ b/include/emp/base/error.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021. - * - * @file error.hpp + * @file * @brief Nearly-universal error, to use in place of emp_assert(false, ...). * Aborts program in both debug and release mode, but does NOT terminate in TDEBUG for testing. */ diff --git a/include/emp/base/errors.hpp b/include/emp/base/errors.hpp index f4ab4e8461..1fa82ff0f7 100644 --- a/include/emp/base/errors.hpp +++ b/include/emp/base/errors.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file errors.hpp + * @file * @brief Tools to help manage various problems in command-line or Emscripten-based applications. * @note Status: ALPHA * @@ -42,9 +43,10 @@ #include #include #include +#include #include -/// If we are in emscripten, make sure to include the header. +// If we are in emscripten, make sure to include the header. #ifdef __EMSCRIPTEN__ #include #endif diff --git a/include/emp/base/map.hpp b/include/emp/base/map.hpp index 7d58125a8d..9c76201b0b 100644 --- a/include/emp/base/map.hpp +++ b/include/emp/base/map.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019-2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019-2020. - * - * @file map.hpp + * @file * @brief A drop-in wrapper for std::map and std:multimap; makes sure we create vars on access. * @note Status: ALPHA * @@ -19,6 +20,7 @@ #include #include +#include #include "assert.hpp" #include "MapProxy.hpp" diff --git a/include/emp/base/notify.hpp b/include/emp/base/notify.hpp new file mode 100644 index 0000000000..a795a66e17 --- /dev/null +++ b/include/emp/base/notify.hpp @@ -0,0 +1,473 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021-2022 +*/ +/** + * @file + * @brief Tools to alert users of messages (including errors and warnings) in a consistant manner. + * @note Status: ALPHA + * + * + * There are a handful of notification types to consider: + * - Message: A simple notification. + * - Verbose: Optional messages that can be activated by category. + * - Warning: Something looks suspicious, but is not technically a problem (don't exit) + * - Error: Something has gone horribly wrong and is impossible to recover from (exit) + * - Exception: Something didn't go the way we expected, but we can still recover (exit if not handled) + * - Debug: A simple notification that should only be printed when NDEBUG is not set (don't exit) + * + * Messages default to "standard out"; all of the other default to "standard error". Handling of + * these notifications can all be overriden by either whole category or by specific tag. + * + * There are three possible recipients for all errors/warnings. + * - The end-user if the problem stems from inputs they provided to the executable. + * - The library user if the problem is due to mis-use of library functionality. + * - The library developers if something that should be impossible occurs. + * + * The content of this file primarily targets the first group; developers should prefer asserts + * to ensure that supposedly "impossible" situations do not occur. + * + * NOTES: + * - Whenever possible, exceptions should be preferred. They are more specific than warnings + * and can be responded to rather than automatically halting execution like errors. + * - Warnings should always detail what should be done differently to surpress that warning. + * + */ + +#ifndef EMP_BASE_NOTIFY_HPP_INCLUDE +#define EMP_BASE_NOTIFY_HPP_INCLUDE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vector.hpp" + +namespace emp { +namespace notify { + using id_t = std::string; + using message_t = std::string; + using except_data_t = std::any; + + using id_arg_t = const id_t &; + using message_arg_t = const message_t &; + using response_t = bool(id_arg_t, message_arg_t, except_data_t); + using exit_fun_t = std::function; + + /// Information about an exception that has occurred. + struct ExceptInfo { + id_t id = "__NONE__"; ///< Which exception was triggered? + message_t message = ""; ///< A detailed message of this exception. + except_data_t data; ///< Extra data needed to resolve this exception. + }; + + enum class Type { MESSAGE=0, DEBUG, WARNING, ERROR, EXCEPTION, NUM_TYPES }; + static constexpr size_t num_types = static_cast(Type::NUM_TYPES); + + /// Convert a type to a human-readable string. + static id_t TypeID(Type type) { + switch (type) { + case Type::MESSAGE: return "Message"; + case Type::DEBUG: return "Debug"; + case Type::WARNING: return "WARNING"; + case Type::ERROR: return "ERROR"; + case Type::EXCEPTION: return "EXCEPTION"; + default: return "Unknown"; + } + } + + /// Convert a type to a human-readable string in COLOR. + static id_t ColorTypeID(Type type) { + const std::string green_text = "\033[32m"; + const std::string magenta_text = "\033[35m"; + const std::string red_text = "\033[31m"; + const std::string yellow_text = "\033[33m"; + const std::string normal_text = "\033[39m"; + const std::string bold_text = "\033[1m"; + const std::string no_bold_text = "\033[22m"; + switch (type) { + case Type::MESSAGE: return green_text + "Message" + normal_text; + case Type::DEBUG: return green_text + bold_text + "Debug" + no_bold_text + normal_text; + case Type::WARNING: return yellow_text + bold_text + "WARNING" + no_bold_text + normal_text; + case Type::ERROR: return red_text + bold_text + "ERROR" + no_bold_text + normal_text; + case Type::EXCEPTION: return magenta_text + bold_text + "EXCEPTION" + no_bold_text + normal_text; + default: return "Unknown"; + } + } + + // Maintain a specified collection of handlers. + class HandlerSet { + private: + using fun_t = std::function; + using fun_no_data_t = std::function; + using fun_msg_only_t = std::function; + emp::vector handlers; + bool exit_on_fail = false; + + public: + HandlerSet() {} + HandlerSet(const HandlerSet &) = default; + HandlerSet(HandlerSet &&) = default; + ~HandlerSet() { } + + bool GetExitOnFail() const { return exit_on_fail; } + HandlerSet & SetExitOnFail(bool _exit=true) { + exit_on_fail = _exit; + return *this; + } + + /// Trigger all handlers associated with a given ID. + bool Trigger(id_arg_t id, message_arg_t message, except_data_t except_data) { + // Run handlers from most recently added to oldest. + for (auto it = handlers.rbegin(); + it != handlers.rend(); + ++it) { + // Run until "true" result + bool result = (*it)(id, message, except_data); + if (result) return true; // Stop if any handler succeeded. + } + + return false; + } + + // Trigger without providing data. + bool Trigger(id_arg_t id, message_arg_t message) { + return Trigger(id, message, 0); + } + + // Trigger from a stored notification. + bool Trigger(const ExceptInfo & info) { + return Trigger(info.id, info.message, info.data); + } + + // Add a function to this set. + HandlerSet & Add(fun_t in) { handlers.push_back(in); return *this; } + + // Add a function with no data. + HandlerSet & Add(fun_no_data_t in) { + handlers.push_back( + [fun=in](id_arg_t id, message_arg_t msg, except_data_t){ return fun(id,msg); } + ); + return *this; + } + + // Add a function with only a single message + HandlerSet & Add(fun_msg_only_t in) { + handlers.push_back( + [fun=in](id_arg_t, message_arg_t msg, except_data_t){ return fun(msg); } + ); + return *this; + } + + + // Clear all handlers associated with a given id. + HandlerSet & Clear() { handlers.resize(0); return *this; } + + /// Replace all handlers with nothing (i.e., clear them) + void Replace() { Clear(); } + + /// Replace all handlers with the generic ones provided. + template + void Replace(fun_t in, FUN_Ts... extra) { + Replace(extra...); + Add(in); + } + }; + + /// Staticly stored data about current notifications. + struct NotifyData { + // For each exception name we will keep a vector of handlers, appended to in the order + // that they arrive (most recent will be last) + std::unordered_map handler_map; // Map of all handlers to use for notifications. + std::unordered_map verbose_map; // Set of categories for verbose messages. + emp::vector exit_funs; // Set of handlers to run on exit. + emp::vector except_queue; // Unresolved exceptions after handlers have run + emp::vector pause_queue; // Unresolved notifications during pause + bool lethal_exceptions = true; // Should unresolved exceptions end the program? + bool is_paused = false; // When paused, save notifications until unpaused. + + HandlerSet & GetHandler(Type type) { return handler_map[TypeID(type)]; } + + NotifyData() { + // Setup the default handlers and exit rules. + GetHandler(Type::MESSAGE).Add( + [](id_arg_t, message_arg_t msg) { + std::cout << msg << std::endl; + return true; + } + ); + + GetHandler(Type::DEBUG).Add( +#ifdef NDEBUG + [](id_arg_t, message_arg_t){ return true; } +#else + [](id_arg_t, message_arg_t msg) { + const std::string tag = ColorTypeID(Type::DEBUG); + std::cout << tag << ": " << msg << std::endl; + return true; + } +#endif + ); + + GetHandler(Type::WARNING).Add( + [](id_arg_t, message_arg_t msg) { + const std::string tag = ColorTypeID(Type::WARNING); + std::cout << tag << ": " << msg << std::endl; + return true; // Only warning, do not exit. + } + ); + + GetHandler(Type::ERROR).Add( + [](id_arg_t, message_arg_t msg) { + const std::string tag = ColorTypeID(Type::ERROR); + std::cout << tag << ": " << msg << std::endl; + return false; // Does not correct the problem, so exit. + } + ); + + GetHandler(Type::EXCEPTION).Add( + [](id_arg_t id, message_arg_t msg) { + const std::string tag = ColorTypeID(Type::EXCEPTION); + std::cerr << tag << " (" << id << "): " << msg << std::endl; + return false; // Does not correct the problem, so exit. + } + ); + GetHandler(Type::EXCEPTION).SetExitOnFail(); + + // The initial exit handler should actually exit, using the appropriate exit code. + exit_funs.push_back( [](int code){ exit(code); } ); + } + }; + + /// Central call to obtain NotifyData singleton. + static NotifyData & GetData() { static NotifyData data; return data; } + inline auto & MessageHandlers() { return GetData().GetHandler(Type::MESSAGE); } + inline auto & DebugHandlers() { return GetData().GetHandler(Type::DEBUG); } + inline auto & WarningHandlers() { return GetData().GetHandler(Type::WARNING); } + inline auto & ErrorHandlers() { return GetData().GetHandler(Type::ERROR); } + + [[maybe_unused]] static void AddExitHandler(exit_fun_t fun) { GetData().exit_funs.push_back(fun); } + [[maybe_unused]] static void ClearExitHandlers() { GetData().exit_funs.resize(0); } + [[maybe_unused]] static void ReplaceExitHandlers() { ClearExitHandlers(); } + template + static void ReplaceExitHandlers(exit_fun_t fun, FUN_Ts... extras) { + ReplaceExitHandlers(extras...); + AddExitHandler(fun); + } + + /// Generic exit handler that calls all of the provided functions. + [[maybe_unused]] static void Exit(int exit_code) { + NotifyData & data = GetData(); + + // Run any cleanup functions. + for (auto it = data.exit_funs.rbegin(); it != data.exit_funs.rend(); ++it) { + (*it)(exit_code); + } + + // Exit for real. + exit(exit_code); + } + + /// Generic Notification where type must be specified. + template + static bool Notify(Type type, Ts... args) { + NotifyData & data = GetData(); + const id_t id = TypeID(type); + + // Setup the message in a string stream. + std::stringstream ss; + ((ss << std::forward(args)), ...); + + // If we are are paused, save this notification for later. + if (data.is_paused) { + data.pause_queue.push_back(ExceptInfo{id, ss.str(), 0}); + return true; + } + + bool result = data.handler_map[id].Trigger(id, ss.str()); + + // And return the success result. + return result; + } + + [[maybe_unused]] static void Pause() { + NotifyData & data = GetData(); + data.is_paused = true; + } + + [[maybe_unused]] static void Unpause() { + NotifyData & data = GetData(); + + // Step through the notifications that have accrued. + for (size_t i = 0; i < data.pause_queue.size(); ++i) { + auto & notice = data.pause_queue[i]; + bool result = data.handler_map[notice.id].Trigger(notice); + if (!result) { // Failed; move to exception queue or exit if error. + if (notice.id == "ERROR") Exit(1); + data.except_queue.push_back(notice); + } + } + + data.pause_queue.resize(0); // Clear out the queue. + + data.is_paused = false; + } + + + /// Send out a regular notification. + template + static bool Message(Ts... args) { return Notify(Type::MESSAGE, std::forward(args)...); } + + /// Send out a DEBUG notification. + template + static bool Debug(Ts... args) { return Notify(Type::DEBUG, std::forward(args)...); } + + /// Send out a notification of a WARNING. + template + static bool Warning(Ts... args) { return Notify(Type::WARNING, std::forward(args)...); } + + /// Send out a notification of an ERROR. + template + static bool Error(Ts... args) { + bool success = Notify(Type::ERROR, std::forward(args)...); + if (!success) { +#ifdef NDEBUG + Exit(1); +#else + abort(); +#endif + } + return success; + } + + // Trigger a warning only if a specified condition is true. + template + static bool TestWarning(bool test, Ts... args) { + if (test) return Warning(std::forward(args)...); + return true; + } + + // Trigger an error only if a specified condition is true. + template + static bool TestError(bool test, Ts... args) { + if (test) return Error(std::forward(args)...); + return true; + } + + + /// Add a handler for a particular exception type. + template + static HandlerSet & AddHandler(id_arg_t id, FUN_T fun) { + return GetData().handler_map[id].Add(fun); + } + + /// Add a generic exception handler. + template + static HandlerSet & AddHandler(FUN_T fun) { + return GetData().handler_map["EXCEPTION"].Add(fun); + } + + /// Ignore exceptions of a specific type. + [[maybe_unused]] static HandlerSet & Ignore(id_arg_t id) { + return AddHandler(id, [](id_arg_t, message_arg_t){ return true; }); + } + + /// Turn on a particular verbosity category. + [[maybe_unused]] static void SetVerbose(std::string id, bool make_active=true) { + GetData().verbose_map[id] = make_active; + } + + /// Send out a notification of an "verbose" message. + template + [[maybe_unused]] static bool Verbose(const std::string & id, Ts... args) { + NotifyData & data = GetData(); + + if (data.verbose_map[id]) { + return Notify(Type::MESSAGE, std::forward(args)...); + } + + return false; + } + + /// Send out a notification of an Exception. + [[maybe_unused]] static bool Exception(id_arg_t id, message_arg_t message="", except_data_t except_data=0) { + NotifyData & data = GetData(); + + if (data.is_paused) { + data.pause_queue.push_back(ExceptInfo{id, message, except_data}); + return true; + } + + // Retrieve any specialized exception handlers for this type of exception. + bool result = data.handler_map[id].Trigger(id, message, except_data); + + // If unresolved, see if we should quit; else use a generic exception handler. + if (!result) { + if (data.handler_map[id].GetExitOnFail()) Exit(1); + result = data.handler_map["EXCEPTION"].Trigger(id, message, except_data); + } + + // If still unresolved, either give up or save the exception for later analysis. + if (!result) { + if (data.handler_map["EXCEPTION"].GetExitOnFail()) Exit(1); + data.except_queue.push_back(ExceptInfo{id, message, except_data}); + } + + return result; + } + + /// Retrieve a vector of ALL unresolved exceptions. + [[maybe_unused]] static const emp::vector & GetExceptions() { return GetData().except_queue; } + + /// Retrieve the first unresolved exception with a given id. + [[maybe_unused]] static ExceptInfo GetException(id_arg_t id) { + for (ExceptInfo & x : GetData().except_queue) if (x.id == id) return x; + return ExceptInfo{}; + } + + /// Return a total count of how many unresolved exceptions are left. + [[maybe_unused]] static size_t CountExceptions() { return GetData().except_queue.size(); } + + /// Return a total count of how many unresolved exceptions have a given id. + [[maybe_unused]] static size_t CountExceptions(id_arg_t id) { + size_t count = 0; + for (ExceptInfo & x : GetData().except_queue) if (x.id == id) ++count; + return count; + } + + /// Identify whether there are ANY unresolved exceptions. + [[maybe_unused]] static bool HasExceptions() { return CountExceptions(); } + + /// Identify whether there are any unresolved exceptions with a given id. + [[maybe_unused]] static bool HasException(id_arg_t id) { + for (ExceptInfo & x : GetData().except_queue) if (x.id == id) return true; + return false; + } + + /// Remove all unresolved exceptions. + [[maybe_unused]] static void ClearExceptions() { GetData().except_queue.resize(0); } + + /// Remove first exception with a given id. + [[maybe_unused]] static void ClearException(id_arg_t id) { + auto & except_queue = GetData().except_queue; + for (size_t i = 0; i < except_queue.size(); ++i) { + if (except_queue[i].id == id) { + // If exception is NOT in the last position, move last position earlier and reduce size. + if (i < except_queue.size() - 1) except_queue[i] = except_queue.back(); + except_queue.resize(except_queue.size() - 1); + return; + } + } + } + +} +} + + +#endif // #ifndef EMP_BASE_NOTIFY_HPP_INCLUDE diff --git a/include/emp/base/optional.hpp b/include/emp/base/optional.hpp index e0902fb6ed..8592aab555 100644 --- a/include/emp/base/optional.hpp +++ b/include/emp/base/optional.hpp @@ -1,11 +1,12 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020. - * - * @file optional.hpp + * @file * @brief Audited implementation of std::optional. - * @note Status: RELEASE + * Status: RELEASE * * Drop-in replacements for std::optional. * In debug mode, operator * and operator-> value accesses are checked for undefined behavior. @@ -15,6 +16,7 @@ #define EMP_BASE_OPTIONAL_HPP_INCLUDE #include +#include #include #include @@ -99,6 +101,7 @@ namespace emp { } // namespace emp +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace std { template @@ -109,7 +112,7 @@ namespace std { }; } // namespace std - +#endif // DOXYGEN_SHOULD_SKIP_THIS #endif #endif // #ifndef EMP_BASE_OPTIONAL_HPP_INCLUDE diff --git a/include/emp/base/unordered_map.hpp b/include/emp/base/unordered_map.hpp index 9b4c4c664a..24142893c4 100644 --- a/include/emp/base/unordered_map.hpp +++ b/include/emp/base/unordered_map.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019-2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019-2020. - * - * @file unordered_map.hpp + * @file * @brief A drop-in wrapper for std::unordered_map and unordered_multi_map; makes sure we create vars on access. * @note Status: ALPHA */ @@ -12,6 +13,7 @@ #define EMP_BASE_UNORDERED_MAP_HPP_INCLUDE #include +#include #include #include "assert.hpp" diff --git a/include/emp/base/vector.hpp b/include/emp/base/vector.hpp index 6fa7ec5aef..c131da6993 100644 --- a/include/emp/base/vector.hpp +++ b/include/emp/base/vector.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2019. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2019. - * - * @file vector.hpp + * @file * @brief A drop-in wrapper for std::vector; adds on bounds checking in debug mode. * @note Status: BETA * @@ -22,6 +23,7 @@ #include #include +#include #include #include @@ -29,7 +31,7 @@ #ifdef EMP_NDEBUG -// Seemlessly translate emp::vector to std::vector +// Seamlessly translate emp::vector to std::vector namespace emp { template using vector = std::vector; } @@ -76,11 +78,23 @@ namespace emp { // Debug tools to make sure this iterator is okay. static std::string & ErrorCode() { static std::string code="No Errors Found."; return code; } + static std::string ErrorStart() { + std::string vec_type = std::string("vector<") + typeid(typename stdv_t::value_type).name() + ">"; + std::string it_type = typeid(ITERATOR_T).name(); + if constexpr (std::is_same()) it_type = "iterator"; + if constexpr (std::is_same()) it_type = "const_iterator"; + if constexpr (std::is_same()) it_type = "reverse_iterator"; + if constexpr (std::is_same()) it_type = "const_reverse_iterator"; + return std::string("Iterator (type = '") + vec_type + "::" + it_type + "') "; + } + + bool OK(bool begin_ok=true, bool end_ok=true, std::string op="") const { + std::string type_name = typeid(ITERATOR_T).name();; - bool OK(bool begin_ok=true, bool end_ok=true) const { if (v_ptr == nullptr) { ErrorCode() = "Invalid Vector! (set to nullptr)"; return false; } if (v_ptr->revision == 0) { ErrorCode() = "Vector deleted! (revision==0)"; return false; } - if (revision != v_ptr->revision) { ErrorCode() = "Vector has changed memeory!"; return false; } + if (revision != v_ptr->revision) { ErrorCode() = "Vector has changed memory!"; return false; } + int64_t pos = 0; if constexpr (std::is_same() || std::is_same()) { @@ -92,58 +106,63 @@ namespace emp { pos = *((ITERATOR_T *) this) - ((stdv_t *) v_ptr)->begin(); } if (pos < 0 || ((size_t) pos) > v_ptr->size()) { - ErrorCode() = "Iterator out of range."; - ErrorCode() += " size="; - ErrorCode() += std::to_string(v_ptr->size()); - ErrorCode() += " pos="; - ErrorCode() += std::to_string(pos); + ErrorCode() = ErrorStart() + "out of range." + + " size=" + std::to_string(v_ptr->size()) + " pos=" + std::to_string(pos); + return false; + } + if (!begin_ok && pos == 0) { + ErrorCode() = ErrorStart() + "not allowed at begin() for operation " + op + "."; + return false; + } + if (!end_ok && ((size_t) pos) == v_ptr->size()) { + ErrorCode() = ErrorStart() + "not allowed at end() for operation " + op + "."; return false; } - if (!begin_ok && pos == 0) { ErrorCode() = "Iterator not allowed at begin()."; return false; } - if (!end_ok && ((size_t) pos) == v_ptr->size()) { ErrorCode() = "Iterator not allowed at end()."; return false; } return true; } - this_t & operator=(const this_t &) = default; - this_t & operator=(this_t &&) = default; + this_t & operator=(const this_t &) & = default; + this_t & operator=(this_t &&) & = default; operator ITERATOR_T() { return *this; } operator const ITERATOR_T() const { return *this; } auto & operator*() { - emp_assert(OK(true, false), ErrorCode()); // Ensure vector hasn't changed since making iterator. + emp_assert(OK(true, false, "dereference"), ErrorCode()); return wrapped_t::operator*(); } const auto & operator*() const { - emp_assert(OK(true, false), ErrorCode()); // Ensure vector hasn't changed since making iterator. + emp_assert(OK(true, false, "const dereference"), ErrorCode()); return wrapped_t::operator*(); } auto operator->() { - emp_assert(OK(true, false), ErrorCode()); // Ensure vector hasn't changed since making iterator. +// emp_assert(OK(true, false, "->"), ErrorCode()); + emp_assert(OK(true, true, "->"), ErrorCode()); // Technically can use -> on end() for memory identification, just can't use result. return wrapped_t::operator->(); } auto operator->() const { - emp_assert(OK(true, false), ErrorCode()); // Ensure vector hasn't changed since making iterator. +// emp_assert(OK(true, false, "const ->"), ErrorCode()); + emp_assert(OK(true, true, "const ->"), ErrorCode()); // Technically can use -> on end() for memory identification, just can't use result. return wrapped_t::operator->(); } this_t & operator++() { - emp_assert(OK(true,false), ErrorCode()); + emp_assert(OK(true,false, "++ (post)"), ErrorCode()); wrapped_t::operator++(); return *this; } this_t operator++(int x) { - emp_assert(OK(true,false), ErrorCode()); + emp_assert(OK(true,false, "++ (pre)"), ErrorCode()); return this_t(wrapped_t::operator++(x), v_ptr); } this_t & operator--() { - emp_assert(OK(false,true), ErrorCode()); + emp_assert(OK(false,true, "-- (post)"), ErrorCode()); wrapped_t::operator--(); return *this; } this_t operator--(int x) { - emp_assert(OK(false,true), ErrorCode()); + emp_assert(OK(false,true, "-- (pre)"), ErrorCode()); return this_t(wrapped_t::operator--(x), v_ptr); } @@ -214,7 +233,7 @@ namespace emp { stdv_t::resize(new_size, val); revision++; } - this_t & operator=(const this_t &) = default; + this_t & operator=(const this_t &) & = default; T & operator[](size_t pos) { emp_assert(pos < stdv_t::size(), pos, stdv_t::size()); @@ -269,8 +288,13 @@ namespace emp { }; /// Build a specialized debug wrapper for emp::vector + #ifndef DOXYGEN_SHOULD_SKIP_THIS template class vector : public std::vector { + #else + template + class vector { + #endif private: using this_t = emp::vector; using stdv_t = std::vector; @@ -304,7 +328,7 @@ namespace emp { emp_assert(new_size < MAX_SIZE, new_size); stdv_t::resize(new_size, val); } - this_t & operator=(const this_t &) = default; + this_t & operator=(const this_t &) & = default; auto operator[](size_t pos) -> decltype(stdv_t::operator[](pos)) { emp_assert(pos < stdv_t::size(), pos, stdv_t::size()); diff --git a/include/emp/bits/BitArray.hpp b/include/emp/bits/BitArray.hpp index e89a2b0667..54b5e9ae3a 100644 --- a/include/emp/bits/BitArray.hpp +++ b/include/emp/bits/BitArray.hpp @@ -1,11 +1,12 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021. - * - * @file BitArray.hpp + * @file * @brief An Array of a fixed number of bits; similar to std::bitset, but with extra bit magic. - * @note Status: RELEASE + * Status: RELEASE * * @todo Some of the functions allow a start bit and end bit; each of these should be checked * to make sure that they will work if the start and end are part of the same byte. One @@ -17,9 +18,12 @@ #include +#include #include #include #include +#include +#include #include "../base/assert.hpp" #include "../base/Ptr.hpp" @@ -28,22 +32,25 @@ #include "../math/math.hpp" #include "../math/Random.hpp" #include "../meta/type_traits.hpp" -#include "../polyfill/span.hpp" #include "_bitset_helpers.hpp" #include "bitset_utils.hpp" -namespace emp { +#include "Bits.hpp" // New version of BitArray is in Bits.hpp + +namespace emp::old { /// A fixed-sized (but arbitrarily large) array of bits, and optimizes operations on those bits /// to be as fast as possible. - /// @param NUM_BITS is the fixed number of bits in this BitArray. - /// @param ZERO_LEFT indicates the side that bit zero will be located. + /// @tparam NUM_BITS is the fixed number of bits in this BitArray. + /// @tparam ZERO_LEFT indicates the side that bit zero will be located. template class BitArray { + #ifndef DOXYGEN_SHOULD_SKIP_THIS // make all templated instantiations friends with each other template friend class BitArray; + #endif // DOXYGEN_SHOULD_SKIP_THIS private: using this_t = BitArray; @@ -65,7 +72,7 @@ namespace emp { // Track number of bits in the final field; use 0 if a perfect fit. static constexpr size_t NUM_END_BITS = NUM_BITS & (FIELD_BITS - 1); - /// How many EXTRA bits are leftover in the gap at the end? + // How many EXTRA bits are leftover in the gap at the end? static constexpr size_t END_GAP = NUM_END_BITS ? (FIELD_BITS - NUM_END_BITS) : 0; // Mask to use to clear out any end bits that should be zeroes. @@ -121,10 +128,10 @@ namespace emp { [[nodiscard]] emp::Ptr BytePtr() { return reinterpret_cast(bits); } - /// Helper: call SHIFT with positive number instead + // Helper: call SHIFT with positive number instead void ShiftLeft(const size_t shift_size); - /// Helper for calling SHIFT with negative number + // Helper for calling SHIFT with negative number void ShiftRight(const size_t shift_size); /// Helper: call ROTATE with negative number instead @@ -137,7 +144,7 @@ namespace emp { /// Constructor: Assume all bits set to zero. explicit BitArray(bool init_val=false) noexcept { if (init_val) SetAll(); else Clear(); } - /// Copy constructor from another BitArray + // Copy constructor from another BitArray BitArray(const this_t & _in) noexcept { Copy(_in.bits); } /// Constructor to generate a BitArray from a std::bitset. @@ -167,17 +174,17 @@ namespace emp { /// Destructor. ~BitArray() = default; - /// Assignment operator (no separate move opperator since no resources to move...) - BitArray & operator=(const this_t & in_bits) noexcept { return Copy(in_bits.bits); } + /// Assignment operator (no separate move operator since no resources to move...) + BitArray & operator=(const this_t & in_bits) & noexcept { return Copy(in_bits.bits); } /// Assignment operator from a std::bitset. - BitArray & operator=(const std::bitset & bitset); + BitArray & operator=(const std::bitset & bitset) &; /// Assignment operator from a string of '0's and '1's. - BitArray & operator=(const std::string & bitstring); + BitArray & operator=(const std::string & bitstring) &; /// Assignment operator from a literal string of '0's and '1's. - BitArray & operator=(const char * bitstring) { return operator=(std::string(bitstring)); } + BitArray & operator=(const char * bitstring) & { return operator=(std::string(bitstring)); } /// Assignment from another BitArray of a different size. template @@ -187,7 +194,7 @@ namespace emp { template BitArray Export(size_t start_bit=0) const; - /// For debugging: make sure that there are no obvous problems with a BitArray object. + /// For debugging: make sure that there are no obvious problems with a BitArray object. bool OK() const; /// How many bits are in this BitArray? @@ -290,12 +297,14 @@ namespace emp { // ========= Comparison Operators ========== // + /// Test if two BitArray objects are identical. template [[nodiscard]] bool operator==(const BitArray & in) const; template [[nodiscard]] bool operator!=(const BitArray & in) const { return !(*this == in); } + /// Compare two BitArray objects, based on the associated binary value. template [[nodiscard]] bool operator< (const BitArray & in) const; @@ -314,7 +323,7 @@ namespace emp { // ========= Access Groups of bits ========= // - /// Retrive the byte at the specified byte index. + /// Retrieve the byte at the specified byte index. [[nodiscard]] uint8_t GetByte(size_t index) const; /// Get a read-only view into the internal array used by BitArray. @@ -428,7 +437,7 @@ namespace emp { [[nodiscard]] int FindOne() const; /// Deprecated: Return the position of the first one; return -1 if no ones in vector. - [[deprecated("Renamed to more acurate FindOne()")]] + [[deprecated("Renamed to more accurate FindOne()")]] [[nodiscard]] int FindBit() const { return FindOne(); } /// Return the position of the first one after start_pos; return -1 if no ones in vector. @@ -439,7 +448,7 @@ namespace emp { [[nodiscard]] int FindOne(const size_t start_pos) const; /// Deprecated version of FindOne(). - [[deprecated("Renamed to more acurate FindOne(start_pos)")]] + [[deprecated("Renamed to more accurate FindOne(start_pos)")]] [[nodiscard]] int FindBit(const size_t start_pos) const; /// Find the most-significant set-bit. @@ -449,7 +458,7 @@ namespace emp { int PopOne(); /// Deprecated version of PopOne(). - [[deprecated("Renamed to more acurate PopOne()")]] + [[deprecated("Renamed to more accurate PopOne()")]] int PopBit() { return PopOne(); } /// Return positions of all ones. @@ -644,10 +653,10 @@ namespace emp { /// Compound operator plus... const BitArray & operator+=(const BitArray & ar2) { return ADD_SELF(ar2); } - /// Compoount operator minus... + /// Compound operator minus... const BitArray & operator-=(const BitArray & ar2) { return SUB_SELF(ar2); } - /// STL COMPATABILITY + /// STL COMPATIBILITY /// A set of functions to allow drop-in replacement with std::bitset. [[nodiscard]] constexpr static size_t size() { return NUM_BITS; } [[nodiscard]] inline bool all() const { return All(); } @@ -684,7 +693,7 @@ namespace emp { const size_t start_pos = FieldPos(start); // Identify the start position WITHIN a bit field. const size_t stop_pos = FieldPos(stop); // Identify the stop position WITHIN a bit field. - size_t start_field = FieldID(start); // Ideftify WHICH bit field we're starting in. + size_t start_field = FieldID(start); // Identify WHICH bit field we're starting in. const size_t stop_field = FieldID(stop-1); // Identify the last field where we actually make a change. // If the start field and stop field are the same, mask off the middle. @@ -720,7 +729,7 @@ namespace emp { return *this; } - + #ifndef DOXYGEN_SHOULD_SKIP_THIS template void BitArray::ShiftLeft(const size_t shift_size) { // If we have only a single field, this operation can be quick. @@ -760,7 +769,7 @@ namespace emp { } - /// Helper for calling SHIFT with negative number + // Helper for calling SHIFT with negative number template void BitArray::ShiftRight(const size_t shift_size) { // If we have only a single field, this operation can be quick. @@ -800,7 +809,7 @@ namespace emp { } } - /// Helper: call ROTATE with negative number + // Helper: call ROTATE with negative number template void BitArray::RotateLeft(const size_t shift_size_raw) { const field_t shift_size = shift_size_raw % NUM_BITS; @@ -812,7 +821,7 @@ namespace emp { field_t & n = bits[0]; size_t c = shift_size; - // mask necessary to suprress shift count overflow warnings + // mask necessary to suppress shift count overflow warnings c &= FIELD_LOG2_MASK; n = (n<>( (-(c+FIELD_BITS-NUM_BITS)) & FIELD_LOG2_MASK )); @@ -823,7 +832,7 @@ namespace emp { ShiftRight(NUM_BITS - shift_size); OR_SELF(dup); } else { - // for big BitArrays, manual rotating is fater + // for big BitArrays, manual rotating is faster // note that we already modded shift_size by NUM_BITS // so there's no need to mod by FIELD_SIZE here @@ -884,7 +893,7 @@ namespace emp { } - /// Helper for calling ROTATE with positive number + // Helper for calling ROTATE with positive number template void BitArray::RotateRight(const size_t shift_size_raw) { @@ -898,7 +907,7 @@ namespace emp { field_t & n = bits[0]; size_t c = shift_size; - // mask necessary to suprress shift count overflow warnings + // mask necessary to suppress shift count overflow warnings c &= FIELD_LOG2_MASK; n = (n>>c) | (n<<( (NUM_BITS-c) & FIELD_LOG2_MASK )); @@ -909,7 +918,7 @@ namespace emp { ShiftLeft(NUM_BITS - shift_size); OR_SELF(dup); } else { - // for big BitArrays, manual rotating is fater + // for big BitArrays, manual rotating is faster const field_t field_shift = (shift_size / FIELD_BITS) % NUM_FIELDS; const int bit_shift = shift_size % FIELD_BITS; @@ -959,14 +968,14 @@ namespace emp { // -------------------- Longer Constructors and bit copying --------------------- - /// Constructor to generate a BitArray from a std::bitset. + // Constructor to generate a BitArray from a std::bitset. template BitArray::BitArray(const std::bitset & bitset) { for (size_t bit{}; bit < NUM_BITS; ++bit) Set( bit, bitset[bit] ); ClearExcessBits(); } - /// Constructor to generate a BitArray from a string of '0's and '1's. + // Constructor to generate a BitArray from a string of '0's and '1's. template BitArray::BitArray(const std::string & bitstring) { @@ -994,18 +1003,18 @@ namespace emp { } } - /// Assignment operator from a std::bitset. + // Assignment operator from a std::bitset. template BitArray & - BitArray::operator=(const std::bitset & bitset) { + BitArray::operator=(const std::bitset & bitset) & { for (size_t i = 0; i < NUM_BITS; i++) Set(i, bitset[i]); return *this; } - /// Assignment operator from a string of '0's and '1's. + // Assignment operator from a string of '0's and '1's. template BitArray & - BitArray::operator=(const std::string & bitstring) { + BitArray::operator=(const std::string & bitstring) & { emp_assert(bitstring.size() <= NUM_BITS); Clear(); if constexpr (ZERO_LEFT) { @@ -1018,7 +1027,7 @@ namespace emp { } - /// Assign from a BitArray of a different size. + // Assign from a BitArray of a different size. template template BitArray & BitArray::Import( @@ -1026,7 +1035,7 @@ namespace emp { const size_t from_bit ) { // Only check for same-ness if the two types are the same. - if constexpr (FROM_BITS == NUM_BITS) emp_assert(&from_array != this); + if constexpr (FROM_BITS == NUM_BITS) { emp_assert(&from_array != this); } emp_assert(from_bit < FROM_BITS); @@ -1064,7 +1073,7 @@ namespace emp { } - /// Convert to a BitArray of a different size. + // Convert to a BitArray of a different size. template template BitArray BitArray::Export(size_t start_bit) const { @@ -1074,7 +1083,7 @@ namespace emp { return out_bits; } - /// For debugging: make sure that there are no obvous problems with a BitArray object. + // For debugging: make sure that there are no obvious problems with a BitArray object. template bool BitArray::OK() const { // Make sure final bits are zeroed out. @@ -1089,13 +1098,13 @@ namespace emp { template bool BitArray::Get(size_t index) const { - emp_assert(index >= 0 && index < NUM_BITS); + emp_assert(index < NUM_BITS); const size_t field_id = FieldID(index); const size_t pos_id = FieldPos(index); return (bits[field_id] & (((field_t)1U) << pos_id)) != 0; } - /// Set the bit at a specified index. + // Set the bit at a specified index. template BitArray & BitArray::Set(size_t index, bool value) { emp_assert(index < NUM_BITS); @@ -1109,7 +1118,7 @@ namespace emp { return *this; } - /// Set all bits to one. + // Set all bits to one. template BitArray & BitArray::SetAll() noexcept { for (field_t & x : bits) x = FIELD_ALL; @@ -1118,10 +1127,10 @@ namespace emp { } - /// Flip a single bit + // Flip a single bit template BitArray & BitArray::Toggle(size_t index) { - emp_assert(index >= 0 && index < NUM_BITS); + emp_assert(index < NUM_BITS); const size_t field_id = FieldID(index); const size_t pos_id = FieldPos(index); const field_t pos_mask = FIELD_1 << pos_id; @@ -1135,7 +1144,7 @@ namespace emp { // ------------------------- Implementations Randomization functions ------------------------- - /// Set all bits randomly, with a 50% probability of being a 0 or 1. + // Set all bits randomly, with a 50% probability of being a 0 or 1. template BitArray & BitArray::Randomize(Random & random) { random.RandFill(BytePtr(), TOTAL_BYTES); @@ -1143,7 +1152,7 @@ namespace emp { return *this; } - /// Set all bits randomly, with probability specified at compile time. + // Set all bits randomly, with probability specified at compile time. template template BitArray & BitArray::RandomizeP(Random & random, @@ -1156,7 +1165,7 @@ namespace emp { } - /// Set all bits randomly, with a given probability of being on. + // Set all bits randomly, with a given probability of being on. template BitArray & BitArray::Randomize(Random & random, const double p, const size_t start_pos, const size_t stop_pos) { @@ -1168,7 +1177,7 @@ namespace emp { return *this; } - /// Set all bits randomly, with a given number of them being on. + // Set all bits randomly, with a given number of them being on. template BitArray & BitArray::ChooseRandom( @@ -1231,7 +1240,7 @@ namespace emp { return *this; } - /// Flip random bits with a given probability. + // Flip random bits with a given probability. // @CAO: Possibly faster to generate a sequence of bits and XORing with them. template BitArray & BitArray::FlipRandom(Random & random, @@ -1248,7 +1257,7 @@ namespace emp { return *this; } - /// Set random bits with a given probability (does not check if already set.) + // Set random bits with a given probability (does not check if already set.) template BitArray & BitArray::SetRandom(Random & random, const double p, @@ -1264,7 +1273,7 @@ namespace emp { return *this; } - /// Unset random bits with a given probability (does not check if already zero.) + // Unset random bits with a given probability (does not check if already zero.) template BitArray & BitArray::ClearRandom(Random & random, const double p, @@ -1280,7 +1289,7 @@ namespace emp { return *this; } - /// Flip a specified number of random bits. + // Flip a specified number of random bits. template BitArray & BitArray::FlipRandomCount(Random & random, const size_t num_bits) @@ -1290,7 +1299,7 @@ namespace emp { return *this ^= target_bits; } - /// Set a specified number of random bits (does not check if already set.) + // Set a specified number of random bits (does not check if already set.) template BitArray & BitArray::SetRandomCount(Random & random, const size_t num_bits) @@ -1300,7 +1309,7 @@ namespace emp { return *this |= target_bits; } - /// Unset a specified number of random bits (does not check if already zero.) + // Unset a specified number of random bits (does not check if already zero.) template BitArray & BitArray::ClearRandomCount(Random & random, const size_t num_bits) @@ -1313,7 +1322,7 @@ namespace emp { // ------------------------- Implementations of Comparison Operators ------------------------- - /// Test if two BitArray objects are identical. + // Test if two BitArray objects are identical. template template bool BitArray::operator==(const BitArray & in_bits) const { @@ -1325,7 +1334,7 @@ namespace emp { return true; } - /// Compare two BitArray objects, based on the associated binary value. + // Compare two BitArray objects, based on the associated binary value. template template bool BitArray::operator<(const BitArray & in_bits) const { @@ -1341,7 +1350,7 @@ namespace emp { // ------------------------- Access Groups of bits ------------------------- - /// Get the full byte starting from the bit at a specified index. + // Get the full byte starting from the bit at a specified index. template uint8_t BitArray::GetByte(size_t index) const { emp_assert(index < TOTAL_BYTES); @@ -1351,8 +1360,8 @@ namespace emp { } - /// Get a read-only view into the internal array used by BitArray. - /// @return Read-only span of BitArray's bytes. + // Get a read-only view into the internal array used by BitArray. + // @return Read-only span of BitArray's bytes. template std::span BitArray::GetBytes() const { return std::span( @@ -1362,7 +1371,7 @@ namespace emp { } - /// Set the full byte starting at the bit at the specified index. + // Set the full byte starting at the bit at the specified index. template void BitArray::SetByte(size_t index, uint8_t value) { emp_assert(index < TOTAL_BYTES); @@ -1372,8 +1381,8 @@ namespace emp { bits[field_id] = (bits[field_id] & ~(((field_t)255U) << pos_id)) | (val_uint << pos_id); } - /// Get the overall value of this BitArray, using a uint encoding, but including all bits - /// and returning the value as a double. + // Get the overall value of this BitArray, using a uint encoding, but including all bits + // and returning the value as a double. template double BitArray::GetValue() const { // If we have 64 bits or fewer, we can load the full value and return it. @@ -1398,7 +1407,7 @@ namespace emp { return out_value; } - /// Get specified type at a given index (in steps of that type size) + // Get specified type at a given index (in steps of that type size) template template T BitArray::GetValueAtIndex(const size_t index) const { @@ -1415,7 +1424,7 @@ namespace emp { } - /// Set specified type at a given index (in steps of that type size) + // Set specified type at a given index (in steps of that type size) template template void BitArray::SetValueAtIndex(const size_t index, T in_value) { @@ -1429,7 +1438,7 @@ namespace emp { } - /// Get the specified type starting from a given BIT position. + // Get the specified type starting from a given BIT position. template template T BitArray::GetValueAtBit(const size_t index) const { @@ -1443,7 +1452,7 @@ namespace emp { } - /// Set the specified type starting from a given BIT position. + // Set the specified type starting from a given BIT position. // @CAO: Can be optimized substantially, especially for long BitArrays. template template @@ -1464,10 +1473,10 @@ namespace emp { // ------------------------- Other Analyses ------------------------- - /// A simple hash function for bit vectors. + // A simple hash function for bit vectors. template std::size_t BitArray::Hash() const noexcept { - /// If we have a vector of size_t, treat it as a vector of hash values to combine. + // If we have a vector of size_t, treat it as a vector of hash values to combine. if constexpr (std::is_same_v) { return hash_combine(bits, NUM_FIELDS); } @@ -1487,7 +1496,7 @@ namespace emp { } // TODO: see https://arxiv.org/pdf/1611.07612.pdf for fast pop counts - /// Count the number of ones in the BitArray. + // Count the number of ones in the BitArray. template size_t BitArray::CountOnes() const { size_t bit_count = 0; @@ -1500,7 +1509,7 @@ namespace emp { return bit_count; } - /// Faster counting of ones for very sparse bit vectors. + // Faster counting of ones for very sparse bit vectors. template size_t BitArray::CountOnes_Sparse() const { size_t bit_count = 0; @@ -1513,7 +1522,7 @@ namespace emp { return bit_count; } - /// Return the index of the first one in the sequence; return -1 if no ones are available. + // Return the index of the first one in the sequence; return -1 if no ones are available. template int BitArray::FindOne() const { size_t field_id = 0; @@ -1522,7 +1531,7 @@ namespace emp { (int) (find_bit(bits[field_id]) + (field_id << FIELD_LOG2)) : -1; } - /// Return index of first one in sequence AFTER start_pos (or -1 if no ones) + // Return index of first one in sequence AFTER start_pos (or -1 if no ones) template int BitArray::FindOne(const size_t start_pos) const { if (start_pos >= NUM_BITS) return -1; // If we're past the end, return fail. @@ -1542,7 +1551,7 @@ namespace emp { (int) (find_bit(bits[field_id]) + (field_id * FIELD_BITS)) : -1; } - /// Find the most-significant set-bit. + // Find the most-significant set-bit. template int BitArray::FindMaxOne() const { // Find the max field with a one. @@ -1569,7 +1578,7 @@ namespace emp { return (int) (max_field * FIELD_BITS + offset); } - /// Return index of first one in sequence (or -1 if no ones); change this position to zero. + // Return index of first one in sequence (or -1 if no ones); change this position to zero. template int BitArray::PopOne() { const int out_bit = FindOne(); @@ -1577,7 +1586,7 @@ namespace emp { return out_bit; } - /// Return a vector indicating the posistions of all ones in the BitArray. + // Return a vector indicating the positions of all ones in the BitArray. template emp::vector BitArray::GetOnes() const { // @CAO -- There are better ways to do this with bit tricks. @@ -1589,7 +1598,7 @@ namespace emp { return ones; } - /// Find the length of the longest continuous series of ones. + // Find the length of the longest continuous series of ones. template size_t BitArray::LongestSegmentOnes() const { size_t length = 0; @@ -1604,14 +1613,14 @@ namespace emp { // ------------------------- Print/String Functions ------------------------- // - /// Convert this BitArray to a string (using default direction) + // Convert this BitArray to a string (using default direction) template std::string BitArray::ToString() const { if constexpr (ZERO_LEFT) return ToArrayString(); else return ToBinaryString(); } - /// Convert this BitArray to an array string [0 index on left] + // Convert this BitArray to an array string [0 index on left] template std::string BitArray::ToArrayString() const { std::string out_string; @@ -1620,7 +1629,7 @@ namespace emp { return out_string; } - /// Convert this BitArray to a numerical string [0 index on right] + // Convert this BitArray to a numerical string [0 index on right] template std::string BitArray::ToBinaryString() const { std::string out_string; @@ -1629,7 +1638,7 @@ namespace emp { return out_string; } - /// Convert this BitArray to a series of IDs + // Convert this BitArray to a series of IDs template std::string BitArray::ToIDString(const std::string & spacer) const { std::stringstream ss; @@ -1637,7 +1646,7 @@ namespace emp { return ss.str(); } - /// Convert this BitArray to a series of IDs with ranges condensed. + // Convert this BitArray to a series of IDs with ranges condensed. template std::string BitArray::ToRangeString(const std::string & spacer, const std::string & ranger) const @@ -1647,7 +1656,7 @@ namespace emp { return ss.str(); } - /// Print a space between each field (or other provided spacer) + // Print a space between each field (or other provided spacer) template void BitArray::PrintFields(std::ostream & out, const std::string & spacer) const { for (size_t i = NUM_BITS-1; i < NUM_BITS; i--) { @@ -1656,7 +1665,7 @@ namespace emp { } } - /// Print a space between each field (or other provided spacer) + // Print a space between each field (or other provided spacer) template void BitArray::PrintDebug(std::ostream & out) const { for (size_t field = 0; field < NUM_FIELDS; field++) { @@ -1672,7 +1681,7 @@ namespace emp { out << "^" << std::endl; } - /// Print the locations of all one bits, using the provided spacer (default is a single space) + // Print the locations of all one bits, using the provided spacer (default is a single space) template void BitArray::PrintOneIDs(std::ostream & out, const std::string & spacer) const { bool started = false; @@ -1685,7 +1694,7 @@ namespace emp { } } - /// Print the ones in a range format. E.g., 2-5,7,10-15 + // Print the ones in a range format. E.g., 2-5,7,10-15 template void BitArray::PrintAsRange(std::ostream & out, const std::string & spacer, @@ -1709,7 +1718,7 @@ namespace emp { // ------------------------- Whole BitArray manipulation functions ------------------------- - /// Perform a Boolean NOT on this BitArray, store result here, and return this object. + // Perform a Boolean NOT on this BitArray, store result here, and return this object. template BitArray & BitArray::NOT_SELF() { for (size_t i = 0; i < NUM_FIELDS; i++) bits[i] = ~bits[i]; @@ -1717,21 +1726,21 @@ namespace emp { return *this; } - /// Perform a Boolean AND with a second BitArray, store result here, and return this object. + // Perform a Boolean AND with a second BitArray, store result here, and return this object. template BitArray & BitArray::AND_SELF(const this_t & array2) { for (size_t i = 0; i < NUM_FIELDS; i++) bits[i] = bits[i] & array2.bits[i]; return *this; } - /// Perform a Boolean OR with a second BitArray, store result here, and return this object. + // Perform a Boolean OR with a second BitArray, store result here, and return this object. template BitArray & BitArray::OR_SELF(const this_t & array2) { for (size_t i = 0; i < NUM_FIELDS; i++) bits[i] = bits[i] | array2.bits[i]; return *this; } - /// Perform a Boolean NAND with a second BitArray, store result here, and return this object. + // Perform a Boolean NAND with a second BitArray, store result here, and return this object. template BitArray & BitArray::NAND_SELF(const this_t & array2) { for (size_t i = 0; i < NUM_FIELDS; i++) bits[i] = ~(bits[i] & array2.bits[i]); @@ -1739,7 +1748,7 @@ namespace emp { return *this; } - /// Perform a Boolean NOR with a second BitArray, store result here, and return this object. + // Perform a Boolean NOR with a second BitArray, store result here, and return this object. template BitArray & BitArray::NOR_SELF(const this_t & array2) { for (size_t i = 0; i < NUM_FIELDS; i++) bits[i] = ~(bits[i] | array2.bits[i]); @@ -1747,14 +1756,14 @@ namespace emp { return *this; } - /// Perform a Boolean XOR with a second BitArray, store result here, and return this object. + // Perform a Boolean XOR with a second BitArray, store result here, and return this object. template BitArray & BitArray::XOR_SELF(const this_t & array2) { for (size_t i = 0; i < NUM_FIELDS; i++) bits[i] = bits[i] ^ array2.bits[i]; return *this; } - /// Perform a Boolean EQU with a second BitArray, store result here, and return this object. + // Perform a Boolean EQU with a second BitArray, store result here, and return this object. template BitArray & BitArray::EQU_SELF(const this_t & array2) { for (size_t i = 0; i < NUM_FIELDS; i++) bits[i] = ~(bits[i] ^ array2.bits[i]); @@ -1762,8 +1771,8 @@ namespace emp { return *this; } - /// Positive shifts go right and negative shifts go left (0 does nothing); - /// return result. + // Positive shifts go right and negative shifts go left (0 does nothing); + // return result. template BitArray BitArray::SHIFT(const int shift_size) const { this_t out_array(*this); @@ -1772,8 +1781,8 @@ namespace emp { return out_array; } - /// Positive shifts go right and negative shifts go left (0 does nothing); - /// store result here, and return this object. + // Positive shifts go right and negative shifts go left (0 does nothing); + // store result here, and return this object. template BitArray & BitArray::SHIFT_SELF(const int shift_size) { if (shift_size > 0) ShiftRight((field_t) shift_size); @@ -1781,7 +1790,7 @@ namespace emp { return *this; } - /// Reverse the order of bits in the BitArray + // Reverse the order of bits in the BitArray template BitArray & BitArray::REVERSE_SELF() { @@ -1807,7 +1816,7 @@ namespace emp { } - /// Reverse order of bits in the BitArray. + // Reverse order of bits in the BitArray. template BitArray BitArray::REVERSE() const { this_t out_array(*this); @@ -1815,8 +1824,8 @@ namespace emp { } - /// Positive rotates go left and negative rotates go left (0 does nothing); - /// return result. + // Positive rotates go left and negative rotates go left (0 does nothing); + // return result. template BitArray BitArray::ROTATE(const int rotate_size) const { this_t out_array(*this); @@ -1825,8 +1834,8 @@ namespace emp { return out_array; } - /// Positive rotates go right and negative rotates go left (0 does nothing); - /// store result here, and return this object. + // Positive rotates go right and negative rotates go left (0 does nothing); + // store result here, and return this object. template BitArray & BitArray::ROTATE_SELF(const int rotate_size) { if (rotate_size > 0) RotateRight((field_t) rotate_size); @@ -1834,7 +1843,7 @@ namespace emp { return *this; } - /// Helper: call ROTATE with negative number instead + // Helper: call ROTATE with negative number instead template template BitArray & BitArray::ROTL_SELF() { @@ -1846,7 +1855,7 @@ namespace emp { field_t & n = bits[0]; size_t c = shift_size; - // mask necessary to suprress shift count overflow warnings + // mask necessary to suppress shift count overflow warnings c &= FIELD_LOG2_MASK; n = (n<>( (-(c+FIELD_BITS-NUM_BITS)) & FIELD_LOG2_MASK )); @@ -1915,7 +1924,7 @@ namespace emp { } - /// Helper for calling ROTATE with positive number + // Helper for calling ROTATE with positive number template template BitArray & BitArray::ROTR_SELF() { @@ -1928,7 +1937,7 @@ namespace emp { field_t & n = bits[0]; size_t c = shift_size; - // mask necessary to suprress shift count overflow warnings + // mask necessary to suppress shift count overflow warnings c &= FIELD_LOG2_MASK; n = (n>>c) | (n<<( (NUM_BITS-c) & FIELD_LOG2_MASK )); @@ -1984,18 +1993,18 @@ namespace emp { } - /// Addition of two BitArrays. - /// Wraps if it overflows. - /// Returns result. + // Addition of two BitArrays. + // Wraps if it overflows. + // Returns result. template BitArray BitArray::ADD(const BitArray & array2) const{ this_t out_array(*this); return out_array.ADD_SELF(array2); } - /// Addition of two BitArrays. - /// Wraps if it overflows. - /// Returns this object. + // Addition of two BitArrays. + // Wraps if it overflows. + // Returns this object. template BitArray & BitArray::ADD_SELF(const this_t & array2) { bool carry = false; @@ -2021,18 +2030,18 @@ namespace emp { return *this; } - /// Subtraction of two BitArrays. - /// Wraps around if it underflows. - /// Returns result. + // Subtraction of two BitArrays. + // Wraps around if it underflows. + // Returns result. template BitArray BitArray::SUB(const this_t & array2) const{ this_t out_array(*this); return out_array.SUB_SELF(array2); } - /// Subtraction of two BitArrays. - /// Wraps if it underflows. - /// Returns this object. + // Subtraction of two BitArrays. + // Wraps if it underflows. + // Returns this object. template BitArray & BitArray::SUB_SELF(const this_t & array2){ @@ -2056,6 +2065,7 @@ namespace emp { return *this; } + #endif // DOXYGEN_SHOULD_SKIP_THIS // ------------------------- Extra Functions ------------------------- @@ -2077,17 +2087,20 @@ namespace emp { } -/// For hashing BitArrays +#ifndef DOXYGEN_SHOULD_SKIP_THIS +// For hashing BitArrays namespace std { template - struct hash> + struct hash> { - size_t operator()( const emp::BitArray & bs ) const noexcept + size_t operator()( const emp::old::BitArray & bs ) const noexcept { return bs.Hash(); } }; } +#endif // DOXYGEN_SHOULD_SKIP_THIS + #endif // #ifndef EMP_BITS_BITARRAY_HPP_INCLUDE diff --git a/include/emp/bits/BitMatrix.hpp b/include/emp/bits/BitMatrix.hpp index a15ef586ee..8c0fd3bad4 100644 --- a/include/emp/bits/BitMatrix.hpp +++ b/include/emp/bits/BitMatrix.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021 - * - * @file BitMatrix.hpp + * @file * @brief A COL x ROW matrix of bits and provides easy indexing and manipulation * @note Status: BETA */ @@ -12,6 +13,7 @@ #define EMP_BITS_BITMATRIX_HPP_INCLUDE #include +#include #include @@ -20,10 +22,10 @@ namespace emp { - /// @brief A simple class to manage a COLS x ROWS matrix of bits. + /// A simple class to manage a COLS x ROWS matrix of bits. /// /// Bits are translated to a bitset with 0 in the upper left and moving through bits from - /// left to right and top to bottom. For example, the indecies in a 3x3 bit matrix would be + /// left to right and top to bottom. For example, the indices in a 3x3 bit matrix would be /// organized as such: /// /// 0 1 2 @@ -36,10 +38,13 @@ namespace emp { BitSet bits; ///< Actual bits in matrix. public: + + #ifndef DOXYGEN_SHOULD_SKIP_THIS template constexpr BitSet Mask() const { return BitSet(); } + #endif // DOXYGEN_SHOULD_SKIP_THIS /// Keep only a single column of values, reducing all others to zeros. template @@ -66,7 +71,6 @@ namespace emp { return mask; } -// public: BitMatrix() { ; } BitMatrix(const BitSet & in_bits) : bits(in_bits) { ; } BitMatrix(const BitMatrix & in_matrix) : bits(in_matrix.bits) { ; } @@ -111,15 +115,14 @@ namespace emp { void ClearCol(size_t col) { bits &= ~(MaskCol<0>() << col); } void ClearRow(size_t row) { bits &= ~(MaskRow<0>() << (row * COLS)); } - // Count the number of set bits in the matrix. + /// Count the number of set bits in the matrix. size_t CountOnes() const { return bits.count(); } - // Find the position of the first non-zero bit. - // size_t FindOne() const { return (~bits & (bits - 1)).count(); } - + /// Find the position of the first non-zero bit. + /// size_t FindOne() const { return (~bits & (bits - 1)).count(); } int FindOne() const { return bits.FindOne(); } - // Shift the whole matrix in the specified direction. + /// Shift the whole matrix in the specified direction. BitMatrix LeftShift() const { return ((bits & ~MaskCol<0>()) >> 1); } BitMatrix RightShift() const { return ((bits << 1) & ~MaskCol<0>()); } BitMatrix UpShift() const { return bits >> COLS; } @@ -129,10 +132,10 @@ namespace emp { BitMatrix URShift() const { return ((bits >> (COLS-1)) & ~MaskCol<0>()); } BitMatrix DRShift() const { return ((bits << (COLS+1)) & ~MaskCol<0>()); } - // Find all points within one step of the ones on this bit matrix. + /// Find all points within one step of the ones on this bit matrix. BitMatrix GetReach() const { return *this | LeftShift() | RightShift() | UpShift() | DownShift(); } - // Find all points reachable from the start position. + /// Find all points reachable from the start position. BitMatrix GetRegion(size_t start_pos) const { // Make sure we have a legal region, or else return an empty matrix. if (start_pos < 0 || start_pos >= GetSize() || bits[start_pos] == 0) return BitMatrix(); @@ -149,10 +152,10 @@ namespace emp { } BitMatrix GetRegion(size_t col, size_t row) const { return GetRegion(ToID(col,row)); } - // Does this bit matrix represent a connected set of ones? + /// Does this bit matrix represent a connected set of ones? bool IsConnected() const { return GetRegion((size_t)FindOne()) == *this; } - // Does this bit matrix have any 2x2 square of ones in it? + /// Does this bit matrix have any 2x2 square of ones in it? bool Has2x2() const { return (*this & UpShift() & LeftShift() & ULShift()).Any(); } void Print(std::ostream & os = std::cout) const { diff --git a/include/emp/bits/BitSet.hpp b/include/emp/bits/BitSet.hpp index 3904464147..f6241af02e 100644 --- a/include/emp/bits/BitSet.hpp +++ b/include/emp/bits/BitSet.hpp @@ -1,24 +1,28 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021. - * - * @file BitSet.hpp + * @file * @brief A drop-in replacement for std::bitset, with additional bit magic features; aliases BitArray. - * @note Status: RELEASE + * Status: RELEASE * */ #ifndef EMP_BITS_BITSET_HPP_INCLUDE #define EMP_BITS_BITSET_HPP_INCLUDE +#include #include "BitArray.hpp" -namespace emp { +#include "Bits.hpp" // New version of BitSet is in Bits.hpp + +namespace emp::old { template - using BitSet = emp::BitArray; + using BitSet = emp::old::BitArray; } diff --git a/include/emp/bits/BitVector.hpp b/include/emp/bits/BitVector.hpp index 8bd2aff868..489c788272 100644 --- a/include/emp/bits/BitVector.hpp +++ b/include/emp/bits/BitVector.hpp @@ -1,11 +1,12 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2022. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021. - * - * @file BitVector.hpp + * @file * @brief A drop-in replacement for std::vector, with additional bitwise logic features. - * @note Status: RELEASE + * Status: RELEASE * * @note Compile with -O3 and -msse4.2 for fast bit counting. * @@ -18,7 +19,7 @@ * @todo For large BitVectors we can use a factory to preserve/adjust bit info. That should be * just as efficient than a reserve, but without the need to store extra in-class info. * @todo Implement append(), resize(), push_bit(), insert(), remove() - * @todo Think about how itertors should work for BitVector. It should probably go bit-by-bit, + * @todo Think about how iterators should work for BitVector. It should probably go bit-by-bit, * but there are very few circumstances where that would be useful. Going through the * positions of all ones would be more useful, but perhaps less intuitive. * @@ -30,9 +31,12 @@ #include +#include #include #include #include +#include +#include #include "../base/assert.hpp" #include "../base/Ptr.hpp" @@ -40,16 +44,17 @@ #include "../datastructs/hash_utils.hpp" #include "../math/math.hpp" #include "../math/Random.hpp" -#include "../polyfill/span.hpp" #include "_bitset_helpers.hpp" #include "bitset_utils.hpp" -namespace emp { +#include "Bits.hpp" // New version of BitVector is in Bits.hpp + +namespace emp::old { - /// @brief A drop-in replacement for std::vector, but with extra bitwise logic features. + /// A drop-in replacement for std::vector, but with extra bitwise logic features. /// - /// This class stores an arbirary number of bits in a set of "fields" (typically 32 bits or 64 + /// This class stores an arbitrary number of bits in a set of "fields" (typically 32 bits or 64 /// bits per field, depending on which should be faster.) Individual bits can be extracted, /// -or- bitwise logic (including more complex bit magic) can be used on the groups of bits. @@ -69,7 +74,7 @@ namespace emp { static constexpr size_t MAX_BITS = (size_t) -1; ///< Value larger than any bit ID. // Number of bits needed to specify position in a field + mask - static constexpr size_t FIELD_LOG2 = emp::Log2(FIELD_BITS); + static constexpr size_t FIELD_LOG2 = static_cast(emp::Log2(FIELD_BITS)); static constexpr field_t FIELD_LOG2_MASK = MaskLow(FIELD_LOG2); size_t num_bits; ///< Total number of bits are we using @@ -84,7 +89,7 @@ namespace emp { /// A mask to cut off all of the final bits. [[nodiscard]] field_t EndMask() const { return MaskLow(NumEndBits()); } - /// How many feilds do we need for the current set of bits? + /// How many fields do we need for the current set of bits? [[nodiscard]] size_t NumFields() const { return num_bits ? (1 + ((num_bits - 1) / FIELD_BITS)) : 0; } /// What is the ID of the last occupied field? @@ -110,10 +115,10 @@ namespace emp { // Assume that the size of the bits has already been adjusted to be the size of the one // being copied and only the fields need to be copied over. - void RawCopy(const Ptr in); + inline void RawCopy(const Ptr in); // Copy bits from one position in the genome to another; leave old positions unchanged. - void RawCopy(const size_t from_start, const size_t from_stop, const size_t to); + inline void RawCopy(const size_t from_start, const size_t from_stop, const size_t to); // Convert the bits to bytes (note that bits are NOT in order at the byte level!) [[nodiscard]] emp::Ptr BytePtr() { return bits.ReinterpretCast(); } @@ -131,20 +136,20 @@ namespace emp { inline BitVector & ApplyRange(const FUN_T & fun, size_t start, size_t stop); // Helper: call SHIFT with positive number - void ShiftLeft(const size_t shift_size); + inline void ShiftLeft(const size_t shift_size); // Helper for calling SHIFT with negative number - void ShiftRight(const size_t shift_size); + inline void ShiftRight(const size_t shift_size); /// Helper: call ROTATE with negative number instead - void RotateLeft(const size_t shift_size_raw); + inline void RotateLeft(const size_t shift_size_raw); /// Helper for calling ROTATE with positive number - void RotateRight(const size_t shift_size_raw); + inline void RotateRight(const size_t shift_size_raw); public: /// Build a new BitVector with specified bit count (default 0) and initialization (default 0) - BitVector(size_t in_num_bits=0, bool init_val=false); + inline BitVector(size_t in_num_bits=0, bool init_val=false); // Prevent ambiguous conversions... /// Anything not otherwise defined for first argument, convert to size_t. @@ -152,67 +157,67 @@ namespace emp { BitVector(T in_num_bits) : BitVector((size_t) in_num_bits, 0) {} /// Copy constructor of existing bit field. - BitVector(const BitVector & in); + inline BitVector(const BitVector & in); /// Move constructor of existing bit field. - BitVector(BitVector && in); + inline BitVector(BitVector && in); /// Constructor to generate a BitVector from a std::bitset. template - explicit BitVector(const std::bitset & bitset); + inline explicit BitVector(const std::bitset & bitset); /// Constructor to generate a BitVector from a string of '0's and '1's. - BitVector(const std::string & bitstring); + inline BitVector(const std::string & bitstring); /// Constructor to generate a BitVector from a literal string of '0's and '1's. BitVector(const char * bitstring) : BitVector(std::string(bitstring)) {} /// Constructor to generate a random BitVector (with equal prob of 0 or 1). - BitVector(size_t in_num_bits, Random & random); + inline BitVector(size_t in_num_bits, Random & random); /// Constructor to generate a random BitVector with provided prob of 1's. - BitVector(size_t in_num_bits, Random & random, const double p1); + inline BitVector(size_t in_num_bits, Random & random, const double p1); /// Constructor to generate a random BitVector with provided number of 1's. - BitVector(size_t in_num_bits, Random & random, const size_t target_ones); + inline BitVector(size_t in_num_bits, Random & random, const size_t target_ones); /// Constructor to generate a random BitVector with provided number of 1's. BitVector(size_t in_num_bits, Random & random, const int target_ones) : BitVector(in_num_bits, random, (size_t) target_ones) { } /// Initializer list constructor. - template BitVector(const std::initializer_list l); + template inline BitVector(const std::initializer_list l); /// Copy, but with a resize. - BitVector(const BitVector & in, size_t new_size); + inline BitVector(const BitVector & in, size_t new_size); /// Destructor - ~BitVector(); + inline ~BitVector(); /// Assignment operator. - BitVector & operator=(const BitVector & in); + inline BitVector & operator=(const BitVector & in) &; /// Move operator. - BitVector & operator=(BitVector && in); + inline BitVector & operator=(BitVector && in) &; /// Assignment operator from a std::bitset. template - BitVector & operator=(const std::bitset & bitset); + inline BitVector & operator=(const std::bitset & bitset) &; /// Assignment operator from a string of '0's and '1's. - BitVector & operator=(const std::string & bitstring); + inline BitVector & operator=(const std::string & bitstring) &; /// Assignment operator from a literal string of '0's and '1's. - BitVector & operator=(const char * bitstring) { return operator=(std::string(bitstring)); } + BitVector & operator=(const char * bitstring) & { return operator=(std::string(bitstring)); } /// Assignment from another BitVector without changing size. - BitVector & Import( const BitVector & from_bv, const size_t from_bit=0 ); + inline BitVector & Import( const BitVector & from_bv, const size_t from_bit=0 ); /// Convert to a BitVector of a different size. - BitVector Export(size_t out_size, size_t start_bit=0) const; + inline BitVector Export(size_t out_size, size_t start_bit=0) const; // Scan this bitvector to make sure that there are no internal problems. - bool OK() const; + inline bool OK() const; // ========= Accessors ========= // @@ -226,31 +231,33 @@ namespace emp { /// How many distinct values could be held in this BitVector? [[nodiscard]] double GetNumStates() const { return emp::Pow2(num_bits); } - /// Retrive the bit value from the specified index. - [[nodiscard]] bool Get(size_t index) const; + /// Retrieve the bit value from the specified index. + [[nodiscard]] inline bool Get(size_t index) const; /// A safe version of Get() for indexing out of range. Useful for representing collections. [[nodiscard]] bool Has(size_t index) const { return (index < num_bits) ? Get(index) : false; } /// Update the bit value at the specified index. - BitVector & Set(size_t index, bool value=true); + inline BitVector & Set(size_t index, bool value=true); /// Set all bits to 1. - BitVector & SetAll(); + inline BitVector & SetAll(); - /// Set a range of bits to one: [start, stop) - BitVector & SetRange(size_t start, size_t stop) - { return ApplyRange([](field_t){ return FIELD_ALL; }, start, stop); } + /// Set a range of bits to value (default one): [start, stop) + BitVector & SetRange(size_t start, size_t stop, bool value=true) { + if (value) return ApplyRange([](field_t){ return FIELD_ALL; }, start, stop); + return Clear(start, stop); + } /// Set all bits to 0. - BitVector & Clear(); + inline BitVector & Clear(); /// Set specific bit to 0. BitVector & Clear(size_t index) { return Set(index, false); } /// Set bits to 0 in the range [start, stop) BitVector & Clear(const size_t start, const size_t stop) - { return ApplyRange([](field_t){ return 0; }, start, stop); } + { return ApplyRange([](field_t) -> size_t { return 0; }, start, stop); } /// Const index operator -- return the bit at the specified position. @@ -263,14 +270,14 @@ namespace emp { BitVector & Toggle() { return NOT_SELF(); } /// Change a specified bit to the opposite value - BitVector & Toggle(size_t index); + inline BitVector & Toggle(size_t index); /// Flips all the bits in a range [start, end) BitVector & Toggle(size_t start, size_t stop) { return ApplyRange([](field_t x){ return ~x; }, start, stop); } /// Return true if ANY bits are set to 1, otherwise return false. - [[nodiscard]] bool Any() const; + [[nodiscard]] inline bool Any() const; /// Return true if NO bits are set to 1, otherwise return false. [[nodiscard]] bool None() const { return !Any(); } @@ -280,75 +287,75 @@ namespace emp { [[nodiscard]] bool All() const { return (~(*this)).None(); } /// Resize this BitVector to have the specified number of bits. - BitVector & Resize(size_t new_bits); + inline BitVector & Resize(size_t new_bits); // ========= Randomization functions ========= // /// Set all bits randomly, with a 50% probability of being a 0 or 1. - BitVector & Randomize(Random & random); + inline BitVector & Randomize(Random & random); /// Set all bits randomly, with probability specified at compile time. template - BitVector & RandomizeP(Random & random, const size_t start_pos=0, size_t stop_pos=MAX_BITS); + inline BitVector & RandomizeP(Random & random, const size_t start_pos=0, size_t stop_pos=MAX_BITS); /// Set all bits randomly, with a given probability of being a one. - BitVector & Randomize(Random & random, const double p, - const size_t start_pos=0, size_t stop_pos=MAX_BITS); + inline BitVector & Randomize(Random & random, const double p, + const size_t start_pos=0, size_t stop_pos=MAX_BITS); /// Set all bits randomly, with a given number of ones. - BitVector & ChooseRandom(Random & random, const int target_ones, - const size_t start_pos=0, size_t stop_pos=MAX_BITS); + inline BitVector & ChooseRandom(Random & random, const size_t target_ones, + const size_t start_pos=0, size_t stop_pos=MAX_BITS); /// Flip random bits with a given probability. - BitVector & FlipRandom(Random & random, const double p, - const size_t start_pos=0, size_t stop_pos=MAX_BITS); + inline BitVector & FlipRandom(Random & random, const double p, + const size_t start_pos=0, size_t stop_pos=MAX_BITS); /// Set random bits with a given probability (does not check if already set.) - BitVector & SetRandom(Random & random, const double p, - const size_t start_pos=0, size_t stop_pos=MAX_BITS); + inline BitVector & SetRandom(Random & random, const double p, + const size_t start_pos=0, size_t stop_pos=MAX_BITS); /// Unset random bits with a given probability (does not check if already zero.) - BitVector & ClearRandom(Random & random, const double p, - const size_t start_pos=0, size_t stop_pos=MAX_BITS); + inline BitVector & ClearRandom(Random & random, const double p, + const size_t start_pos=0, size_t stop_pos=MAX_BITS); /// Flip a specified number of random bits. - BitVector & FlipRandomCount(Random & random, const size_t target_bits); + inline BitVector & FlipRandomCount(Random & random, const size_t target_bits); /// Set a specified number of random bits (does not check if already set.) - BitVector & SetRandomCount(Random & random, const size_t target_bits); + inline BitVector & SetRandomCount(Random & random, const size_t target_bits); /// Unset a specified number of random bits (does not check if already zero.) - BitVector & ClearRandomCount(Random & random, const size_t target_bits); + inline BitVector & ClearRandomCount(Random & random, const size_t target_bits); // ========= Comparison Operators ========= // - [[nodiscard]] bool operator==(const BitVector & in) const; - [[nodiscard]] bool operator!=(const BitVector & in) const { return !(*this == in); } - [[nodiscard]] bool operator< (const BitVector & in) const; - [[nodiscard]] bool operator> (const BitVector & in) const { return in < *this; } - [[nodiscard]] bool operator<=(const BitVector & in) const { return !(in < *this); } - [[nodiscard]] bool operator>=(const BitVector & in) const { return !(*this < in); } + [[nodiscard]] inline bool operator==(const BitVector & in) const; + [[nodiscard]] inline bool operator!=(const BitVector & in) const { return !(*this == in); } + [[nodiscard]] inline bool operator< (const BitVector & in) const; + [[nodiscard]] inline bool operator> (const BitVector & in) const { return in < *this; } + [[nodiscard]] inline bool operator<=(const BitVector & in) const { return !(in < *this); } + [[nodiscard]] inline bool operator>=(const BitVector & in) const { return !(*this < in); } // ========= Conversion Operators ========= // /// Automatically convert BitVector to other vector types. - template operator emp::vector(); + template inline operator emp::vector(); /// Casting a bit array to bool identifies if ANY bits are set to 1. - explicit operator bool() const { return Any(); } + explicit inline operator bool() const { return Any(); } // ========= Access Groups of bits ========= // - /// Retrive the byte at the specified byte index. - [[nodiscard]] uint8_t GetByte(size_t index) const; + /// Retrieve the byte at the specified byte index. + [[nodiscard]] inline uint8_t GetByte(size_t index) const; /// Get a read-only view into the internal array used by BitVector. /// @return Read-only span of BitVector's bytes. - [[nodiscard]] std::span GetBytes() const; + [[nodiscard]] inline std::span GetBytes() const; /// Get a read-only pointer to the internal array used by BitVector. /// (note that bits are NOT in order at the byte level!) @@ -356,18 +363,18 @@ namespace emp { emp::Ptr RawBytes() const { return BytePtr(); } /// Update the byte at the specified byte index. - void SetByte(size_t index, uint8_t value); + inline void SetByte(size_t index, uint8_t value); /// Get the overall value of this BitVector, using a uint encoding, but including all bits /// and returning the value as a double. - [[nodiscard]] double GetValue() const; + [[nodiscard]] inline double GetValue() const; /// Return a span with all fields in order. std::span FieldSpan() { return std::span(bits.Raw(), NumFields()); } /// Get specified type at a given index (in steps of that type size) template - [[nodiscard]] T GetValueAtIndex(const size_t index) const; + [[nodiscard]] inline T GetValueAtIndex(const size_t index) const; // Retrieve the 8-bit uint from the specified uint index. [[nodiscard]] uint8_t GetUInt8(size_t index) const { return GetValueAtIndex(index); } @@ -386,7 +393,7 @@ namespace emp { /// Set specified type at a given index (in steps of that type size) - template void SetValueAtIndex(const size_t index, T value); + template inline void SetValueAtIndex(const size_t index, T value); /// Update the 8-bit uint at the specified uint index. void SetUInt8(const size_t index, uint8_t value) { SetValueAtIndex(index, value); } @@ -406,7 +413,7 @@ namespace emp { /// Get specified type starting at a given BIT position. template - [[nodiscard]] T GetValueAtBit(const size_t index) const; + [[nodiscard]] inline T GetValueAtBit(const size_t index) const; // Retrieve the 8-bit uint from the specified uint index. [[nodiscard]] uint8_t GetUInt8AtBit(size_t index) const { return GetValueAtBit(index); } @@ -424,7 +431,7 @@ namespace emp { [[nodiscard]] uint32_t GetUIntAtBit(size_t index) const { return GetUInt32AtBit(index); } - template void SetValueAtBit(const size_t index, T value); + template inline void SetValueAtBit(const size_t index, T value); /// Update the 8-bit uint at the specified uint index. void SetUInt8AtBit(const size_t index, uint8_t value) { SetValueAtBit(index, value); } @@ -445,44 +452,44 @@ namespace emp { // ========= Other Analyses ========= // /// A simple hash function for bit vectors. - [[nodiscard]] std::size_t Hash(size_t start_field=0) const; + [[nodiscard]] inline std::size_t Hash(size_t start_field=0) const; /// Count the number of ones in the BitVector. - [[nodiscard]] size_t CountOnes() const; + [[nodiscard]] inline size_t CountOnes() const; /// Faster counting of ones for very sparse bit vectors. - [[nodiscard]] size_t CountOnes_Sparse() const; + [[nodiscard]] inline size_t CountOnes_Sparse() const; /// Count the number of zeros in the BitVector. [[nodiscard]] size_t CountZeros() const { return GetSize() - CountOnes(); } /// Pop the last bit in the vector. /// @return value of the popped bit. - bool PopBack(); + inline bool PopBack(); /// Push given bit(s) onto the back of a vector. /// @param bit value of bit to be pushed. /// @param num number of bits to be pushed. - void PushBack(const bool bit=true, const size_t num=1); + inline void PushBack(const bool bit=true, const size_t num=1); /// Insert bit(s) into any index of vector using bit magic. /// Blog post on implementation reasoning: https://devolab.org/?p=2249 /// @param index location to insert bit(s). /// @param val value of bit(s) to insert. /// @param num number of bits to insert, default 1. - void Insert(const size_t index, const bool val=true, const size_t num=1); + inline void Insert(const size_t index, const bool val=true, const size_t num=1); /// Delete bits from any index in a vector. /// TODO: consider a bit magic approach here. /// @param index location to delete bit(s). /// @param num number of bits to delete, default 1. - void Delete(const size_t index, const size_t num=1); + inline void Delete(const size_t index, const size_t num=1); /// Return the position of the first one; return -1 if no ones in vector. - [[nodiscard]] int FindOne() const; + [[nodiscard]] inline int FindOne() const; /// Deprecated: Return the position of the first one; return -1 if no ones in vector. - [[deprecated("Renamed to more acurate FindOne()")]] + [[deprecated("Renamed to more accurate FindOne()")]] [[nodiscard]] int FindBit() const { return FindOne(); } /// Return the position of the first one after start_pos; return -1 if no ones in vector. @@ -490,30 +497,39 @@ namespace emp { /// /// for (int pos = bv.FindOne(); pos >= 0; pos = bv.FindOne(pos+1)) { ... } /// - [[nodiscard]] int FindOne(const size_t start_pos) const; + [[nodiscard]] inline int FindOne(const size_t start_pos) const; + + /// Special version of FindOne takes int; most common way to call. + [[nodiscard]] int FindOne(int start_pos) const { + return FindOne(static_cast(start_pos)); + } /// Deprecated version of FindOne(). - [[deprecated("Renamed to more acurate FindOne(start_pos)")]] + [[deprecated("Renamed to more accurate FindOne(start_pos)")]] [[nodiscard]] int FindBit(const size_t start_pos) const; /// Find the most-significant set-bit. - [[nodiscard]] int FindMaxOne() const; + [[nodiscard]] inline int FindMaxOne() const; /// Return the position of the first one and change it to a zero. Return -1 if no ones. - int PopOne(); + inline int PopOne(); /// Deprecated version of PopOne(). - [[deprecated("Renamed to more acurate PopOne()")]] + [[deprecated("Renamed to more accurate PopOne()")]] int PopBit() { return PopOne(); } /// Return positions of all ones. - [[nodiscard]] emp::vector GetOnes() const; + [[nodiscard]] inline emp::vector GetOnes() const; + + /// Collect positions of ones in the provided vector (allows id type choice) + template + inline emp::vector & GetOnes(emp::vector & out_vals) const; /// Find the length of the longest continuous series of ones. - [[nodiscard]] size_t LongestSegmentOnes() const; + [[nodiscard]] inline size_t LongestSegmentOnes() const; /// Return true if any ones are in common with another BitVector. - [[nodiscard]] bool HasOverlap(const BitVector & in) const; + [[nodiscard]] inline bool HasOverlap(const BitVector & in) const; // ========= Print/String Functions ========= // @@ -522,17 +538,17 @@ namespace emp { [[nodiscard]] char GetAsChar(size_t id) const { return Get(id) ? '1' : '0'; } /// Convert this BitVector to a vector string [index 0 on left] - [[nodiscard]] std::string ToString() const; + [[nodiscard]] inline std::string ToString() const; /// Convert this BitVector to a numerical string [index 0 on right] - [[nodiscard]] std::string ToBinaryString() const; + [[nodiscard]] inline std::string ToBinaryString() const; /// Convert this BitVector to a series of IDs - [[nodiscard]] std::string ToIDString(const std::string & spacer=" ") const; + [[nodiscard]] inline std::string ToIDString(const std::string & spacer=" ") const; /// Convert this BitVector to a series of IDs with ranges condensed. - [[nodiscard]] std::string ToRangeString(const std::string & spacer=",", - const std::string & ranger="-") const; + [[nodiscard]] inline std::string ToRangeString(const std::string & spacer=",", + const std::string & ranger="-") const; /// Regular print function (from least significant bit to most) void Print(std::ostream & out=std::cout) const { out << ToString(); } @@ -544,18 +560,18 @@ namespace emp { void PrintArray(std::ostream & out=std::cout) const { out << ToString(); } /// Print a space between each field (or other provided spacer) - void PrintFields(std::ostream & out=std::cout, const std::string & spacer=" ") const; + void inline PrintFields(std::ostream & out=std::cout, const std::string & spacer=" ") const; /// Print out details about the internals of the BitVector. - void PrintDebug(std::ostream & out=std::cout) const; + void inline PrintDebug(std::ostream & out=std::cout) const; /// Print the positions of all one bits, spaces are the default separator. - void PrintOneIDs(std::ostream & out=std::cout, const std::string & spacer=" ") const; + void inline PrintOneIDs(std::ostream & out=std::cout, const std::string & spacer=" ") const; /// Print the ones in a range format. E.g., 2-5,7,10-15 - void PrintAsRange(std::ostream & out=std::cout, - const std::string & spacer=",", - const std::string & ranger="-") const; + void inline PrintAsRange(std::ostream & out=std::cout, + const std::string & spacer=",", + const std::string & ranger="-") const; /// Overload ostream operator to return Print. friend std::ostream& operator<<(std::ostream &out, const BitVector & bv) { @@ -567,25 +583,25 @@ namespace emp { // ========= Boolean Logic and Shifting Operations ========= // /// Perform a Boolean NOT with this BitVector, store result here, and return this object. - BitVector & NOT_SELF(); + inline BitVector & NOT_SELF(); /// Perform a Boolean AND with this BitVector, store result here, and return this object. - BitVector & AND_SELF(const BitVector & bv2); + inline BitVector & AND_SELF(const BitVector & bv2); /// Perform a Boolean OR with this BitVector, store result here, and return this object. - BitVector & OR_SELF(const BitVector & bv2); + inline BitVector & OR_SELF(const BitVector & bv2); /// Perform a Boolean NAND with this BitVector, store result here, and return this object. - BitVector & NAND_SELF(const BitVector & bv2); + inline BitVector & NAND_SELF(const BitVector & bv2); /// Perform a Boolean NOR with this BitVector, store result here, and return this object. - BitVector & NOR_SELF(const BitVector & bv2); + inline BitVector & NOR_SELF(const BitVector & bv2); /// Perform a Boolean XOR with this BitVector, store result here, and return this object. - BitVector & XOR_SELF(const BitVector & bv2); + inline BitVector & XOR_SELF(const BitVector & bv2); /// Perform a Boolean EQU with this BitVector, store result here, and return this object. - BitVector & EQU_SELF(const BitVector & bv2); + inline BitVector & EQU_SELF(const BitVector & bv2); /// Perform a Boolean NOT on this BitVector and return the result. @@ -611,65 +627,74 @@ namespace emp { /// Positive shifts go left and negative go right (0 does nothing); return result. - [[nodiscard]] BitVector SHIFT(const int shift_size) const; + [[nodiscard]] inline BitVector SHIFT(const int shift_size) const; /// Positive shifts go left and negative go right; store result here, and return this object. - BitVector & SHIFT_SELF(const int shift_size); + inline BitVector & SHIFT_SELF(const int shift_size); /// Reverse the order of bits in the bitset - BitVector & REVERSE_SELF(); + inline BitVector & REVERSE_SELF(); /// Reverse order of bits in the bitset. - [[nodiscard]] BitVector REVERSE() const; + [[nodiscard]] inline BitVector REVERSE() const; /// Positive rotates go left and negative rotates go left (0 does nothing); /// return result. - [[nodiscard]] BitVector ROTATE(const int rotate_size) const; + [[nodiscard]] inline BitVector ROTATE(const int rotate_size) const; /// Positive rotates go right and negative rotates go left (0 does nothing); /// store result here, and return this object. - BitVector & ROTATE_SELF(const int rotate_size); + inline BitVector & ROTATE_SELF(const int rotate_size); /// Helper: call ROTATE with negative number instead template - BitVector & ROTL_SELF(); + inline BitVector & ROTL_SELF(); /// Helper for calling ROTATE with positive number template - BitVector & ROTR_SELF(); + inline BitVector & ROTR_SELF(); /// Addition of two BitVectors. /// Wraps if it overflows. /// Returns result. - [[nodiscard]] BitVector ADD(const BitVector & set2) const; + [[nodiscard]] inline BitVector ADD(const BitVector & set2) const; /// Addition of two BitVectors. /// Wraps if it overflows. /// Returns this object. - BitVector & ADD_SELF(const BitVector & set2); + inline BitVector & ADD_SELF(const BitVector & set2); /// Subtraction of two BitVectors. /// Wraps around if it underflows. /// Returns result. - [[nodiscard]] BitVector SUB(const BitVector & set2) const; + [[nodiscard]] inline BitVector SUB(const BitVector & set2) const; /// Subtraction of two BitVectors. /// Wraps if it underflows. /// Returns this object. - BitVector & SUB_SELF(const BitVector & set2); + inline BitVector & SUB_SELF(const BitVector & set2); /// Operator bitwise NOT... [[nodiscard]] inline BitVector operator~() const { return NOT(); } /// Operator bitwise AND... - [[nodiscard]] inline BitVector operator&(const BitVector & ar2) const { return AND(ar2); } + [[nodiscard]] inline BitVector operator&(const BitVector & ar2) const { + emp_assert(size() == ar2.size(), size(), ar2.size()); + return AND(ar2); + } /// Operator bitwise OR... - [[nodiscard]] inline BitVector operator|(const BitVector & ar2) const { return OR(ar2); } + [[nodiscard]] inline BitVector operator|(const BitVector & ar2) const { + emp_assert(size() == ar2.size(), size(), ar2.size()); + return OR(ar2); + } /// Operator bitwise XOR... - [[nodiscard]] inline BitVector operator^(const BitVector & ar2) const { return XOR(ar2); } + [[nodiscard]] inline BitVector operator^(const BitVector & ar2) const { + emp_assert(size() == ar2.size(), size(), ar2.size()); + return XOR(ar2); + } /// Operator shift left... [[nodiscard]] inline BitVector operator<<(const size_t shift_size) const { return SHIFT(-(int)shift_size); } @@ -678,13 +703,19 @@ namespace emp { [[nodiscard]] inline BitVector operator>>(const size_t shift_size) const { return SHIFT((int)shift_size); } /// Compound operator bitwise AND... - BitVector & operator&=(const BitVector & ar2) { return AND_SELF(ar2); } + BitVector & operator&=(const BitVector & ar2) { + emp_assert(size() == ar2.size()); return AND_SELF(ar2); + } /// Compound operator bitwise OR... - BitVector & operator|=(const BitVector & ar2) { return OR_SELF(ar2); } + BitVector & operator|=(const BitVector & ar2) { + emp_assert(size() == ar2.size()); return OR_SELF(ar2); + } /// Compound operator bitwise XOR... - BitVector & operator^=(const BitVector & ar2) { return XOR_SELF(ar2); } + BitVector & operator^=(const BitVector & ar2) { + emp_assert(size() == ar2.size()); return XOR_SELF(ar2); + } /// Compound operator for shift left... BitVector & operator<<=(const size_t shift_size) { return SHIFT_SELF(-(int)shift_size); } @@ -692,15 +723,22 @@ namespace emp { /// Compound operator for shift right... BitVector & operator>>=(const size_t shift_size) { return SHIFT_SELF((int)shift_size); } - // ========= Standard Library Compatability ========= // + // ========= Standard Library Compatibility ========= // // A set of functions to allow drop-in replacement with std::bitset. [[nodiscard]] size_t size() const { return num_bits; } void resize(std::size_t new_size) { Resize(new_size); } + void push_back(bool value) { PushBack(value); } + [[nodiscard]] auto at(size_t pos) { return operator[](pos); } + [[nodiscard]] auto at(size_t pos) const { return operator[](pos); } + [[nodiscard]] auto front() { return at(0); } + [[nodiscard]] auto front() const { return at(0); } + [[nodiscard]] auto back() { return at(GetSize()-1); } + [[nodiscard]] auto back() const { return at(GetSize()-1); } [[nodiscard]] bool all() const { return All(); } [[nodiscard]] bool any() const { return Any(); } [[nodiscard]] bool none() const { return !Any(); } - size_t count() const { return CountOnes(); } + [[nodiscard]] size_t count() const { return CountOnes(); } BitVector & flip() { return Toggle(); } BitVector & flip(size_t pos) { return Toggle(pos); } BitVector & flip(size_t start, size_t end) { return Toggle(start, end); } @@ -727,6 +765,8 @@ namespace emp { for (size_t i = 0; i < NUM_FIELDS; i++) bits[i] = in[i]; } + #ifndef DOXYGEN_SHOULD_SKIP_THIS + // Move bits from one position in the genome to another; leave old positions unchanged. // @CAO: Can speed up by focusing only on the moved fields (i.e., don't shift unused bits). void BitVector::RawCopy(const size_t from_start, const size_t from_stop, const size_t to) { @@ -748,6 +788,8 @@ namespace emp { OR_SELF(move_bits); // Merge bitstrings together. } + #endif // DOXYGEN_SHOULD_SKIP_THIS + template BitVector & BitVector::ApplyRange(const FUN_T & fun, size_t start, size_t stop) { if (start == stop) return *this; // Empty range. @@ -756,7 +798,7 @@ namespace emp { emp_assert(stop <= num_bits, stop, num_bits); // Stop cannot be past the end of the bits const size_t start_pos = FieldPos(start); // Identify the start position WITHIN a bit field. const size_t stop_pos = FieldPos(stop); // Identify the stop position WITHIN a bit field. - size_t start_field = FieldID(start); // Ideftify WHICH bit field we're starting in. + size_t start_field = FieldID(start); // Identify WHICH bit field we're starting in. const size_t stop_field = FieldID(stop-1); // Identify the last field where we actually make a change. // If the start field and stop field are the same, mask off the middle. @@ -876,37 +918,38 @@ namespace emp { field_t & n = bits[0]; size_t c = shift_size; - // Mask necessary to suprress shift count overflow warnings. + // Mask necessary to suppress shift count overflow warnings. c &= FIELD_LOG2_MASK; n = (n<>( (-(c+FIELD_BITS-num_bits)) & FIELD_LOG2_MASK )); } else if (NUM_FIELDS < 32) { // For small BitVectors, shifting L/R and ORing is faster. - emp::BitVector dup(*this); + BitVector dup(*this); dup.ShiftLeft(shift_size); ShiftRight(num_bits - shift_size); OR_SELF(dup); } - else { // For big BitVectors, manual rotating is fater + else { // For big BitVectors, manual rotating is faster // Note: we already modded shift_size by num_bits, so no need to mod by FIELD_SIZE - const int field_shift = ( shift_size + EndGap() ) / FIELD_BITS; + const size_t field_shift = ( shift_size + EndGap() ) / FIELD_BITS; // If we field shift, we need to shift bits by (FIELD_BITS - NumEndBits()) // to account for the filler that gets pulled out of the middle - const int bit_shift = NumEndBits() && (shift_size + field_shift ? EndGap() : 0) % FIELD_BITS; - const int bit_overflow = FIELD_BITS - bit_shift; + const size_t field_gap = field_shift ? EndGap() : 0; + const size_t bit_shift = NumEndBits() && (shift_size + field_gap) % FIELD_BITS; + const size_t bit_overflow = FIELD_BITS - bit_shift; // if rotating more than field capacity, we need to rotate fields auto field_span = FieldSpan(); std::rotate( field_span.rbegin(), - field_span.rbegin()+field_shift, + field_span.rbegin()+static_cast(field_shift), field_span.rend() ); // if necessary, shift filler bits out of the middle if (NumEndBits()) { - const int filler_idx = (LastField() + field_shift) % NUM_FIELDS; - for (int i = filler_idx + 1; i < (int)NUM_FIELDS; ++i) { + const size_t filler_idx = (LastField() + field_shift) % NUM_FIELDS; + for (size_t i = filler_idx + 1; i < NUM_FIELDS; ++i) { bits[i-1] |= bits[i] << NumEndBits(); bits[i] >>= (FIELD_BITS - NumEndBits()); } @@ -922,7 +965,7 @@ namespace emp { bits[LastField()] ); - for (int i = LastField(); i > 0; --i) { + for (size_t i = LastField(); i > 0; --i) { bits[i] <<= bit_shift; bits[i] |= (bits[i-1] >> bit_overflow); } @@ -952,21 +995,21 @@ namespace emp { field_t & n = bits[0]; size_t c = shift_size; - // mask necessary to suprress shift count overflow warnings + // mask necessary to suppress shift count overflow warnings c &= FIELD_LOG2_MASK; n = (n>>c) | (n<<( (num_bits-c) & FIELD_LOG2_MASK )); } else if (NUM_FIELDS < 32) { // for small BitVectors, shifting L/R and ORing is faster - emp::BitVector dup(*this); + BitVector dup(*this); dup.ShiftRight(shift_size); ShiftLeft(num_bits - shift_size); OR_SELF(dup); } else { - // for big BitVectors, manual rotating is fater + // for big BitVectors, manual rotating is faster const field_t field_shift = (shift_size / FIELD_BITS) % NUM_FIELDS; - const int bit_shift = shift_size % FIELD_BITS; + const size_t bit_shift = shift_size % FIELD_BITS; const field_t bit_overflow = FIELD_BITS - bit_shift; // if rotating more than field capacity, we need to rotate fields @@ -979,8 +1022,8 @@ namespace emp { // if necessary, shift filler bits out of the middle if (NumEndBits()) { - const int filler_idx = LastField() - field_shift; - for (int i = filler_idx + 1; i < (int)NUM_FIELDS; ++i) { + const size_t filler_idx = LastField() - field_shift; + for (size_t i = filler_idx + 1; i < NUM_FIELDS; ++i) { bits[i-1] |= bits[i] << NumEndBits(); bits[i] >>= (FIELD_BITS - NumEndBits()); } @@ -1130,7 +1173,7 @@ namespace emp { } /// Assignment operator. - BitVector & BitVector::operator=(const BitVector & in) { + BitVector & BitVector::operator=(const BitVector & in) & { emp_assert(in.OK()); if (&in == this) return *this; @@ -1150,7 +1193,7 @@ namespace emp { } /// Move operator. - BitVector & BitVector::operator=(BitVector && in) { + BitVector & BitVector::operator=(BitVector && in) & { emp_assert(&in != this); // in is an r-value, so this shouldn't be possible... if (bits) bits.DeleteArray(); // If we already have bits, get rid of them. num_bits = in.num_bits; // Update the number of bits... @@ -1163,7 +1206,7 @@ namespace emp { /// Assignment operator from a std::bitset. template - BitVector & BitVector::operator=(const std::bitset & bitset) { + BitVector & BitVector::operator=(const std::bitset & bitset) & { const size_t start_fields = NumFields(); num_bits = NUM_BITS; const size_t new_fields = NumFields(); @@ -1182,7 +1225,7 @@ namespace emp { } /// Assignment operator from a string of '0's and '1's. - BitVector & BitVector::operator=(const std::string & bitstring) { + BitVector & BitVector::operator=(const std::string & bitstring) & { const size_t start_fields = NumFields(); num_bits = bitstring.size(); const size_t new_fields = NumFields(); @@ -1246,7 +1289,7 @@ namespace emp { } // Otherwise bits is null; num_bits should be zero. - else emp_assert(num_bits == 0); + else { emp_assert(num_bits == 0); } return true; } @@ -1255,7 +1298,7 @@ namespace emp { // -------------------- Implementations of common accessors ------------------- - /// Retrive the bit value from the specified index. + /// Retrieve the bit value from the specified index. bool BitVector::Get(size_t index) const { emp_assert(index < num_bits, index, num_bits); const size_t field_id = FieldID(index); @@ -1371,7 +1414,7 @@ namespace emp { } /// Set all bits randomly, with a given number of them being on. - BitVector & BitVector::ChooseRandom(Random & random, const int target_ones, + BitVector & BitVector::ChooseRandom(Random & random, const size_t target_ones, const size_t start_pos, size_t stop_pos) { if (stop_pos == MAX_BITS) stop_pos = num_bits; @@ -1379,8 +1422,7 @@ namespace emp { emp_assert(stop_pos <= num_bits); const size_t target_size = stop_pos - start_pos; - emp_assert(target_ones >= 0); - emp_assert(target_ones <= (int) target_size); + emp_assert(target_ones <= target_size); // Approximate the probability of ones as a starting point. double p = ((double) target_ones) / (double) target_size; @@ -1546,7 +1588,7 @@ namespace emp { // ------------------------- Access Groups of bits ------------------------- - /// Retrive the byte at the specified byte index. + /// Retrieve the byte at the specified byte index. uint8_t BitVector::GetByte(size_t index) const { emp_assert(index < NumBytes(), index, NumBytes()); const size_t field_id = Byte2Field(index); @@ -1558,7 +1600,7 @@ namespace emp { /// @return Read-only span of BitVector's bytes. std::span BitVector::GetBytes() const { return std::span( - bits.ReinterpretCast(), + bits.ReinterpretCast().Raw(), NumBytes() ); } @@ -1584,7 +1626,7 @@ namespace emp { if (max_one < 64) return (double) GetUInt64(0); // To grab the most significant field, figure out how much to shift it by. - const int shift_bits = max_one - 63; + const size_t shift_bits = static_cast(max_one) - 63; double out_value = (double) (*this >> shift_bits).GetUInt64(0); out_value *= emp::Pow2(shift_bits); @@ -1711,11 +1753,11 @@ namespace emp { if (bit) SetRange(num_bits-num, num_bits); } - /// Insert bit(s) into any index of vector using bit magic. - /// Blog post on implementation reasoning: https://devolab.org/?p=2249 - /// @param index location to insert bit(s). - /// @param val value of bit(s) to insert (default true) - /// @param num number of bits to insert, default 1. + // Insert bit(s) into any index of vector using bit magic. + // Blog post on implementation reasoning: https://devolab.org/?p=2249 + // @param index location to insert bit(s). + // @param val value of bit(s) to insert (default true) + // @param num number of bits to insert, default 1. void BitVector::Insert(const size_t index, const bool val, const size_t num) { Resize(num_bits + num); // Adjust to new number of bits. BitVector low_bits(*this); // Copy current bits @@ -1727,9 +1769,9 @@ namespace emp { } - /// Delete bits from any index in a vector. - /// @param index location to delete bit(s). - /// @param num number of bits to delete, default 1. + // Delete bits from any index in a vector. + // @param index location to delete bit(s). + // @param num number of bits to delete, default 1. void BitVector::Delete(const size_t index, const size_t num) { emp_assert(index+num <= GetSize()); // Make sure bits to delete actually exist! RawCopy(index+num, num_bits, index); // Shift positions AFTER delete into place. @@ -1772,11 +1814,11 @@ namespace emp { /// Find the most-significant set-bit. int BitVector::FindMaxOne() const { // Find the max field with a one. - int max_field = NumFields() - 1; - while (max_field >= 0 && bits[max_field] == 0) max_field--; + size_t max_field = NumFields() - 1; + while (max_field > 0 && bits[max_field] == 0) max_field--; // If there are no ones, return -1. - if (max_field == -1) return -1; + if (bits[max_field] == 0) return -1; const field_t field = bits[max_field]; // Save a local copy of this field. field_t mask = (field_t) -1; // Mask off the bits still under consideration. @@ -1804,10 +1846,18 @@ namespace emp { /// Return positions of all ones. emp::vector BitVector::GetOnes() const { + emp::vector out_vals; + GetOnes(out_vals); + return out_vals; + } + + /// Return positions of all ones using a specified type. + template + emp::vector & BitVector::GetOnes(emp::vector & out_vals) const { // @CAO -- There are better ways to do this with bit tricks. - emp::vector out_vals(CountOnes()); - size_t cur_pos = 0; - for (size_t i = 0; i < num_bits; i++) { + out_vals.resize(CountOnes()); + T cur_pos = 0; + for (T i = 0; i < num_bits; i++) { if (Get(i)) out_vals[cur_pos++] = i; } return out_vals; @@ -2003,9 +2053,9 @@ namespace emp { // adapted from https://stackoverflow.com/questions/2602823/in-c-c-whats-the-simplest-way-to-reverse-the-order-of-bits-in-a-byte for (size_t i = 0; i < NumBytes(); ++i) { unsigned char & b = BytePtr()[i]; - b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; - b = (b & 0xCC) >> 2 | (b & 0x33) << 2; - b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + b = static_cast( (b & 0xF0) >> 4 | (b & 0x0F) << 4 ); + b = static_cast( (b & 0xCC) >> 2 | (b & 0x33) << 2 ); + b = static_cast( (b & 0xAA) >> 1 | (b & 0x55) << 1 ); } // shift out filler bits @@ -2055,7 +2105,7 @@ namespace emp { field_t & n = bits[0]; size_t c = shift_size; - // mask necessary to suprress shift count overflow warnings + // mask necessary to suppress shift count overflow warnings c &= FIELD_LOG2_MASK; n = (n<>( (-(c+FIELD_BITS-num_bits)) & FIELD_LOG2_MASK )); @@ -2063,34 +2113,34 @@ namespace emp { // note that we already modded shift_size by num_bits // so there's no need to mod by FIELD_SIZE here - int field_shift = NumEndBits() ? ( + size_t field_shift = NumEndBits() ? ( (shift_size + FIELD_BITS - NumEndBits()) / FIELD_BITS ) : ( shift_size / FIELD_BITS ); // if we field shift, we need to shift bits by (FIELD_BITS - NumEndBits()) // more to account for the filler that gets pulled out of the middle - int bit_shift = NumEndBits() && field_shift ? ( + size_t bit_shift = NumEndBits() && field_shift ? ( (shift_size + FIELD_BITS - NumEndBits()) % FIELD_BITS ) : ( shift_size % FIELD_BITS ); - int bit_overflow = FIELD_BITS - bit_shift; + size_t bit_overflow = FIELD_BITS - bit_shift; // if rotating more than field capacity, we need to rotate fields if (field_shift) { auto field_span = FieldSpan(); std::rotate( field_span.rbegin(), - field_span.rbegin()+field_shift, + field_span.rbegin()+static_cast(field_shift), field_span.rend() ); } // if necessary, shift filler bits out of the middle if (NumEndBits()) { - const int filler_idx = (LAST_FIELD + field_shift) % NUM_FIELDS; - for (int i = filler_idx + 1; i < (int)NUM_FIELDS; ++i) { + const size_t filler_idx = (LAST_FIELD + field_shift) % NUM_FIELDS; + for (size_t i = filler_idx + 1; i < NUM_FIELDS; ++i) { bits[i-1] |= bits[i] << NumEndBits(); bits[i] >>= (FIELD_BITS - NumEndBits()); } @@ -2106,7 +2156,7 @@ namespace emp { bits[LAST_FIELD] ); - for (int i = LAST_FIELD; i > 0; --i) { + for (size_t i = LAST_FIELD; i > 0; --i) { bits[i] <<= bit_shift; bits[i] |= (bits[i-1] >> bit_overflow); } @@ -2138,14 +2188,14 @@ namespace emp { field_t & n = bits[0]; size_t c = shift_size; - // mask necessary to suprress shift count overflow warnings + // mask necessary to suppress shift count overflow warnings c &= FIELD_LOG2_MASK; n = (n>>c) | (n<<( (num_bits-c) & FIELD_LOG2_MASK )); } else { field_t field_shift = (shift_size / FIELD_BITS) % NUM_FIELDS; - int bit_shift = shift_size % FIELD_BITS; + size_t bit_shift = shift_size % FIELD_BITS; field_t bit_overflow = FIELD_BITS - bit_shift; // if rotating more than field capacity, we need to rotate fields @@ -2160,8 +2210,8 @@ namespace emp { // if necessary, shift filler bits out of the middle if (NumEndBits()) { - int filler_idx = LAST_FIELD - field_shift; - for (int i = filler_idx + 1; i < (int)NUM_FIELDS; ++i) { + size_t filler_idx = LAST_FIELD - field_shift; + for (size_t i = filler_idx + 1; i < NUM_FIELDS; ++i) { bits[i-1] |= bits[i] << NumEndBits(); bits[i] >>= (FIELD_BITS - NumEndBits()); } @@ -2268,14 +2318,20 @@ namespace emp { // ---------------------- Implementations to work with standard library ---------------------- +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace std { - /// Hash function to allow BitVector to be used with maps and sets (must be in std). +#endif // DOXYGEN_SHOULD_SKIP_THIS + /// Hash function to allow BitVector to be used with maps and sets. + /// This is added to the std namespace so that BitVectors can be used + /// in data structures that require hashing (such as unordered_map) template <> - struct hash { - std::size_t operator()(const emp::BitVector & bv) const { + struct hash { + std::size_t operator()(const emp::old::BitVector & bv) const { return bv.Hash(); } }; +#ifndef DOXYGEN_SHOULD_SKIP_THIS } +#endif #endif // #ifndef EMP_BITS_BITVECTOR_HPP_INCLUDE diff --git a/include/emp/bits/Bits.hpp b/include/emp/bits/Bits.hpp new file mode 100644 index 0000000000..d279a90abf --- /dev/null +++ b/include/emp/bits/Bits.hpp @@ -0,0 +1,2355 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2022-23 +*/ +/** + * @file + * @brief A generic bit-handler to replace vector, etc +additional bitwise logic features. + * @note Status: RELEASE + * + * The Bits template allows the user to recreate the functionality of std::vector, + * array, std::bitset, and other such bit-handling classes. + * + * This class stores an arbitrary number of bits in a set of "fields" (typically 32 bits or 64 + * bits per field, depending on which should be faster.) Individual bits can be extracted, + * -or- bitwise logic (including more complex bit magic) can be used on the groups of bits. + * + * The template parameters are: + * DATA_T : How is memory managed? + * ZERO_LEFT : Should the index of zero be the left-most bit? (right-most if false) + * + * Specializations are: + * BitVector : A replacement for std::vector (index 0 is on left) + * BitValue : Like BitVector, but index 0 is on the right + * StaticBitVector : Like Bitvector, but max size and fixed memory. + * StaticBitValue : Like BitValue, but max size and fixed memory. + * BitArray : A replacement for std::array (index 0 is on left) + * BitSet : A replacement for std::bitset (index 0 is on right) + * + * In the case of replacements, the aim was for identical functionality, but many additional + * features, especially associated with bitwise logic operations. + * + * @note Compile with -O3 and -msse4.2 for fast bit counting. + * + * + * @todo Most of the operators don't check to make sure that both Bit groups are the same size. + * We should create versions (Intersection() and Union()?) that adjust sizes if needed. + * @todo Do small BitVector optimization. Currently we have number of bits (8 bytes) and a + * pointer to the memory for the bitset (another 8 bytes), but we could use those 16 bytes + * as 1 byte of size info followed by 15 bytes of bitset (120 bits!) + * @todo For large BitVectors we can use a factory to preserve/adjust bit info. That should be + * just as efficient than a reserve, but without the need to store extra in-class info. + * @todo Think about how iterators should work for Bit collections. It should probably go + * bit-by-bit, but there are very few circumstances where that would be useful. Going + * through the positions of all ones would be more useful, but perhaps less intuitive. + */ + +#ifndef EMP_BITS_BITS_HPP_INCLUDE +#define EMP_BITS_BITS_HPP_INCLUDE + + +#include +#include +#include +#include +#include +#include + +#include "../base/array.hpp" +#include "../base/assert.hpp" +#include "../base/error.hpp" +#include "../base/Ptr.hpp" +#include "../base/vector.hpp" +#include "../datastructs/hash_utils.hpp" +#include "../math/constants.hpp" +#include "../math/math.hpp" +#include "../math/Random.hpp" +#include "../math/Range.hpp" +#include "../meta/type_traits.hpp" + +#include "Bits_Data.hpp" +#include "_bitset_helpers.hpp" +#include "bitset_utils.hpp" + + +namespace emp { + + /// @brief A flexible base template to handle BitVector, BitArray, BitSet, & other combinations. + /// @tparam DATA_T How is this Bits object allowed to change size? + /// @tparam ZERO_LEFT Should the index of zero be the left-most bit? (right-most if false) + template + class Bits { + using this_t = Bits; + using field_t = bits_field_t; + + // All internal data (and base-level manipulators) for Bits. + DATA_T _data; + + static constexpr size_t FIELD_BITS = NUM_FIELD_BITS; + + // Number of bits needed to specify position in a field + mask + static constexpr size_t FIELD_LOG2 = static_cast(emp::Log2(FIELD_BITS)); + static constexpr field_t FIELD_LOG2_MASK = MaskLow(FIELD_LOG2); + + static constexpr field_t FIELD_0 = (field_t) 0; ///< All bits in a field set to 0 + static constexpr field_t FIELD_1 = (field_t) 1; ///< Least significant bit set to 1 + static constexpr field_t FIELD_255 = (field_t) 255; ///< Least significant 8 bits set to 1 + static constexpr field_t FIELD_ALL = ~FIELD_0; ///< All bits in a field set to 1 + + // Identify the field that a specified bit is in. + [[nodiscard]] static constexpr size_t FieldID(const size_t index) { return index / FIELD_BITS; } + + // Identify the position within a field where a specified bit is. + [[nodiscard]] static constexpr size_t FieldPos(const size_t index) { return index & (FIELD_BITS-1); } + + // Identify which field a specified byte position would be in. + [[nodiscard]] static constexpr size_t Byte2Field(const size_t index) { return index / sizeof(field_t); } + + // Convert a byte position in Bits to a byte position in the target field. + [[nodiscard]] static constexpr size_t Byte2FieldPos(const size_t index) { return FieldPos(index * 8); } + + [[nodiscard]] constexpr field_t MaskField(size_t mask_size) const { + return MaskLow(mask_size); + } + [[nodiscard]] constexpr field_t MaskField(size_t mask_size, size_t offset) const { + return MaskLow(mask_size) << offset; + } + + // Assume that the size of the bits has already been adjusted to be the size of the one + // being copied and only the fields need to be copied over. + void RawCopy(const Ptr from, size_t copy_fields=emp::MAX_SIZE_T); + + // Shortcut for RawCopy if we are copying a whole other Bits object. + template + void RawCopy(const Bits & in_bits) { + RawCopy(in_bits.FieldPtr(), in_bits.NumFields()); + } + + // Copy bits from one position in the genome to another; leave old positions unchanged. + constexpr void RawMove(const size_t from_start, const size_t from_stop, const size_t to); + + // Convert the bits to bytes (note that bits are NOT in order at the byte level!) + [[nodiscard]] emp::Ptr BytePtr() { return _data.BytePtr(); } + + // Convert the bits to const bytes array (note that bits are NOT in order at the byte level!) + [[nodiscard]] emp::Ptr BytePtr() const { return _data.BytePtr(); } + + // Any bits past the last "real" bit in the last field should be kept as zeros. + constexpr this_t & ClearExcessBits() { + if (_data.NumEndBits()) _data.bits[_data.LastField()] &= _data.EndMask(); + return *this; + } + + // Apply a transformation to each bit field in a specified range. + template + Bits & ApplyRange(const FUN_T & fun, size_t start, size_t stop); + + // Helper: call SHIFT with positive number + constexpr void ShiftLeft(const size_t shift_size); + + // Helper for calling SHIFT with negative number + // Raw indicates if we should keep bits that are technically out of range; may be needed if + // we are trying to shift bits back INTO range after another operation. + constexpr void ShiftRight(const size_t shift_size, bool raw=false); + + /// Helper: call ROTATE with negative number instead + constexpr void ROTL_SELF(const size_t shift_size_raw); + + /// Helper for calling ROTATE with positive number + constexpr void ROTR_SELF(const size_t shift_size_raw); + + public: + /// @brief Default constructor; will build the default number of bits (often 0, but not always) + /// @param init_val Initial value of all default bits. + Bits(bool init_val=0) { if (init_val) SetAll(); else Clear(); } + + /// @brief Build a new Bits with specified bit count and initialization (default 0) + Bits(size_t in_num_bits, bool init_val=false); + + // Prevent ambiguous conversions... + /// @brief Anything not otherwise defined for first argument, convert to size_t. + template ::value, int>::type = 0> + Bits(T in_num_bits, bool init_val=false) : Bits(static_cast(in_num_bits), init_val) {} + + /// @brief Copy constructor of existing bits object. + Bits(const Bits & in) = default; + + /// @brief Constructor for other type of existing bits object. + template + Bits(const Bits & in); + + /// @brief Move constructor of existing bit field. + Bits(this_t && in) = default; + + /// @brief Constructor to generate a Bits from a std::bitset. + template + explicit Bits(const std::bitset & bitset); + + /// @brief Constructor to generate a Bits from a string of '0's and '1's. + Bits(const std::string & bitstring); + + /// @brief Constructor to generate a Bits from a literal string of '0's and '1's. + Bits(const char * bitstring) : Bits(std::string(bitstring)) {} + + /// @brief Constructor to generate a random set of bits in the default size. + /// @param random Random number generator to use. + Bits(Random & random); + + /// @brief Constructor to generate random Bits with provided prob of 1's, default size. + /// @param random Random number generator to use. + /// @param p1 Probability of a bit being a one. + Bits(Random & random, const double p1); + + /// @brief Constructor to generate random Bits with specified # of ones, default size. + /// @param random Random number generator to use. + /// @param target_ones Number of ones to include in the Bits. + Bits(Random & random, const size_t target_ones); + + /// @brief Constructor to generate random Bits with specified # of ones, default size. + /// @param random Random number generator to use. + /// @param target_ones Number of ones to include in the Bits. + Bits(Random & random, const int target_ones) : Bits(random, (size_t) target_ones) { } + + /// @brief Constructor to generate a specified number of random Bits (with equal prob of 0 or 1). + Bits(size_t in_num_bits, Random & random); + + /// @brief Constructor to generate a random Bits with provided prob of 1's. + Bits(size_t in_num_bits, Random & random, const double p1); + + /// @brief Constructor to generate a random Bits with provided number of 1's. + Bits(size_t in_num_bits, Random & random, const size_t target_ones); + + /// @brief Constructor to generate a random Bits with provided number of 1's. + Bits(size_t in_num_bits, Random & random, const int target_ones) + : Bits(in_num_bits, random, (size_t) target_ones) { } + + /// @brief Initializer list constructor. + template Bits(const std::initializer_list l); + + /// @brief Copy, but with a resize. + template + Bits(const Bits & in, size_t new_size); + + /// @brief Destructor + ~Bits() = default; + + /// @brief Copy assignment operator. + Bits & operator=(const Bits & in) &; + + /// @brief Assignment operator for other Bits object + template + Bits & operator=(const Bits & in) &; + + /// @brief Move operator. + Bits & operator=(Bits && in) &; + + /// @brief Assignment operator from a std::bitset. + template + Bits & operator=(const std::bitset & bitset) &; + + /// @brief Assignment operator from a string of '0's and '1's. + Bits & operator=(const std::string & bitstring) &; + + /// @brief Assignment operator from a literal string of '0's and '1's. + Bits & operator=(const char * bitstring) & { return operator=(std::string(bitstring)); } + + /// @brief Assignment from another Bits object without changing size. + template + Bits & Import( + const Bits & from_bits, + const size_t from_start_pos=0, + size_t max_copy_bits=emp::MAX_SIZE_T + ); + + /// @brief Convert to a Bits of a different size. + template > + [[nodiscard]] OUT_T Export(size_t out_size, size_t start_bit=0) const; + + /// @brief Convert to a BitArray of a different size. + template + [[nodiscard]] Bits,true> + ExportArray(size_t start_bit=0) const { + return Export< Bits,true> >(NUM_BITS, start_bit); + } + + /// @brief concatenate another Bits object on to the end of this one. + template + Bits & Append(const Bits & in_bits); + + // @brief Scan this bitvector to make sure that there are no internal problems. + [[nodiscard]] bool OK() const { return _data.OK(); } + + + // ========= Accessors ========= // + + /// @brief How many bits do we currently have? + [[nodiscard]] constexpr auto GetSize() const { return _data.NumBits(); } + + /// @brief How many bits are locked in a compile time? + [[nodiscard]] static constexpr auto GetCTSize() { return DATA_T::NumCTBits(); } + + /// @brief How many bytes are in this Bits? (includes empty field space) + [[nodiscard]] constexpr auto GetNumBytes() const { return _data.NumBytes(); } + + /// @brief How many distinct values could be held in this Bits? + [[nodiscard]] constexpr double GetNumStates() const { return emp::Pow2(_data.NumBits()); } + + /// @brief Retrieve the bit value from the specified index. + [[nodiscard]] constexpr bool Get(size_t index) const; + + /// @brief A safe version of Get() for indexing out of range. Useful for representing collections. + [[nodiscard]] constexpr bool Has(size_t index) const { + return (index < _data.NumBits()) ? Get(index) : false; + } + + /// @brief Update the bit value at the specified index. + Bits & Set(size_t index, bool value=true); + + /// @brief Set all bits to 1. + Bits & SetAll(); + + /// @brief Set a range of bits to value (default one): [start, stop) + Bits & SetRange(size_t start, size_t stop, bool value=true) { + if (value) return ApplyRange([](field_t){ return FIELD_ALL; }, start, stop); + return Clear(start, stop); + } + + /// @brief Set all bits to 0. + Bits & Clear(); + + /// @brief Set specific bit to 0. + Bits & Clear(size_t index) { return Set(index, false); } + + /// @brief Set bits to 0 in the range [start, stop) + Bits & Clear(const size_t start, const size_t stop) { + return ApplyRange([](field_t) -> size_t { return 0; }, start, std::min(stop,GetSize())); + } + + + /// @brief Const index operator -- return the bit at the specified position. + [[nodiscard]] bool operator[](size_t index) const { return Get(index); } + + /// @brief Index operator; return proxy to bit at specified position usable as an lvalue. + BitProxy operator[](size_t index) { return BitProxy(*this, index); } + + /// @brief Change every bit in the sequence. + Bits & Toggle() { return NOT_SELF(); } + + /// @brief Change a specified bit to the opposite value + Bits & Toggle(size_t index); + + /// @brief Flips all the bits in a range [start, end) + Bits & Toggle(size_t start, size_t stop) + { return ApplyRange([](field_t x){ return ~x; }, start, stop); } + + /// @brief Return true if ANY bits are set to 1, otherwise return false. + [[nodiscard]] bool Any() const; + + /// @brief Return true if NO bits are set to 1, otherwise return false. + [[nodiscard]] bool None() const { return !Any(); } + + /// @brief Return true if ALL bits are set to 1, otherwise return false. + // @CAO: Can speed up by not duplicating Bits; fields should be all 1, last should be mask. + [[nodiscard]] bool All() const { return (~(*this)).None(); } + + /// @brief Resize this Bits object to have the specified number of bits (if allowed) + Bits & Resize(size_t new_bits) { _data.RawResize(new_bits, true); return *this; } + + + // ========= Randomization functions ========= // + + /// @brief Set all bits randomly, with a 50% probability of being a 0 or 1. + Bits & Randomize(Random & random); + + /// @brief Set all bits randomly, with probability specified at compile time. + template + Bits & RandomizeP(Random & random, const size_t start_pos=0, size_t stop_pos=MAX_SIZE_T); + + /// @brief Set all bits randomly, with a given probability of being a one. + Bits & Randomize(Random & random, const double p, + const size_t start_pos=0, size_t stop_pos=MAX_SIZE_T); + + /// @brief Set all bits randomly, with a given number of ones. + Bits & ChooseRandom(Random & random, const size_t target_ones, + const size_t start_pos=0, size_t stop_pos=MAX_SIZE_T); + + /// @brief Flip random bits with a given probability. + Bits & FlipRandom(Random & random, const double p, + const size_t start_pos=0, size_t stop_pos=MAX_SIZE_T); + + /// @brief Set random bits with a given probability (does not check if already set.) + Bits & SetRandom(Random & random, const double p, + const size_t start_pos=0, size_t stop_pos=MAX_SIZE_T); + + /// @brief Unset random bits with a given probability (does not check if already zero.) + Bits & ClearRandom(Random & random, const double p, + const size_t start_pos=0, size_t stop_pos=MAX_SIZE_T); + + /// @brief Flip a specified number of random bits. + Bits & FlipRandomCount(Random & random, const size_t target_bits); + + /// @brief Set a specified number of random bits (does not check if already set.) + Bits & SetRandomCount(Random & random, const size_t target_bits); + + /// @brief Unset a specified number of random bits (does not check if already zero.) + Bits & ClearRandomCount(Random & random, const size_t target_bits); + + + // ========= Comparison Operators ========= // + + /// @brief Compare two bits objects, even with different template arguments. + template + [[nodiscard]] bool operator==(const Bits & in) const; + template + [[nodiscard]] bool operator!=(const Bits & in) const { return !(*this == in); } + template + [[nodiscard]] bool operator< (const Bits & in) const; + template + [[nodiscard]] bool operator> (const Bits & in) const { return in < *this; } + template + [[nodiscard]] bool operator<=(const Bits & in) const { return !(in < *this); } + template + [[nodiscard]] bool operator>=(const Bits & in) const { return !(*this < in); } + + + // ========= Conversion Operators ========= // + + /// @brief Automatically convert Bits to other vector types. + template operator emp::vector(); + + /// @brief Casting a bit array to bool identifies if ANY bits are set to 1. + explicit operator bool() const { return Any(); } + + + // ========= Access Groups of bits ========= // + + /// @brief Retrieve the byte at the specified byte index. + [[nodiscard]] uint8_t GetByte(size_t index) const; + + /// @brief et a read-only view into the internal array used by Bits. + /// @return Read-only span of Bits's bytes. + [[nodiscard]] auto GetBytes() const { return _data.AsByteSpan(); } + + /// @brief Return a span with all fields in order. + [[nodiscard]] std::span FieldSpan() { + return std::span(_data.FieldPtr().Raw(), _data.NumFields()); + } + + /// @brief Return a const span with all fields in order. + [[nodiscard]] std::span FieldSpan() const { + return std::span(_data.FieldPtr().Raw(), _data.NumFields()); + } + + [[nodiscard]] size_t NumFields() const { return _data.NumFields(); } + + /// @brief Return a pointer to the set of fields. + [[nodiscard]] auto FieldPtr() { return _data.FieldPtr(); } + + /// @brief Return a const pointer to the set of fields. + [[nodiscard]] auto FieldPtr() const { return _data.FieldPtr(); } + + /// @brief Get a read-only pointer to the internal array used by Bits. + /// (note that bits are NOT in order at the byte level!) + /// @return Read-only pointer to Bits' bytes. + [[nodiscard]] emp::Ptr RawBytes() const { return BytePtr(); } + + /// @brief Update the byte at the specified byte index. + void SetByte(size_t index, uint8_t value); + + /// @brief Get overall base-2 value of this Bits, returning as a double. + [[nodiscard]] double GetValue() const; + + /// @brief Get specified type at a given index (in steps of that type size) + template + [[nodiscard]] T GetValueAtIndex(const size_t index) const; + + /// @brief Retrieve the 8-bit uint from the specified uint index. + [[nodiscard]] uint8_t GetUInt8(size_t index) const { return GetValueAtIndex(index); } + + /// @brief Retrieve the 16-bit uint from the specified uint index. + [[nodiscard]] uint16_t GetUInt16(size_t index) const { return GetValueAtIndex(index); } + + /// @brief Retrieve the 32-bit uint from the specified uint index. + [[nodiscard]] uint32_t GetUInt32(size_t index) const { return GetValueAtIndex(index); } + + /// @brief Retrieve the 64-bit uint from the specified uint index. + [[nodiscard]] uint64_t GetUInt64(size_t index) const { return GetValueAtIndex(index); } + + /// @brief By default, retrieve the 32-bit uint from the specified uint index. + [[nodiscard]] uint32_t GetUInt(size_t index) const { return GetUInt32(index); } + + + /// @brief Set specified type at a given index (in steps of that type size) + template Bits & SetValueAtIndex(const size_t index, T value); + + /// @brief Update the 8-bit uint at the specified uint index. + void SetUInt8(const size_t index, uint8_t value) { SetValueAtIndex(index, value); } + + /// @brief Update the 16-bit uint at the specified uint index. + void SetUInt16(const size_t index, uint16_t value) { SetValueAtIndex(index, value); } + + /// @brief Update the 32-bit uint at the specified uint index. + void SetUInt32(const size_t index, uint32_t value) { SetValueAtIndex(index, value); } + + /// @brief Update the 64-bit uint at the specified uint index. + void SetUInt64(const size_t index, uint64_t value) { SetValueAtIndex(index, value); } + + /// @brief By default, update the 32-bit uint at the specified uint index. + void SetUInt(const size_t index, uint32_t value) { SetUInt32(index, value); } + + + /// @briefGet specified type starting at a given BIT position. + template + [[nodiscard]] T GetValueAtBit(const size_t index) const; + + /// @brief Retrieve the 8-bit uint from the specified uint index. + [[nodiscard]] uint8_t GetUInt8AtBit(size_t index) const { return GetValueAtBit(index); } + + /// @brief Retrieve the 16-bit uint from the specified uint index. + [[nodiscard]] uint16_t GetUInt16AtBit(size_t index) const { return GetValueAtBit(index); } + + /// @brief Retrieve the 32-bit uint from the specified uint index. + [[nodiscard]] uint32_t GetUInt32AtBit(size_t index) const { return GetValueAtBit(index); } + + /// @brief Retrieve the 64-bit uint from the specified uint index. + [[nodiscard]] uint64_t GetUInt64AtBit(size_t index) const { return GetValueAtBit(index); } + + /// @brief By default, retrieve the 32-bit uint from the specified uint index. + [[nodiscard]] uint32_t GetUIntAtBit(size_t index) const { return GetUInt32AtBit(index); } + + + template Bits & SetValueAtBit(const size_t index, T value); + + /// @brief Update the 8-bit uint at the specified uint index. + void SetUInt8AtBit(const size_t index, uint8_t value) { SetValueAtBit(index, value); } + + /// @brief Update the 16-bit uint at the specified uint index. + void SetUInt16AtBit(const size_t index, uint16_t value) { SetValueAtBit(index, value); } + + /// @brief Update the 32-bit uint at the specified uint index. + void SetUInt32AtBit(const size_t index, uint32_t value) { SetValueAtBit(index, value); } + + /// @brief Update the 64-bit uint at the specified uint index. + void SetUInt64AtBit(const size_t index, uint64_t value) { SetValueAtBit(index, value); } + + /// @brief By default, update the 32-bit uint at the specified uint index. + void SetUIntAtBit(const size_t index, uint32_t value) { SetUInt32AtBit(index, value); } + + + // ========= Other Analyses ========= // + + /// @brief A simple hash function for bit vectors. + [[nodiscard]] std::size_t Hash(size_t start_field=0) const; + + /// @brief Count the number of ones in Bits. + [[nodiscard]] constexpr size_t CountOnes() const; + + /// @brief Count the number of ones in a range within Bits. [start, end) + [[nodiscard]] constexpr size_t CountOnes(size_t start, size_t end) const; + + /// @brief Faster counting of ones for very sparse bit vectors. + [[nodiscard]] constexpr size_t CountOnes_Sparse() const; + + /// @brief Count the number of zeros in Bits. + [[nodiscard]] constexpr size_t CountZeros() const { return GetSize() - CountOnes(); } + + /// @brief Pop the last bit in the vector. + /// @return value of the popped bit. + bool PopBack(); + + /// @brief Push given bit(s) onto the back of a vector. + /// @param bit value of bit to be pushed. + /// @param num number of bits to be pushed. + void PushBack(const bool bit=true, const size_t num=1); + + /// @brief Push given bit(s) onto the front of a vector. + /// @param bit value of bit to be pushed. + /// @param num number of bits to be pushed. + void PushFront(const bool bit=true, const size_t num=1); + + /// @brief Insert bit(s) into any index of vector using bit magic. + /// Blog post on implementation reasoning: https://devolab.org/?p=2249 + /// @param index location to insert bit(s). + /// @param val value of bit(s) to insert. + /// @param num number of bits to insert, default 1. + void Insert(const size_t index, const bool val=true, const size_t num=1); + + /// @brief Delete bits from any index in a vector. + // TODO: consider a bit magic approach here. + /// @param index location to delete bit(s). + /// @param num number of bits to delete, default 1. + void Delete(const size_t index, const size_t num=1); + + /// @brief Return the position of the first one; return -1 if no ones in vector. + [[nodiscard]] int FindOne() const; + + /// @brief Return the position of the first zero; return -1 if no zeroes in vector. + [[nodiscard]] int FindZero() const; + + + /// Deprecated: Return the position of the first one; return -1 if no ones in vector. + [[deprecated("Renamed to more accurate FindOne()")]] + [[nodiscard]] int FindBit() const { return FindOne(); } + + /// @brief Return the position of the first one after start_pos (or -1 if none) + /// You can loop through all 1-bit positions of Bits object "bits" with: + /// + /// for (int pos = bits.FindOne(); pos >= 0; pos = bits.FindOne(pos+1)) { ... } + + [[nodiscard]] int FindOne(const size_t start_pos) const; + + /// @brief Return the position of the first zero after start_pos (or -1 if none) + /// You can loop through all 0-bit positions of Bits object "bits" with: + /// + /// for (int pos = bits.FindZero(); pos >= 0; pos = bits.FindZero(pos+1)) { ... } + + [[nodiscard]] int FindZero(const size_t start_pos) const; + + /// @brief Special version of FindOne takes int; common way to call. + [[nodiscard]] int FindOne(int start_pos) const { + return FindOne(static_cast(start_pos)); + } + + /// @brief Special version of FindZero takes int; common way to call. + [[nodiscard]] int FindZero(int start_pos) const { + return FindZero(static_cast(start_pos)); + } + + /// Deprecated version of FindOne(). + [[deprecated("Renamed to more accurate FindOne(start_pos)")]] + [[nodiscard]] int FindBit(const size_t start_pos) const; + + /// @brief Find the most-significant set-bit. + [[nodiscard]] int FindMaxOne() const; + + /// @brief Return the position of the first one and change it to a zero. Return -1 if none. + int PopOne(); + + /// Deprecated version of PopOne(). + [[deprecated("Renamed to more accurate PopOne()")]] + int PopBit() { return PopOne(); } + + /// @brief Return vector of positions of all ones. + [[nodiscard]] emp::vector GetOnes() const; + + /// @brief Collect positions of ones in the provided vector (allows id type choice) + template + emp::vector & GetOnes(emp::vector & out_vals) const; + + /// @brief Find the length of the longest continuous series of ones. + [[nodiscard]] size_t LongestSegmentOnes() const; + + /// @brief Find ids of all groups of ones. + /// @return A vector of ranges that identify all ids of ones. + [[nodiscard]] emp::vector> GetRanges() const; + + /// @brief Return true if any ones are in common with another Bits. + [[nodiscard]] bool HasOverlap(const Bits & in) const; + + + // ========= Print/String Functions ========= // + + /// @brief Convert a specified bit to a character. + [[nodiscard]] char GetAsChar(size_t id) const { return Get(id) ? '1' : '0'; } + + /// @brief Convert this Bits to a vector string [index 0 based on ZERO_LEFT] + [[nodiscard]] std::string ToString() const; + + /// @brief Convert this Bits to an array-based string [index 0 on left] + [[nodiscard]] std::string ToArrayString() const; + + /// @brief Convert this Bits to a numerical string [index 0 on right] + [[nodiscard]] std::string ToBinaryString() const; + + /// @brief Convert this Bits to a series of IDs + [[nodiscard]] std::string ToIDString(const std::string & spacer=" ") const; + + /// @brief Convert this Bits to a series of IDs with ranges condensed. + [[nodiscard]] std::string ToRangeString(const std::string & spacer=",", + const std::string & ranger="-") const; + + /// @brief Regular print function (from least significant bit to most) + void Print(std::ostream & out=std::cout) const { out << ToString(); } + + /// @brief Numerical print function (from most significant bit to least) + void PrintBinary(std::ostream & out=std::cout) const { out << ToBinaryString(); } + + /// @brief Print from smallest bit position to largest. + void PrintArray(std::ostream & out=std::cout) const { out << ToArrayString(); } + + /// @brief Print a space between each field (or other provided spacer) + void PrintFields(std::ostream & out=std::cout, const std::string & spacer=" ") const; + + /// @brief Print out details about the internals of Bits. + void PrintDebug(std::ostream & out=std::cout, const std::string & label="") const; + + /// @brief Print the positions of all one bits, spaces are the default separator. + void PrintOneIDs(std::ostream & out=std::cout, const std::string & spacer=" ") const; + + /// @brief Print the ones in a range format. E.g., 2-5,7,10-15 + void PrintAsRange(std::ostream & out=std::cout, + const std::string & spacer=",", + const std::string & ranger="-") const; + + /// @brief Overload ostream operator to return Print. + friend std::ostream& operator<<(std::ostream &out, const Bits & bits) { + bits.Print(out); + return out; + } + + + // ========= Boolean Logic and Shifting Operations ========= // + + /// @brief Perform a Boolean NOT with this Bits, store result here, and return this object. + Bits & NOT_SELF(); + + /// @brief Perform a Boolean AND with this Bits, store result here, and return this object. + Bits & AND_SELF(const Bits & bits2); + + /// @brief Perform a Boolean OR with this Bits, store result here, and return this object. + Bits & OR_SELF(const Bits & bits2); + + /// @brief Perform a Boolean NAND with this Bits, store result here, and return this object. + Bits & NAND_SELF(const Bits & bits2); + + /// @brief Perform a Boolean NOR with this Bits, store result here, and return this object. + Bits & NOR_SELF(const Bits & bits2); + + /// @brief Perform a Boolean XOR with this Bits, store result here, and return this object. + Bits & XOR_SELF(const Bits & bits2); + + /// @brief Perform a Boolean EQU with this Bits, store result here, and return this object. + Bits & EQU_SELF(const Bits & bits2); + + + /// @brief Perform a Boolean NOT on this Bits and return the result. + [[nodiscard]] Bits NOT() const { return Bits(*this).NOT_SELF(); } + + /// @brief Perform a Boolean AND on this Bits and return the result. + [[nodiscard]] Bits AND(const Bits & bits2) const { return Bits(*this).AND_SELF(bits2); } + + /// @brief Perform a Boolean OR on this Bits and return the result. + [[nodiscard]] Bits OR(const Bits & bits2) const { return Bits(*this).OR_SELF(bits2); } + + /// @brief Perform a Boolean NAND on this Bits and return the result. + [[nodiscard]] Bits NAND(const Bits & bits2) const { return Bits(*this).NAND_SELF(bits2); } + + /// @brief Perform a Boolean NOR on this Bits and return the result. + [[nodiscard]] Bits NOR(const Bits & bits2) const { return Bits(*this).NOR_SELF(bits2); } + + /// @brief Perform a Boolean XOR on this Bits and return the result. + [[nodiscard]] Bits XOR(const Bits & bits2) const { return Bits(*this).XOR_SELF(bits2); } + + /// @brief Perform a Boolean EQU on this Bits and return the result. + [[nodiscard]] Bits EQU(const Bits & bits2) const { return Bits(*this).EQU_SELF(bits2); } + + + /// @brief Positive shifts left and negative right (0 does nothing); return result. + [[nodiscard]] Bits SHIFT(const int shift_size) const; + + /// @brief Positive shifts left and negative right; store result here, and return *this. + Bits & SHIFT_SELF(const int shift_size); + + /// @brief Reverse the order of bits in the bitset + Bits & REVERSE_SELF(); + + /// @brief Reverse order of bits in the bitset. + [[nodiscard]] Bits REVERSE() const; + + /// @brief Positive rotates right and negative goes left; return result. + [[nodiscard]] Bits ROTATE(const int rotate_size) const; + + /// @brief Positive rotates right and negative goes left; store here, and return *this. + Bits & ROTATE_SELF(const int rotate_size); + + /// @brief Sums two Bits objects (following uint rules); returns result. + [[nodiscard]] Bits ADD(const Bits & set2) const; + + /// @brief Sums another Bits object onto this one (following uint rules); returns *this. + Bits & ADD_SELF(const Bits & set2); + + /// @brief Subtracts on Bits object from another (following uint rules); returns result. + [[nodiscard]] Bits SUB(const Bits & set2) const; + + /// @brief Subtracts another Bits object from this one (following uint rules); returns *this. + Bits & SUB_SELF(const Bits & set2); + + + /// @brief Operator bitwise NOT... + [[nodiscard]] inline Bits operator~() const { return NOT(); } + + /// @brief Operator bitwise AND... + [[nodiscard]] inline Bits operator&(const Bits & ar2) const { + emp_assert(size() == ar2.size(), size(), ar2.size()); + return AND(ar2); + } + + /// @brief Operator bitwise OR... + [[nodiscard]] inline Bits operator|(const Bits & ar2) const { + emp_assert(size() == ar2.size(), size(), ar2.size()); + return OR(ar2); + } + + /// @brief Operator bitwise XOR... + [[nodiscard]] inline Bits operator^(const Bits & ar2) const { + emp_assert(size() == ar2.size(), size(), ar2.size()); + return XOR(ar2); + } + + /// @brief Operator shift left... + [[nodiscard]] inline Bits operator<<(const size_t shift_size) const { return SHIFT(-(int)shift_size); } + + /// @brief Operator shift right... + [[nodiscard]] inline Bits operator>>(const size_t shift_size) const { return SHIFT((int)shift_size); } + + /// @brief Compound operator bitwise AND... + Bits & operator&=(const Bits & ar2) { + emp_assert(size() == ar2.size()); return AND_SELF(ar2); + } + + /// @brief Compound operator bitwise OR... + Bits & operator|=(const Bits & ar2) { + emp_assert(size() == ar2.size()); return OR_SELF(ar2); + } + + /// @brief Compound operator bitwise XOR... + Bits & operator^=(const Bits & ar2) { + emp_assert(size() == ar2.size()); return XOR_SELF(ar2); + } + + /// @brief Compound operator for shift left... + Bits & operator<<=(const size_t shift_size) { return SHIFT_SELF(-(int)shift_size); } + + /// @brief Compound operator for shift right... + Bits & operator>>=(const size_t shift_size) { return SHIFT_SELF((int)shift_size); } + + /// @brief Operator plus... + [[nodiscard]] Bits operator+(const Bits & ar2) const { return ADD(ar2); } + + /// @brief Operator minus... + [[nodiscard]] Bits operator-(const Bits & ar2) const { return SUB(ar2); } + + /// @brief Compound operator plus... + const Bits & operator+=(const Bits & ar2) { return ADD_SELF(ar2); } + + /// @brief Compound operator minus... + const Bits & operator-=(const Bits & ar2) { return SUB_SELF(ar2); } + + + // ========= Cereal Compatibility ========= // + + /// @brief Setup this bits object so that it can be stored in an archive and re-loaded. + template + void serialize(Archive & ar) { ar(_data); } + + + // ========= Standard Library Compatability ========= // + // A set of functions to allow drop-in replacement with std::bitset. + + [[nodiscard]] constexpr size_t size() const { return _data.NumBits(); } + [[nodiscard]] auto & at(size_t pos) { return operator[](pos); } + [[nodiscard]] auto at(size_t pos) const { return operator[](pos); } + [[nodiscard]] auto & front() { return at(0); } + [[nodiscard]] auto front() const { return at(0); } + [[nodiscard]] auto & back() { return at(GetSize()-1); } + [[nodiscard]] auto back() const { return at(GetSize()-1); } + void resize(std::size_t new_size) { Resize(new_size); } + void push_back(const bool bit=true, const size_t num=1) { PushBack(bit, num); } + void pop_back() { resize(GetSize() - 1); } + [[nodiscard]] constexpr bool all() const { return All(); } + [[nodiscard]] constexpr bool any() const { return Any(); } + [[nodiscard]] constexpr bool none() const { return !Any(); } + [[nodiscard]] constexpr size_t count() const { return CountOnes(); } + Bits & flip() { return Toggle(); } + Bits & flip(size_t pos) { return Toggle(pos); } + Bits & flip(size_t start, size_t end) { return Toggle(start, end); } + void reset() { Clear(); } + void reset(size_t id) { Set(id, false); } + void set() { SetAll(); } + void set(size_t id) { Set(id); } + [[nodiscard]] bool test(size_t index) const { return Get(index); } + auto data() { return FieldSpan(); } + auto data() const { return FieldSpan(); } + }; + + + + // ------------------------ Implementations for Internal Functions ------------------------ + + template + void Bits:: + RawCopy(const Ptr from, size_t num_fields) + { + // If num_fields was not specified, set it to the max number of fields. + if (num_fields == emp::MAX_SIZE_T) num_fields = _data.NumFields(); + + emp_assert(num_fields <= _data.NumFields(), "Trying to RawCopy() more fields than can fit."); + + for (size_t i = 0; i < num_fields; i++) _data.bits[i] = from[i]; + } + + // Move bits from one position in the genome to another; leave old positions unchanged. + // All positions are requires to exist and memory must be available for the move. + // @CAO: Can speed up by focusing only on the moved fields (i.e., don't shift unused bits). + template + constexpr void Bits:: + RawMove(const size_t from_start, const size_t from_stop, const size_t to) + { + emp_assert(from_start <= from_stop); // Must move legal region. + emp_assert(from_stop <= _data.NumBits()); // Cannot move from past end. + emp_assert(to <= _data.NumBits()); // Must move to somewhere legal. + + const size_t move_size = from_stop - from_start; // How big is the chunk to move? + emp_assert(to + move_size <= _data.NumBits()); // Must fit in new position. + + // If nothing to copy OR already in place, stop right there. + if (move_size == 0 || from_start == to) return; + + const size_t to_stop = to + move_size; // Where is the end to move it to? + const int shift = (int) from_start - (int) to; // How far will the moved piece shift? + this_t move_bits(*this); // Place to hold moved bits. + move_bits.SHIFT_SELF(shift); // Put the moved bits in place. + Clear(to, to_stop); // Make room for the moved bits. + move_bits.Clear(0, to); // Clear everything BEFORE moved bits. + move_bits.Clear(to_stop, _data.NumBits()); // Clear everything AFTER moved bits. + OR_SELF(move_bits); // Merge bit strings together. + } + + template + template + Bits & Bits:: + ApplyRange(const FUN_T & fun, size_t start, size_t stop) + { + emp_assert(start <= stop, start, stop, _data.NumBits()); // Start cannot be after stop. + emp_assert(stop <= _data.NumBits(), stop, _data.NumBits()); // Stop must be in range. + + if (start == stop) return *this; // Empty range. + + const size_t start_pos = FieldPos(start); // Start position WITHIN a bit field. + const size_t stop_pos = FieldPos(stop); // Stop position WITHIN a bit field. + size_t start_field = FieldID(start); // ID of bit field we're starting in. + const size_t stop_field = FieldID(stop); // ID of last field to actively scan. + + // If all bits are in the same field, mask off the middle. + if (start_field == FieldID(stop-1)) { + const size_t apply_bits = stop - start; // How many bits to change? + const field_t mask = MaskField(apply_bits, start_pos); // Target change bits with a mask. + field_t & target = _data.bits[start_field]; // Isolate the field to change. + target = (target & ~mask) | (fun(target) & mask); // Update targeted bits! + } + + // Otherwise mask the ends and fully modify the chunks in between. + else { + // If we're only using a portions of start field, mask it and setup. + if (start_pos != 0) { + const size_t start_bits = FIELD_BITS - start_pos; // How many bits in start field? + const field_t mask = MaskField(start_bits, start_pos); // Target start bits with a mask. + field_t & target = _data.bits[start_field]; // Isolate the field to change. + target = (target & ~mask) | (fun(target) & mask); // Update targeted bits! + start_field++; // Move to the next field. + } + + // Middle fields + for (size_t cur_field = start_field; cur_field < stop_field; cur_field++) { + _data.bits[cur_field] = fun(_data.bits[cur_field]); + } + + // Set portions of stop field + if (stop_pos != 0) { + const field_t mask = MaskField(stop_pos); // Target end bits with a mask. + field_t & target = _data.bits[stop_field]; // Isolate the field to change. + target = (target & ~mask) | (fun(target) & mask); // Update targeted bits! + } + } + + return *this; + } + + template + constexpr void Bits::ShiftLeft(const size_t shift_size) { + // If we are shifting out of range, clear the bits and stop. + if (shift_size >= GetSize()) { Clear(); return; } + + // If we have only a single field, this operation can be quick. + if (_data.NumFields() == 1) { + (_data.bits[0] <<= shift_size) &= _data.EndMask(); + return; + } + + const size_t field_shift = shift_size / FIELD_BITS; + const size_t bit_shift = shift_size % FIELD_BITS; + const size_t bit_overflow = FIELD_BITS - bit_shift; + + // Loop through each field, from L to R, and update it. + if (field_shift) { + for (size_t i = _data.LastField(); i >= field_shift; --i) { + _data.bits[i] = _data.bits[i - field_shift]; + } + for (size_t i = field_shift; i > 0; --i) _data.bits[i-1] = 0; + } + + // account for bit_shift + if (bit_shift) { + for (size_t i = _data.LastField() ; i > field_shift; --i) { + _data.bits[i] <<= bit_shift; + _data.bits[i] |= (_data.bits[i-1] >> bit_overflow); + } + // Handle final field (field_shift position) + _data.bits[field_shift] <<= bit_shift; + } + + // Mask out any bits that have left-shifted away + ClearExcessBits(); + } + + template + constexpr void Bits::ShiftRight(const size_t shift_size, bool raw) { + if (shift_size == 0) return; + + // If we are shifting out of range, clear the bits and stop. + if (!raw && shift_size >= GetSize()) { Clear(); return; } + + // If we have only a single field, this operation can be quick. + if (_data.NumFields() == 1) { + _data.bits[0] >>= shift_size; + return; + } + + const size_t field_shift = shift_size / FIELD_BITS; + const size_t bit_shift = shift_size % FIELD_BITS; + const size_t bit_overflow = FIELD_BITS - bit_shift; + const size_t NUM_FIELDS = _data.NumFields(); + const size_t field_shift2 = NUM_FIELDS - field_shift; + + // account for field_shift + if (field_shift) { + for (size_t i = 0; i < field_shift2; ++i) { + _data.bits[i] = _data.bits[i + field_shift]; + } + // Clear fields where bits were fully shifted out. + for (size_t i = field_shift2; i < NUM_FIELDS; i++) _data.bits[i] = FIELD_0; + } + + // account for bit_shift + if (bit_shift) { + for (size_t i = 0; i < (field_shift2 - 1); ++i) { + _data.bits[i] >>= bit_shift; + _data.bits[i] |= (_data.bits[i+1] << bit_overflow); + } + _data.bits[field_shift2 - 1] >>= bit_shift; + } + } + + /// Helper: call ROTATE with negative number + template + constexpr void Bits::ROTL_SELF(const size_t shift_size_raw) { + if (GetSize() == 0) return; // Nothing to rotate if there are not bits. + const field_t shift_size = shift_size_raw % GetSize(); + + // Use different approaches based on number of bits. + if (_data.NumFields() == 1) { + _data.bits[0] = emp::RotateBitsLeft(_data.bits[0], shift_size, GetSize()); + } else { // For few bits, shifting L/R and OR-ing is faster. + this_t dup(*this); + dup.ShiftLeft(shift_size); + ShiftRight(GetSize() - shift_size); + OR_SELF(dup); + } + } + + + /// Helper for calling ROTATE with positive number + template + constexpr void Bits::ROTR_SELF(const size_t shift_size_raw) { + const size_t shift_size = shift_size_raw % GetSize(); + + // use different approaches based on number of bits + if (_data.NumFields() == 1) { + _data.bits[0] = emp::RotateBitsRight(_data.bits[0], shift_size, GetSize()); + } else { + this_t dup(*this); + dup.ShiftRight(shift_size); + ShiftLeft(GetSize() - shift_size); + OR_SELF(dup); + } + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////// + // ---------------------------------------------------------------------------------------- + // --------------------- Implementations of Public Member Functions ----------------------- + // ---------------------------------------------------------------------------------------- + /////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////// + + + // ------------------- Implementations of Constructors and Assignments -------------------- + + /// Build a new Bits object with specified bit count and initialization (default 0) + template + Bits::Bits(size_t _num_bits, bool init_val) : _data(_num_bits) { + if (init_val) SetAll(); else Clear(); + } + + /// Constructor from other type of Bits field. + template + template + Bits::Bits(const Bits & in) + : _data(in.GetSize()) + { + emp_assert(in.OK()); + RawCopy(in); + } + + // -- Move constructor in class; set to default -- + + /// Constructor to generate a Bits from a std::bitset. + template + template + Bits::Bits(const std::bitset & bitset) + : _data(NUM_BITS) + { + // Copy over the values. + Clear(); + for (size_t i = 0; i < NUM_BITS; ++i) Set(i, bitset[i]); + } + + /// Constructor to generate a Bits from a string of '0's and '1's. + template + Bits::Bits(const std::string & bitstring) + : _data(CountBits(bitstring)) + { + Clear(); + + size_t pos = 0; + for (char c : bitstring) { + if (c == '1') { + if constexpr (ZERO_LEFT) Set(pos); + else Set(GetSize() - pos - 1); + pos++; + } + if (c == '0') ++pos; // Leave position as zero and move to next pos. + } + } + + /// Constructor to generate a random set of bits in the default size. + template + Bits::Bits(Random & random) + { + emp_assert(GetSize() > 0, "Trying to construct a random series of bits, but with no bits!"); + Randomize(random); + ClearExcessBits(); + } + + /// Constructor to generate random Bits with provided prob of 1's, default size. + template + Bits::Bits(Random & random, const double p1) + { + emp_assert(GetSize() > 0, "Trying to construct a random series of bits, but with no bits!"); + emp_assert(p1 >= 0.0 && p1 <= 1.0, "Probability of ones out of range", p1); + Randomize(random, p1); + ClearExcessBits(); + } + + /// Constructor to generate random Bits with specified number of ones. + template + Bits::Bits(Random & random, const size_t target_ones) + { + emp_assert(GetSize() > 0, "Trying to construct a random series of bits, but with no bits!"); + ChooseRandom(random, target_ones); + ClearExcessBits(); + } + + /// Constructor to generate a random Bits (with equal prob of 0 or 1). + template + Bits::Bits(size_t in_num_bits, Random & random) + : _data(in_num_bits) + { + Clear(); + Randomize(random); + } + + /// Constructor to generate a random Bits with provided prob of 1's. + template + Bits::Bits(size_t in_num_bits, Random & random, const double p1) + : _data(in_num_bits) + { + emp_assert(p1 >= 0.0 && p1 <= 1.0, "Probability of ones out of range", p1); + Clear(); + Randomize(random, p1); + } + + /// Constructor to generate a random Bits with provided number of 1's. + template + Bits::Bits(size_t in_num_bits, Random & random, const size_t target_ones) + : _data(in_num_bits) + { + Clear(); + ChooseRandom(random, target_ones); + } + + /// Initializer list constructor. + template + template + Bits::Bits(const std::initializer_list l) + : _data(l.size()) + { + Clear(); + size_t idx = 0; + if constexpr (ZERO_LEFT) { + for (auto i = std::begin(l); i != std::end(l); ++i) Set(idx++, *i); + } else { + for (auto i = std::rbegin(l); i != std::rend(l); ++i) Set(idx++, *i); + } + } + + /// Copy, but with a resize. + template + template + Bits:: + Bits(const Bits & in, size_t new_size) + : Bits(new_size) + { + emp_assert(in.OK()); + + // How many fields do we need to copy? + size_t copy_fields = std::min(_data.NumFields(), in.NumFields()); + + RawCopy(in.FieldPtr(), copy_fields); + } + + /// Copy assignment operator. + template + Bits & + Bits::operator=(const Bits & in) & + { + emp_assert(in.OK()); + if (&in != this) { + _data.RawResize(in.GetSize()); + RawCopy(in); + } + + return *this; + } + + /// Other Bits assignment operator. + template + template + Bits & + Bits::operator=(const Bits & in) & + { + emp_assert(in.OK()); + Resize(in.GetSize()); + RawCopy(in); + + return *this; + } + + /// Move operator. + template + Bits & + Bits::operator=(Bits && in) & + { + emp_assert(&in != this); // Shouldn't be possible in an r-value + _data = std::move(in._data); // Shift move into _data objects. + return *this; + } + + /// Assignment operator from a std::bitset. + template + template + Bits & + Bits::operator=(const std::bitset & bitset) & + { + _data.RawResize(NUM_BITS); + for (size_t i = 0; i < NUM_BITS; i++) Set(i, bitset[i]); // Copy bits in. + return ClearExcessBits(); // Set excess bits to zeros. + } + + /// Assignment operator from a string of '0's and '1's. + template + Bits & + Bits::operator=(const std::string & bitstring) & + { + const size_t new_size = CountBits(bitstring); + _data.RawResize(new_size); + + Clear(); + + size_t pos = 0; + for (char c : bitstring) { + if (c == '1') { + if constexpr (ZERO_LEFT) Set(pos); + else Set(new_size - pos - 1); + pos++; + } + if (c == '0') ++pos; // Leave position as zero and move to next pos. + } + + return *this; + } + + + /// Assign from a Bits object of a different size, while keeping current size. + /// If there are too many bits being imported, extras are cut off. + /// If there are fewer bits, the remainder are zero'd out (up to max_copy_bits) + // @CAO: Can copy fields for a speedup. + template + template + Bits & + Bits::Import( + const Bits & from_bits, + const size_t from_start_pos, + size_t max_copy_bits) + { + emp_assert(from_start_pos < from_bits.GetSize()); + size_t bits_available = from_bits.GetSize() - from_start_pos; + + // Actual copied bits is limited by bits available to copy and bits in this object. + size_t copy_size = emp::Min(bits_available, GetSize(), max_copy_bits); + + for (size_t i = 0; i < copy_size; ++i) { + Set(i, from_bits[i+from_start_pos]); + } + + // Any bits AFTER the ones copied, but before the max copy, should be zeroed out. + Clear(copy_size, max_copy_bits); + + return *this; + } + + /// Convert to a Bitset of a different size. + template + template + OUT_T Bits::Export(size_t out_size, size_t start_bit) const { + OUT_T out_bits(out_size); + out_bits.Import(*this, start_bit); + return out_bits; + } + + /// Concatenate another Bits object on to the end of this one. + template + template + Bits & Bits::Append( + const Bits & in_bits + ) { + this_t shift_copy(in_bits); + const size_t old_size = GetSize(); + const size_t new_size = old_size + in_bits.GetSize(); + Resize(new_size); + shift_copy.Resize(new_size); + shift_copy <<= old_size; + OR_SELF(shift_copy); + return *this; + } + + + // -------------------- Implementations of common accessors ------------------- + + /// Retrieve the bit value from the specified index. + template + constexpr bool Bits::Get(size_t index) const { + emp_assert(index < GetSize(), index, GetSize()); + const size_t field_id = FieldID(index); + const size_t pos_id = FieldPos(index); + return _data.bits[field_id] & (FIELD_1 << pos_id); + } + + /// Update the bit value at the specified index. + template + Bits & Bits::Set(size_t index, bool value) { + emp_assert(index < GetSize(), index, GetSize()); + const size_t field_id = FieldID(index); + const size_t pos_id = FieldPos(index); + const field_t pos_mask = FIELD_1 << pos_id; + + if (value) _data.bits[field_id] |= pos_mask; + else _data.bits[field_id] &= ~pos_mask; + + return *this; + } + + /// Set all bits to 1. + template + Bits & Bits::SetAll() { + const size_t NUM_FIELDS = _data.NumFields(); + for (size_t i = 0; i < NUM_FIELDS; i++) _data.bits[i] = FIELD_ALL; + return ClearExcessBits(); + } + + /// Set all bits to 0. + template + Bits & Bits::Clear() { + const size_t NUM_FIELDS = _data.NumFields(); + for (size_t i = 0; i < NUM_FIELDS; i++) _data.bits[i] = FIELD_0; + return *this; + } + + /// Change a specified bit to the opposite value + template + Bits & Bits::Toggle(size_t index) { + emp_assert(index < GetSize(), index, GetSize()); + const size_t field_id = FieldID(index); + const size_t pos_id = FieldPos(index); + const field_t pos_mask = FIELD_1 << pos_id; + + _data.bits[field_id] ^= pos_mask; + + return *this; + } + + + // ------ @CAO CONTINUE HERE!!! ------ + + + template + bool Bits::Any() const { + const size_t NUM_FIELDS = _data.NumFields(); + for (size_t i = 0; i < NUM_FIELDS; i++) { + if (_data.bits[i]) return true; + } + return false; + } + + // ------------------------- Implementations Randomization functions ------------------------- + + /// Set all bits randomly, with a 50% probability of being a 0 or 1. + template + Bits & Bits::Randomize(Random & random) { + random.RandFill(BytePtr(), _data.NumBytes()); + return ClearExcessBits(); + } + + /// Set all bits randomly, with probability specified at compile time. + template + template + Bits & Bits::RandomizeP(Random & random, + const size_t start_pos, size_t stop_pos) { + if (stop_pos == MAX_SIZE_T) stop_pos = GetSize(); + + emp_assert(start_pos <= stop_pos); + emp_assert(stop_pos <= GetSize()); + random.RandFillP

(BytePtr(), _data.NumBytes(), start_pos, stop_pos); + return *this; + } + + + /// Set all bits randomly, with a given probability of being on. + template + Bits & + Bits::Randomize(Random & random, const double p, + const size_t start_pos, size_t stop_pos) { + if (stop_pos == MAX_SIZE_T) stop_pos = GetSize(); + + emp_assert(start_pos <= stop_pos, start_pos, stop_pos); + emp_assert(stop_pos <= GetSize(), stop_pos, GetSize()); + emp_assert(p >= 0.0 && p <= 1.0, p); + random.RandFill(BytePtr(), _data.NumBytes(), p, start_pos, stop_pos); + return *this; + } + + /// Set all bits randomly, with a given number of them being on. + template + Bits & + Bits::ChooseRandom(Random & random, const size_t target_ones, + const size_t start_pos, size_t stop_pos) { + if (stop_pos == MAX_SIZE_T) stop_pos = GetSize(); + + emp_assert(start_pos <= stop_pos); + emp_assert(stop_pos <= GetSize()); + + const size_t target_size = stop_pos - start_pos; + emp_assert(target_ones <= target_size); + + // Approximate the probability of ones as a starting point. + double p = ((double) target_ones) / (double) target_size; + + // If we are not randomizing the whole sequence, we need to track the number of ones + // in the NON-randomized region to subtract off later. + size_t kept_ones = 0; + if (target_size != GetSize()) { + Clear(start_pos, stop_pos); + kept_ones = CountOnes(); + } + + // Try to find a shortcut if p allows.... + // (These values are currently educated guesses) + if (p < 0.12) { if (target_size == GetSize()) Clear(start_pos, stop_pos); } + else if (p < 0.2) RandomizeP(random, start_pos, stop_pos); + else if (p < 0.35) RandomizeP(random, start_pos, stop_pos); + else if (p < 0.42) RandomizeP(random, start_pos, stop_pos); + else if (p < 0.58) RandomizeP(random, start_pos, stop_pos); + else if (p < 0.65) RandomizeP(random, start_pos, stop_pos); + else if (p < 0.8) RandomizeP(random, start_pos, stop_pos); + else if (p < 0.88) RandomizeP(random, start_pos, stop_pos); + else SetRange(start_pos, stop_pos); + + size_t cur_ones = CountOnes() - kept_ones; + + // Do we need to add more ones? + while (cur_ones < (size_t) target_ones) { + size_t pos = random.GetUInt(start_pos, stop_pos); + auto bit = operator[](pos); + if (!bit) { + bit.Set(); + cur_ones++; + } + } + + // See if we have too many ones. + while (cur_ones > (size_t) target_ones) { + size_t pos = random.GetUInt(start_pos, stop_pos); + auto bit = operator[](pos); + if (bit) { + bit.Clear(); + cur_ones--; + } + } + + return *this; + } + + /// Flip random bits with a given probability. + // @CAO: Possibly faster to generate a sequence of bits and XORing with them. + template + Bits & + Bits::FlipRandom(Random & random, + const double p, + const size_t start_pos, + size_t stop_pos) + { + if (stop_pos == MAX_SIZE_T) stop_pos = GetSize(); + + emp_assert(start_pos <= stop_pos); + emp_assert(stop_pos <= GetSize()); + emp_assert(p >= 0.0 && p <= 1.0, p); + + for (size_t i=start_pos; i < stop_pos; ++i) if (random.P(p)) Toggle(i); + + return *this; + } + + /// Set random bits with a given probability (does not check if already set.) + template + Bits & Bits::SetRandom(Random & random, + const double p, + const size_t start_pos, + size_t stop_pos) + { + if (stop_pos == MAX_SIZE_T) stop_pos = GetSize(); + + emp_assert(start_pos <= stop_pos); + emp_assert(stop_pos <= GetSize()); + emp_assert(p >= 0.0 && p <= 1.0, p); + + for (size_t i=start_pos; i < stop_pos; ++i) if (random.P(p)) Set(i); + + return *this; + } + + /// Unset random bits with a given probability (does not check if already zero.) + template + Bits & Bits::ClearRandom(Random & random, + const double p, + const size_t start_pos, + size_t stop_pos) + { + if (stop_pos == MAX_SIZE_T) stop_pos = GetSize(); + + emp_assert(start_pos <= stop_pos); + emp_assert(stop_pos <= GetSize()); + emp_assert(p >= 0.0 && p <= 1.0, p); + + for (size_t i=start_pos; i < stop_pos; ++i) if (random.P(p)) Clear(i); + + return *this; + } + + /// Flip a specified number of random bits. + template + Bits & Bits::FlipRandomCount( + Random & random, + const size_t target_bits + ) { + emp_assert(GetSize() <= GetSize()); + Bits choice(GetSize(), random, target_bits); + return XOR_SELF(choice); + } + + /// Set a specified number of random bits (does not check if already set.) + template + Bits & Bits::SetRandomCount( + Random & random, + const size_t target_bits + ) { + emp_assert(GetSize() <= GetSize()); + Bits choice(GetSize(), random, target_bits); + return OR_SELF(choice); + } + + /// Unset a specified number of random bits (does not check if already zero.) + template + Bits & Bits::ClearRandomCount( + Random & random, + const size_t target_bits + ) { + emp_assert(GetSize() <= GetSize()); + Bits choice(GetSize(), random, GetSize() - target_bits); + return AND_SELF(choice); + } + + + // ------------------------- Implementations of Comparison Operators ------------------------- + + /// Test if two bit vectors are identical. + template + template + bool Bits::operator==(const Bits & in) const { + if (GetSize() != in.GetSize()) return false; + + const size_t NUM_FIELDS = _data.NumFields(); + auto in_fields = in.FieldSpan(); + for (size_t i = 0; i < NUM_FIELDS; ++i) { + if (_data.bits[i] != in_fields[i]) return false; + } + return true; + } + + /// Compare the would-be numerical values of two bit vectors. + template + template + bool Bits::operator<(const Bits & in) const { + if (GetSize() != in.GetSize()) return GetSize() < in.GetSize(); + + const size_t NUM_FIELDS = _data.NumFields(); + auto in_fields = in.FieldSpan(); + for (size_t i = NUM_FIELDS; i > 0; --i) { // Start loop at the largest field. + const size_t pos = i-1; + if (_data.bits[pos] == in_fields[pos]) continue; // If same, keep looking! + return (_data.bits[pos] < in_fields[pos]); // Otherwise, do comparison + } + return false; // Bit vectors are identical. + } + + /// Automatically convert Bits object to other vector types. + template + template + Bits::operator emp::vector() { + emp::vector out(GetSize()); + for (size_t i = 0; i < GetSize(); i++) { + out[i] = (T) Get(i); + } + return out; + } + + + // ------------------------- Access Groups of bits ------------------------- + + /// Retrieve the byte at the specified byte index. + template + uint8_t Bits::GetByte(size_t index) const { + emp_assert(index < _data.NumBytes(), index, _data.NumBytes()); + const size_t field_id = Byte2Field(index); + const size_t pos_id = Byte2FieldPos(index); + return (_data.bits[field_id] >> pos_id) & 255U; + } + + /// Update the byte at the specified byte index. + template + void Bits::SetByte(size_t index, uint8_t value) { + emp_assert(index < _data.NumBytes(), index, _data.NumBytes()); + const size_t field_id = Byte2Field(index); + const size_t pos_id = Byte2FieldPos(index); + const field_t val_uint = value; + _data.bits[field_id] = (_data.bits[field_id] & ~(FIELD_255 << pos_id)) | (val_uint << pos_id); + } + + /// Get the overall value of this BitSet, using a uint encoding, but including all bits + /// and returning the value as a double. + template + double Bits::GetValue() const { + const int max_one = FindMaxOne(); + + // If there are no ones, this value must be 0. + if (max_one == -1) return 0.0; + + // If all ones are in the least-significant field, just return it. + if (max_one < 64) return (double) GetUInt64(0); + + // To grab the most significant field, figure out how much to shift it by. + const size_t shift_bits = static_cast(max_one) - 63; + double out_value = (double) (*this >> shift_bits).GetUInt64(0); + + out_value *= emp::Pow2(shift_bits); + + return out_value; + } + + /// Get specified type at a given index (in steps of that type size) + template + template + T Bits::GetValueAtIndex(const size_t index) const { + // For the moment, must fit inside bounds; eventually should pad with zeros. + emp_assert((index + 1) * sizeof(T) <= _data.TotalBytes()); + + T out_value; + std::memcpy( &out_value, BytePtr().Raw() + index * sizeof(T), sizeof(T) ); + return out_value; + } + + + /// Set specified type at a given index (in steps of that type size) + template + template + Bits & Bits::SetValueAtIndex(const size_t index, T in_value) { + // For the moment, must fit inside bounds; eventually should pad with zeros. + emp_assert((index + 1) * sizeof(T) <= _data.TotalBytes()); + std::memcpy( BytePtr().Raw() + index * sizeof(T), &in_value, sizeof(T) ); + return ClearExcessBits(); + } + + + /// Get the specified type starting from a given BIT position. + template + template + T Bits::GetValueAtBit(const size_t index) const { + // For the moment, must fit inside bounds; eventually should pad with zeros. + emp_assert((index+7)/8 + sizeof(T) < _data.TotalBytes()); + + Bits out_bits(*this); + out_bits >>= index; + + return out_bits.template GetValueAtIndex(0); + } + + + /// Set the specified type starting from a given BIT position. + // @CAO: Can be optimized substantially, especially for long Bits objects. + template + template + Bits & Bits::SetValueAtBit(const size_t index, T value) { + // For the moment, must fit inside bounds; eventually should (?) pad with zeros. + emp_assert((index+7)/8 + sizeof(T) < _data.TotalBytes()); + constexpr size_t type_bits = sizeof(T) * 8; + + const size_t end_pos = Min(index+type_bits, GetSize()); + Clear(index, end_pos); // Clear out the bits where new value will go. + Bits in_bits(GetSize()); // Setup a bitset for the new bits. + in_bits.SetValueAtIndex(0, value); // Insert the new bits. + in_bits <<= index; // Shift new bits into place. + OR_SELF(in_bits); // Place new bits into current Bits object. + + return ClearExcessBits(); + } + + + // ------------------------- Other Analyses ------------------------- + + /// A simple hash function for bit vectors. + template + std::size_t Bits::Hash(size_t start_field) const { + static_assert(std::is_same_v, "Hash() requires fields to be size_t"); + + // If there are no fields left, hash on size one. + if (start_field == _data.NumFields()) return GetSize(); + + // If we have only one field left, combine it with size. + if (start_field == _data.NumFields()-1) return hash_combine(_data.bits[start_field], GetSize()); + + // Otherwise we have more than one field. Combine and recurse. + size_t partial_hash = hash_combine(_data.bits[start_field], _data.bits[start_field+1]); + + return hash_combine(partial_hash, Hash(start_field+2)); + } + + // TODO: see https://arxiv.org/pdf/1611.07612.pdf for fast pop counts + /// Count the number of ones in Bits. + template + constexpr size_t Bits::CountOnes() const { + if (GetSize() == 0) return 0; + const field_t NUM_FIELDS = _data.NumFields(); + size_t bit_count = 0; + for (size_t i = 0; i < NUM_FIELDS; i++) { + // when compiling with -O3 and -msse4.2, this is the fastest population count method. + std::bitset std_bs(_data.bits[i]); + bit_count += std_bs.count(); + } + + emp_assert(bit_count <= GetSize()); + return bit_count; + } + + // TODO: Speed this up so that we don't need to copy out all of the bits. + /// Count the number of ones in a specified range of Bits. + template + constexpr size_t Bits::CountOnes(size_t start, size_t end) const { + emp_assert(start <= end); + emp_assert(end <= GetSize()); + if (start == end) return 0; + const size_t range_size = end-start; + return Export(range_size, start).CountOnes(); + } + + /// Faster counting of ones for very sparse bit vectors. + template + constexpr size_t Bits::CountOnes_Sparse() const { + size_t bit_count = 0; + const size_t NUM_FIELDS = _data.NumFields(); + for (size_t i = 0; i < NUM_FIELDS; ++i) { + field_t cur_field = _data.bits[i]; + while (cur_field) { + cur_field &= (cur_field-1); // Peel off a single 1. + bit_count++; // Increment the counter + } + } + return bit_count; + } + + /// Pop the last bit in the vector. + /// @return value of the popped bit. + template + bool Bits::PopBack() { + const bool val = Get(GetSize()-1); + Resize(GetSize() - 1); + return val; + } + + /// Push given bit(s) onto the back of a vector. + /// @param bit value of bit to be pushed. + /// @param num number of bits to be pushed. + template + void Bits::PushBack(const bool bit, const size_t num) { + Resize(GetSize() + num); + if (bit) SetRange(GetSize()-num, GetSize()); + } + + /// Push given bit(s) onto the front of a vector. + /// @param bit value of bit to be pushed. + /// @param num number of bits to be pushed. + template + void Bits::PushFront(const bool bit, const size_t num) { + Resize(GetSize() + num); + SHIFT_SELF(num); + if (bit) SetRange(0, num); + } + + /// Insert bit(s) into any index of vector using bit magic. + /// Blog post on implementation reasoning: https://devolab.org/?p=2249 + /// @param index location to insert bit(s). + /// @param val value of bit(s) to insert (default true) + /// @param num number of bits to insert, default 1. + template + void Bits::Insert(const size_t index, const bool val, const size_t num) { + Resize(GetSize() + num); // Adjust to new number of bits. + Bits low_bits(*this); // Copy current bits + SHIFT_SELF(-(int)num); // Shift the high bits into place. + Clear(0, index+num); // Reduce current to just high bits. + low_bits.Clear(index, GetSize()); // Reduce copy to just low bits. + if (val) SetRange(index, index+num); // If new bits should be ones, make it so. + OR_SELF(low_bits); // Put the low bits back in place. + } + + + /// Delete bits from any index in a vector. + /// @param index location to delete bit(s). + /// @param num number of bits to delete, default 1. + template + void Bits::Delete(const size_t index, const size_t num) { + emp_assert(index+num <= GetSize()); // Make sure bits to delete actually exist! + RawMove(index+num, GetSize(), index); // Shift positions AFTER delete into place. + Resize(GetSize() - num); // Crop off end bits. + } + + /// Return the position of the first one; return -1 if no ones in vector. + template + int Bits::FindOne() const { + const size_t NUM_FIELDS = _data.NumFields(); + size_t field_id = 0; + while (field_id < NUM_FIELDS && _data.bits[field_id]==0) field_id++; + return (field_id < NUM_FIELDS) ? + (int) (find_bit(_data.bits[field_id]) + (field_id * FIELD_BITS)) : -1; + } + + /// Return the position of the first zero; return -1 if no zeros in vector. + template + int Bits::FindZero() const { + const size_t NUM_FIELDS = _data.NumFields(); + size_t field_id = 0; + while (field_id < NUM_FIELDS && _data.bits[field_id]==FIELD_ALL) field_id++; + return (field_id < NUM_FIELDS) ? + (int) (find_bit(~_data.bits[field_id]) + (field_id * FIELD_BITS)) : -1; + } + + /// Return the position of the first one after start_pos; return -1 if no ones in vector. + /// You can loop through all 1-bit positions in "bits" with: + /// + /// for (int pos = bits.FindOne(); pos >= 0; pos = bits.FindOne(pos+1)) { ... } + + template + int Bits::FindOne(const size_t start_pos) const { + if (start_pos >= GetSize()) return -1; // If we are past the end, return fail. + size_t field_id = FieldID(start_pos); // What field do we start in? + const size_t field_pos = FieldPos(start_pos); // What position in that field? + + // If there's a hit in a partial first field, return it. + if (field_pos && (_data.bits[field_id] & ~(MaskField(field_pos)))) { + return (int) (find_bit(_data.bits[field_id] & ~(MaskField(field_pos))) + + field_id * FIELD_BITS); + } + + // Search other fields... + const size_t NUM_FIELDS = _data.NumFields(); + if (field_pos) field_id++; + while (field_id < NUM_FIELDS && _data.bits[field_id]==0) field_id++; + return (field_id < NUM_FIELDS) ? + (int) (find_bit(_data.bits[field_id]) + (field_id * FIELD_BITS)) : -1; + } + + /// Return the position of the first zero after start_pos; return -1 if no zeroes in vector. + /// You can loop through all 0-bit positions in "bits" with: + /// + /// for (int pos = bits.FindZero(); pos >= 0; pos = bits.FindZero(pos+1)) { ... } + + template + int Bits::FindZero(const size_t start_pos) const { + if (start_pos >= GetSize()) return -1; // If we are past the end, return fail. + size_t field_id = FieldID(start_pos); // What field do we start in? + const size_t field_pos = FieldPos(start_pos); // What position in that field? + + // If there's a hit in a partial first field, return it. + if (field_pos && (~_data.bits[field_id] & ~(MaskField(field_pos)))) { + return (int) (~find_bit(_data.bits[field_id] & ~(MaskField(field_pos))) + + field_id * FIELD_BITS); + } + + // Search other fields... + const size_t NUM_FIELDS = _data.NumFields(); + if (field_pos) field_id++; + while (field_id < NUM_FIELDS && _data.bits[field_id]==FIELD_ALL) field_id++; + return (field_id < NUM_FIELDS) ? + (int) (find_bit(~_data.bits[field_id]) + (field_id * FIELD_BITS)) : -1; + } + + /// Find the most-significant set-bit. + template + int Bits::FindMaxOne() const { + // Find the max field with a one. + size_t max_field = _data.NumFields() - 1; + while (max_field > 0 && _data.bits[max_field] == 0) max_field--; + + // If there are no ones, return -1. + if (_data.bits[max_field] == 0) return -1; + + const field_t field = _data.bits[max_field]; // Save a local copy of this field. + field_t mask = (field_t) -1; // Mask off the bits still under consideration. + size_t offset = 0; // Indicate where the mask should be applied. + size_t range = FIELD_BITS; // Indicate how many bits are in the mask. + + while (range > 1) { + // Cut the range in half and see if we need to adjust the offset. + range /= 2; // Cut range size in half + mask >>= range; // Cut the mask down. + + // Check the upper half of original range; if has a one shift new offset to there. + if (field & (mask << (offset + range))) offset += range; + } + + return (int) (max_field * FIELD_BITS + offset); + } + + /// Return the position of the first one and change it to a zero. Return -1 if no ones. + template + int Bits::PopOne() { + const int out_bit = FindOne(); + if (out_bit >= 0) Clear((size_t) out_bit); + return out_bit; + } + + /// Return positions of all ones. + template + emp::vector Bits::GetOnes() const { + emp::vector out_vals; + GetOnes(out_vals); + return out_vals; + } + + /// Return positions of all ones using a specified type. + template + template + emp::vector & Bits::GetOnes(emp::vector & out_vals) const { + // @CAO -- There are better ways to do this with bit tricks. + out_vals.resize(CountOnes()); + T cur_pos = 0; + for (T i = 0; i < GetSize(); i++) { + if (Get(i)) out_vals[cur_pos++] = i; + } + return out_vals; + } + + /// Find the length of the longest continuous series of ones. + template + size_t Bits::LongestSegmentOnes() const { + size_t length = 0; + Bits test_bits(*this); + while(test_bits.Any()){ + ++length; + test_bits.AND_SELF(test_bits<<1); + } + return length; + } + + template + emp::vector> Bits::GetRanges() const { + emp::vector> out_ranges; + for (int start_pos = FindOne(); start_pos >= 0; start_pos = FindOne(start_pos+1)) { + int end_pos = FindZero(start_pos); + end_pos = (end_pos == -1) ? GetSize() - 1 : end_pos - 1; + out_ranges.emplace_back(start_pos, end_pos); + } + return out_ranges; + } + + /// Return true if any ones are in common with another Bits object. + template + bool Bits::HasOverlap(const Bits & in) const { + const size_t num_fields = std::min(_data.NumFields(), in.NumFields()); + auto in_fields = in.FieldSpan(); + for (size_t i = 0; i < num_fields; ++i) { + // Short-circuit if we find any overlap. + if (_data.bits[i] & in_fields[i]) return true; + } + return false; + } + + + // ------------------------- Printing and string conversion ------------------------- + + /// Convert this Bits object to a vector string [0 index on left] + template + std::string Bits::ToString() const { + if constexpr (ZERO_LEFT) return ToArrayString(); + else return ToBinaryString(); + } + + /// Convert this Bits object to a vector string [0 index on left] + template + std::string Bits::ToArrayString() const { + std::string out_string; + out_string.reserve(GetSize()); + for (size_t i = 0; i < GetSize(); ++i) out_string.push_back(GetAsChar(i)); + return out_string; + } + + /// Convert this Bits object to a numerical string [0 index on right] + template + std::string Bits::ToBinaryString() const { + std::string out_string; + out_string.reserve(GetSize()); + for (size_t i = GetSize(); i > 0; --i) out_string.push_back(GetAsChar(i-1)); + return out_string; + } + + /// Convert this Bits object to a series of IDs + template + std::string Bits::ToIDString(const std::string & spacer) const { + std::stringstream ss; + PrintOneIDs(ss, spacer); + return ss.str(); + } + + /// Convert this Bits object to a series of IDs with ranges condensed. + template + std::string Bits::ToRangeString(const std::string & spacer, + const std::string & ranger) const + { + std::stringstream ss; + PrintAsRange(ss, spacer, ranger); + return ss.str(); + } + + /// Print a space between each field (or other provided spacer) + template + void Bits::PrintFields(std::ostream & out, const std::string & spacer) const { + for (size_t i = GetSize()-1; i < GetSize(); i--) { + out << Get(i); + if (i && (i % FIELD_BITS == 0)) out << spacer; + } + } + + /// Print a space between each field (or other provided spacer) + template + void Bits::PrintDebug( + std::ostream & out, + const std::string & label + ) const { + if (label.size()) out << label << ":\n"; + for (size_t field = 0; field < _data.NumFields(); field++) { + for (size_t bit_id = 0; bit_id < FIELD_BITS; bit_id++) { + bool bit = (FIELD_1 << bit_id) & _data.bits[field]; + out << ( bit ? 1 : 0 ); + } + out << " : " << field << std::endl; + } + size_t end_pos = _data.NumEndBits(); + if (end_pos == 0) end_pos = FIELD_BITS; + for (size_t i = 0; i < end_pos; i++) out << " "; + out << "^" << std::endl; + } + + /// Print the positions of all one bits, spaces are the default separator. + template + void Bits::PrintOneIDs(std::ostream & out, const std::string & spacer) const { + bool started = false; + for (size_t i = 0; i < GetSize(); i++) { + if (Get(i)) { + if (started) out << spacer; + out << i; + started = true; + } + } + } + + /// Print the ones in a range format. E.g., 2-5,7,10-15 + template + void Bits::PrintAsRange(std::ostream & out, + const std::string & spacer, + const std::string & ranger) const + { + emp::vector ones = GetOnes(); + + for (size_t pos = 0; pos < ones.size(); pos++) { + if (pos) out << spacer; + + size_t start = ones[pos]; + while (pos+1 < ones.size() && ones[pos+1] == ones[pos]+1) pos++; + size_t end = ones[pos]; + + out << start; + if (start != end) out << ranger << end; + } + } + + + // ------------------------- Base Boolean-logic operations ------------------------- + + /// Perform a Boolean NOT with this Bits object, store result here, and return this object. + template + Bits & Bits::NOT_SELF() { + const size_t NUM_FIELDS = _data.NumFields(); + for (size_t i = 0; i < NUM_FIELDS; i++) _data.bits[i] = ~_data.bits[i]; + return ClearExcessBits(); + } + + /// Perform a Boolean AND with this Bits object, store result here, and return this object. + template + Bits & Bits::AND_SELF(const Bits & bits2) { + const size_t NUM_FIELDS = _data.NumFields(); + for (size_t i = 0; i < NUM_FIELDS; i++) _data.bits[i] = _data.bits[i] & bits2._data.bits[i]; + return *this; + } + + /// Perform a Boolean OR with this Bits object, store result here, and return this object. + template + Bits & Bits::OR_SELF(const Bits & bits2) { + const size_t NUM_FIELDS = _data.NumFields(); + for (size_t i = 0; i < NUM_FIELDS; i++) _data.bits[i] = _data.bits[i] | bits2._data.bits[i]; + return *this; + } + + /// Perform a Boolean NAND with this Bits object, store result here, and return this object. + template + Bits & Bits::NAND_SELF(const Bits & bits2) { + const size_t NUM_FIELDS = _data.NumFields(); + for (size_t i = 0; i < NUM_FIELDS; i++) _data.bits[i] = ~(_data.bits[i] & bits2._data.bits[i]); + return ClearExcessBits(); + } + + /// Perform a Boolean NOR with this Bits object, store result here, and return this object. + template + Bits & Bits::NOR_SELF(const Bits & bits2) { + const size_t NUM_FIELDS = _data.NumFields(); + for (size_t i = 0; i < NUM_FIELDS; i++) _data.bits[i] = ~(_data.bits[i] | bits2._data.bits[i]); + return ClearExcessBits(); + } + + /// Perform a Boolean XOR with this Bits object, store result here, and return this object. + template + Bits & Bits::XOR_SELF(const Bits & bits2) { + const size_t NUM_FIELDS = _data.NumFields(); + for (size_t i = 0; i < NUM_FIELDS; i++) _data.bits[i] = _data.bits[i] ^ bits2._data.bits[i]; + return *this; + } + + /// Perform a Boolean EQU with this Bits object, store result here, and return this object. + template + Bits & Bits::EQU_SELF(const Bits & bits2) { + const size_t NUM_FIELDS = _data.NumFields(); + for (size_t i = 0; i < NUM_FIELDS; i++) _data.bits[i] = ~(_data.bits[i] ^ bits2._data.bits[i]); + return ClearExcessBits(); + } + + /// Positive shifts go left and negative go right (0 does nothing); return result. + template + Bits Bits::SHIFT(const int shift_size) const { + Bits out_bits(*this); + if (shift_size > 0) out_bits.ShiftRight((size_t) shift_size); + else if (shift_size < 0) out_bits.ShiftLeft((size_t) -shift_size); + return out_bits; + } + + /// Positive shifts go left and negative go right; store result here, and return this object. + template + Bits & Bits::SHIFT_SELF(const int shift_size) { + if (shift_size > 0) ShiftRight((size_t) shift_size); + else if (shift_size < 0) ShiftLeft((size_t) -shift_size); + return *this; + } + + /// Reverse the order of bits in the bitset + template + Bits & Bits::REVERSE_SELF() { + auto bit_span = _data.AsSpan(); + + // Reverse order of whole fields + std::reverse( bit_span.begin(), bit_span.end() ); + + // Reverse the bits in each field. + for (auto & cur_field : bit_span) cur_field = emp::ReverseBits(cur_field); + + // Move the gap to the other side. + if (_data.NumEndBits()) ShiftRight(_data.EndGap(), true); + + return *this; + } + + /// Reverse order of bits in the bitset. + template + Bits Bits::REVERSE() const { + Bits out_set(*this); + return out_set.REVERSE_SELF(); + } + + + /// Positive rotates go left and negative rotates go left (0 does nothing); + /// return result. + template + Bits Bits::ROTATE(const int rotate_size) const { + Bits out_set(*this); + if (rotate_size > 0) out_set.ROTR_SELF((field_t) rotate_size); + else if (rotate_size < 0) out_set.ROTL_SELF((field_t) (-rotate_size)); + return out_set; + } + + /// Positive rotates go right and negative rotates go left (0 does nothing); + /// store result here, and return this object. + template + Bits & Bits::ROTATE_SELF(const int rotate_size) { + if (rotate_size > 0) ROTR_SELF((field_t) rotate_size); + else if (rotate_size < 0) ROTL_SELF((field_t) -rotate_size); + return *this; + } + + + /// Addition of two Bitsets. + /// Wraps if it overflows. + /// Returns result. + template + Bits Bits::ADD(const Bits & set2) const{ + Bits out_set(*this); + return out_set.ADD_SELF(set2); + } + + /// Addition of two Bitsets. + /// Wraps if it overflows. + /// Returns this object. + template + Bits & Bits::ADD_SELF(const Bits & set2) { + bool carry = false; + + for (size_t i = 0; i < GetSize()/FIELD_BITS; ++i) { + field_t addend = set2._data.bits[i] + static_cast(carry); + carry = set2._data.bits[i] > addend; + + field_t sum = _data.bits[i] + addend; + carry |= _data.bits[i] > sum; + + _data.bits[i] = sum; + } + + if (_data.NumEndBits()) { + _data.bits[GetSize()/FIELD_BITS] = ( + _data.bits[GetSize()/FIELD_BITS] + + set2._data.bits[GetSize()/FIELD_BITS] + + static_cast(carry) + ) & _data.EndMask(); + } + + return *this; + } + + /// Subtraction of two Bitsets. + /// Wraps around if it underflows. + /// Returns result. + template + Bits Bits::SUB(const Bits & set2) const{ + Bits out_set(*this); + return out_set.SUB_SELF(set2); + } + + /// Subtraction of two Bitsets. + /// Wraps if it underflows. + /// Returns this object. + template + Bits & Bits::SUB_SELF(const Bits & set2){ + + bool carry = false; + + for (size_t i = 0; i < GetSize()/FIELD_BITS; ++i) { + field_t subtrahend = set2._data.bits[i] + static_cast(carry); + carry = set2._data.bits[i] > subtrahend; + carry |= _data.bits[i] < subtrahend; + _data.bits[i] -= subtrahend; + } + + if (_data.NumEndBits()) { + _data.bits[GetSize()/FIELD_BITS] = ( + _data.bits[GetSize()/FIELD_BITS] + - set2._data.bits[GetSize()/FIELD_BITS] + - static_cast(carry) + ) & _data.EndMask(); + } + + return *this; + } + + // Set up some aliases from common types of Bit strings. + // BitVector and BitArray function like vectors and arrays, which is to say that the zero + // index is on the left-hand side. BitSet and BitValue are treated like numerical + // representations, with the zero-position on the right-hand side. + + // using BitVector = Bits; + using BitVector = Bits; + using BitValue = Bits; + + template using BitArray = Bits, true>; + template using BitSet = Bits, false>; + template using StaticBitVector = Bits, true>; + template using StaticBitValue = Bits, false>; +} + + +// ---------------------- Implementations to work with standard library ---------------------- + +namespace std { + /// Hash function to allow Bits objects to be used with maps and sets (must be in std). + template + struct hash> { + std::size_t operator()(const emp::Bits & bits) const { + return bits.Hash(); + } + }; +} + +#endif // #ifndef EMP_BITS_BITS_HPP_INCLUDE diff --git a/include/emp/bits/Bits_Data.hpp b/include/emp/bits/Bits_Data.hpp new file mode 100644 index 0000000000..c245822506 --- /dev/null +++ b/include/emp/bits/Bits_Data.hpp @@ -0,0 +1,460 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2022-23 +*/ +/** + * @file + * @brief Helper class to handle memory management for Bits objects. + * @note Status: BETA + * + * Bits_Data handles the actual bits inside of the Bits class. Bits itself provides many tools + * to operate on that data. + */ + +#ifndef EMP_BITS_BITS_DATA_HPP_INCLUDE +#define EMP_BITS_BITS_DATA_HPP_INCLUDE + + +#include +#include + +#include "../base/array.hpp" +#include "../base/assert.hpp" +#include "../base/Ptr.hpp" +#include "../math/math.hpp" + +#include "bitset_utils.hpp" + +namespace emp { + + // BitsMode specifies how a Bits object can change the number of bits in itself. + // FIXED is locked at the base size an cannot change and is stored in static memory. + // CAPPED must be the base size or lower, but requires size tracking. + // DYNAMIC defaults to base size, but can be changed; requires indirect memory and allocation. + // WATERMARK is like DYNAMIC, but never reallocates memory when shrinking active size. + enum class BitsMode { FIXED, CAPPED, DYNAMIC, WATERMARK }; + + namespace internal { + + // ------------------------------------------------------------------------------------ + // SIZE TRACKING + // ------------------------------------------------------------------------------------ + + /// Dynamic size is stored here to work with, but not the actual bits. + template + struct Bits_Data_Size_Var { + using field_t = bits_field_t; + + size_t num_bits; ///< Total number of bits are we using + + constexpr void SetSize(size_t new_size) { num_bits = new_size; } + + [[nodiscard]] constexpr size_t NumBits() const noexcept { return num_bits; } + + // Number of bits locked in at compile time. + [[nodiscard]] static constexpr size_t NumCTBits() noexcept { return 0; } + + /// Number of bits used in partial field at the end; 0 = perfect fit. + [[nodiscard]] constexpr size_t NumEndBits() const noexcept { + return num_bits & (NUM_FIELD_BITS - 1); + } + + /// EXTRA bits leftover in the gap at the end + [[nodiscard]] constexpr size_t EndGap() const noexcept { + return NumEndBits() ? (NUM_FIELD_BITS - NumEndBits()) : 0; + } + + /// Mask to cut off all of the final bits. + [[nodiscard]] constexpr field_t EndMask() const noexcept { + return MaskLow(NumEndBits()); + } + + /// How many fields do we need for the current set of bits? + [[nodiscard]] constexpr size_t NumFields() const noexcept { + return num_bits ? (1 + ((num_bits - 1) / NUM_FIELD_BITS)) : 0; + } + + /// ID of the last occupied field + [[nodiscard]] constexpr size_t LastField() const noexcept { + return NumFields() - 1; + } + + /// Number of bytes needed for the current set of bits + [[nodiscard]] constexpr size_t NumBytes() const noexcept { + return num_bits ? (1 + ((num_bits - 1) >> 3)) : 0; + } + + /// How many bytes are allocated? + [[nodiscard]] constexpr size_t TotalBytes() const noexcept { + return NumFields() * sizeof(field_t); + } + + Bits_Data_Size_Var(size_t in_size=DEFAULT_SIZE) : num_bits(in_size) { } + Bits_Data_Size_Var(const Bits_Data_Size_Var &) = default; + + template + void serialize(Archive & ar) { ar(num_bits); } + + [[nodiscard]] constexpr bool OK() const { return true; } // Nothing to check yet. + }; + + /// If we have a fixed number of bits, we know size at compile time. + template + struct Bits_Data_Size_Fixed { + using field_t = bits_field_t; + static constexpr size_t DEFAULT_SIZE = NUM_BITS; + + constexpr void SetSize(size_t new_size) { + emp_assert(new_size == NUM_BITS, "Cannot change to new_size"); + } + + [[nodiscard]] constexpr size_t NumBits() const noexcept { return NUM_BITS; } + + // Number of bits locked in at compile time. + [[nodiscard]] static constexpr size_t NumCTBits() noexcept { return NUM_BITS; } + + /// Number of bits used in partial field at the end; 0 if perfect fit. + [[nodiscard]] constexpr size_t NumEndBits() const noexcept { + return NUM_BITS & (NUM_FIELD_BITS - 1); + } + + /// How many EXTRA bits are leftover in the gap at the end? + [[nodiscard]] constexpr size_t EndGap() const noexcept { + return (NUM_FIELD_BITS - NumEndBits()) % NUM_FIELD_BITS; + } + + /// A mask to cut off all of the final bits. + [[nodiscard]] constexpr field_t EndMask() const noexcept { + return MaskLow(NumEndBits()); + } + + /// How many felids do we need for the current set of bits? + [[nodiscard]] constexpr size_t NumFields() const noexcept { + return NUM_BITS ? (1 + ((NUM_BITS - 1) / NUM_FIELD_BITS)) : 0; + } + + /// What is the ID of the last occupied field? + [[nodiscard]] constexpr size_t LastField() const noexcept { return NumFields() - 1; } + + /// How many bytes are used for the current set of bits? (rounded up!) + [[nodiscard]] constexpr size_t NumBytes() const noexcept { + return NUM_BITS ? (1 + ((NUM_BITS - 1) >> 3)) : 0; + } + + /// How many bytes are allocated? (rounded up!) + [[nodiscard]] constexpr size_t TotalBytes() const noexcept { + return NumFields() * sizeof(field_t); + } + + Bits_Data_Size_Fixed([[maybe_unused]] size_t in_size=NUM_BITS) { + emp_assert(in_size <= NUM_BITS, in_size, NUM_BITS); + } + Bits_Data_Size_Fixed(const Bits_Data_Size_Fixed &) = default; + + template + void serialize(Archive & /* ar */) { /* Nothing to do here. */ } + + [[nodiscard]] constexpr bool OK() const { return true; } // Nothing to check yet. + }; + + + // ------------------------------------------------------------------------------------ + // RAW MEMORY MANAGEMENT + // ------------------------------------------------------------------------------------ + + /// Data & functions for Bits types with fixed memory (size may be dynamic, capped by CAPACITY) + template + struct Bits_Data_Mem_Static_Base : public BASE_T { + using base_size_t = BASE_T; + using field_t = bits_field_t; + static constexpr size_t MAX_FIELDS = (1 + ((CAPACITY - 1) / NUM_FIELD_BITS)); + + emp::array bits; ///< Fields to hold the actual bit values. + + Bits_Data_Mem_Static_Base() = default; + Bits_Data_Mem_Static_Base(size_t num_bits) : BASE_T(num_bits) + { + emp_assert(num_bits <= CAPACITY, num_bits, CAPACITY); + } + Bits_Data_Mem_Static_Base(const Bits_Data_Mem_Static_Base &) = default; + Bits_Data_Mem_Static_Base(Bits_Data_Mem_Static_Base &&) = default; + + Bits_Data_Mem_Static_Base & operator=(const Bits_Data_Mem_Static_Base &) = default; + Bits_Data_Mem_Static_Base & operator=(Bits_Data_Mem_Static_Base &&) = default; + + // --- Helper functions -- + + [[nodiscard]] Ptr FieldPtr() { return bits.data(); } + [[nodiscard]] Ptr FieldPtr() const { return bits.data(); } + + void RawResize(const size_t new_size, const bool preserve_data=false) { + const size_t old_num_fields = BASE_T::NumFields(); + BASE_T::SetSize(new_size); + if (preserve_data && BASE_T::NumEndBits()) { + bits[BASE_T::LastField()] &= BASE_T::EndMask(); + for (size_t id = BASE_T::NumFields(); id < old_num_fields; ++id) bits[id] = 0; + } + } + + [[nodiscard]] auto AsSpan() { return std::span(bits.data(), MAX_FIELDS); } + [[nodiscard]] auto AsSpan() const { return std::span(bits.data(), MAX_FIELDS); } + + [[nodiscard]] bool OK() const { return true; } // Nothing to check yet. + + template + void serialize(Archive & ar) { + BASE_T::serialize(ar); // Save size info. + for (size_t i=0; i < BASE_T::NumFields(); ++i) { ar(bits[i]); } + } + + }; + + template + using Bits_Data_Mem_Static = + Bits_Data_Mem_Static_Base< Bits_Data_Size_Var, CAPACITY >; + + template + using Bits_Data_Mem_Fixed = + Bits_Data_Mem_Static_Base< Bits_Data_Size_Fixed, CAPACITY >; + + /// Data & functions for Bits types with dynamic memory (size is tracked elsewhere) + template + struct Bits_Data_Mem_Dynamic : public Bits_Data_Size_Var + { + using base_t = Bits_Data_Size_Var; + using base_size_t = base_t; + using field_t = bits_field_t; + + Ptr bits; ///< Pointer to array with the status of each bit + + Bits_Data_Mem_Dynamic(size_t num_bits=DEFAULT_SIZE) : base_t(num_bits), bits(nullptr) + { + if (num_bits) bits = NewArrayPtr(NumBitFields(num_bits)); + } + Bits_Data_Mem_Dynamic(const Bits_Data_Mem_Dynamic & in) : base_t(), bits(nullptr) { Copy(in); } + Bits_Data_Mem_Dynamic(Bits_Data_Mem_Dynamic && in) : bits(nullptr) { Move(std::move(in)); } + ~Bits_Data_Mem_Dynamic() { if (bits) bits.DeleteArray(); } + + Bits_Data_Mem_Dynamic & operator=(const Bits_Data_Mem_Dynamic & in) { Copy(in); return *this; } + Bits_Data_Mem_Dynamic & operator=(Bits_Data_Mem_Dynamic && in) { Move(std::move(in)); return *this; } + + // --- Helper functions -- + + [[nodiscard]] Ptr FieldPtr() { return bits; } + [[nodiscard]] Ptr FieldPtr() const { return bits; } + + void MakeEmpty() { + base_t::SetSize(0); + if (bits) bits.DeleteArray(); + bits = nullptr; + } + + void RawResize(const size_t new_size, const bool preserve_data=false) { + if (new_size == 0) { return MakeEmpty(); } + + // See if number of bit fields needs to change. + const size_t num_old_fields = base_t::NumFields(); + const size_t num_new_fields = NumBitFields(new_size); + + if (num_old_fields != num_new_fields) { + auto new_bits = NewArrayPtr(num_new_fields); + if (num_old_fields) { + if (preserve_data) { + size_t copy_count = std::min(num_old_fields, num_new_fields); + emp::CopyMemory(bits, new_bits, copy_count); + } + bits.DeleteArray(); // Delete old memory + } + bits = new_bits; // Use new memory + if (preserve_data) { + // Zero out any newly added fields. + for (size_t i = num_old_fields; i < num_new_fields; ++i) bits[i] = 0; + } + } + + base_t::SetSize(new_size); + + // Clear out any extra bits in the last field. + if (preserve_data && base_t::NumEndBits()) { + bits[base_t::LastField()] &= base_t::EndMask(); + } + } + + // Assume size is already correct. + void Copy(const Bits_Data_Mem_Dynamic & in) { + RawResize(in.NumBits()); + for (size_t i = 0; i < base_t::NumFields(); ++i) bits[i] = in.bits[i]; + } + + void Move(Bits_Data_Mem_Dynamic && in) { + base_t::SetSize(in.NumBits()); + if (bits) bits.DeleteArray(); // Clear out old bits. + bits = in.bits; // Move over the bits. + in.bits = nullptr; // Clear them out of the original. + } + + [[nodiscard]] auto AsSpan() { return std::span(bits.Raw(), base_t::NumFields()); } + [[nodiscard]] auto AsSpan() const { return std::span(bits.Raw(), base_t::NumFields()); } + + template + void save(Archive & ar) { + base_t::serialize(ar); // Save size info. + for (size_t i=0; i < base_t::NumFields(); ++i) { + ar(bits[i]); + } + } + + template + void load(Archive & ar) { + base_t::serialize(ar); + if (bits) bits.DeleteArray(); // Delete old memory if needed + bits = NewArrayPtr(base_t::NumFields()); + for (size_t i=0; i < base_t::NumFields(); ++i) { + ar(bits[i]); + } + } + + bool OK() const { + // Do some checking on the bits array ptr to make sure it's value. + if (bits) { + #ifdef EMP_TRACK_MEM + emp_assert(bits.DebugIsArray()); // Must be marked as an array. + emp_assert(bits.OK()); // Pointer must be okay. + #endif + } + else { emp_assert(base_t::num_bits == 0); } // If bits is null, num_bits should be zero. + return true; + } + }; + + /// Data & functions for Bits types with dynamic memory (size is tracked elsewhere) + template + struct Bits_Data_Mem_Watermark : public Bits_Data_Mem_Dynamic + { + using this_t = Bits_Data_Mem_Watermark; + using base_t = Bits_Data_Mem_Dynamic; + using field_t = bits_field_t; + using base_t::bits; ///< Pointer to array with the status of each bit + size_t field_capacity = 0; ///< How many fields is the watermark up to? + + Bits_Data_Mem_Watermark(size_t num_bits=DEFAULT_SIZE) : base_t(num_bits) + { + field_capacity = base_t::NumFields(); + } + Bits_Data_Mem_Watermark(const this_t & in) : base_t(0) { Copy(in); } + Bits_Data_Mem_Watermark(this_t && in) : base_t(0) { Move(std::move(in)); } + ~Bits_Data_Mem_Watermark() { /* cleanup in base class */ } + + Bits_Data_Mem_Watermark & operator=(const this_t & in) { Copy(in); return *this; } + Bits_Data_Mem_Watermark & operator=(this_t && in) { Move(std::move(in)); return *this; } + + // --- Helper functions -- + + /// Resize to have at least the specified number of fields. + /// @param new_size The number of bits the new data needs to hold. + /// @param preserve_data Should we keep existing bits and zero out new bits? + void RawResize(const size_t new_size, const bool preserve_data=false) { + // See if number of bit fields needs to change. + const size_t num_old_fields = base_t::NumFields(); + const size_t num_new_fields = NumBitFields(new_size); + + // If we need more fields than are currently available, reallocate memory. + if (num_new_fields > field_capacity) { + auto new_bits = NewArrayPtr(num_new_fields); + if (field_capacity) { // If we already had some allocated fields... + // If needed, copy over previous memory. + if (preserve_data) emp::CopyMemory(bits, new_bits, field_capacity); + bits.DeleteArray(); // Delete old memory + } + field_capacity = num_new_fields; + bits = new_bits; // Use new memory + } + + base_t::SetSize(new_size); + + if (preserve_data) { + // Clear any new (or previously unused) fields. + for (size_t i = num_old_fields; i < num_new_fields; ++i) bits[i] = 0; + + // Clear out any extra end bits. + if (base_t::NumEndBits()) bits[base_t::LastField()] &= base_t::EndMask(); + } + } + + void Copy(const Bits_Data_Mem_Watermark & in) { // Same as base class, but call THIS RawResize(). + RawResize(in.NumBits()); + for (size_t i = 0; i < base_t::NumFields(); ++i) bits[i] = in.bits[i]; + } + + void Move(Bits_Data_Mem_Watermark && in) { + base_t::Move(std::move(in)); + field_capacity = in.field_capacity; + } + + template + void save(Archive & ar) { base_t::save(ar); } // Base class handles saving. + + template + void load(Archive & ar) { + base_t::load(ar); + field_capacity = base_t::NumFields(); // Use loaded size as capacity. + } + + bool OK() const { + emp_assert(field_capacity >= base_t::NumFields()); + return base_t::OK(); + } + }; + + + + /// Internal data for the Bits class to separate static vs. dynamic. + template + struct Bits_Data : public BASE_T + { + using field_t = bits_field_t; + + Bits_Data() = default; + Bits_Data(size_t num_bits) : BASE_T(num_bits) { } + Bits_Data(const Bits_Data & in) = default; + Bits_Data(Bits_Data && in) = default; + + Bits_Data & operator=(const Bits_Data &) = default; + Bits_Data & operator=(Bits_Data &&) = default; + + [[nodiscard]] emp::Ptr BytePtr() { + return BASE_T::FieldPtr().template ReinterpretCast(); + } + [[nodiscard]] emp::Ptr BytePtr() const { + return BASE_T::FieldPtr().template ReinterpretCast(); + } + + [[nodiscard]] auto AsByteSpan() const { return std::as_bytes( BASE_T::AsSpan() ); } + + [[nodiscard]] bool OK() const { + bool result = BASE_T::OK(); + + // If there are end bits, make sure that everything past the last one is clear. + if (BASE_T::NumEndBits()) { + // Make sure final bits are zeroed out. + const field_t excess_bits = + BASE_T::bits[BASE_T::LastField()] & ~MaskLow(BASE_T::NumEndBits()); + result &= !excess_bits; + } + + return result; + } + + }; + } + + using Bits_WatermarkData = internal::Bits_Data< internal::Bits_Data_Mem_Watermark<0> >; + using Bits_DynamicData = internal::Bits_Data< internal::Bits_Data_Mem_Dynamic<0> >; + template + using Bits_FixedData = internal::Bits_Data< internal::Bits_Data_Mem_Fixed >; + template + using Bits_StaticData = internal::Bits_Data< internal::Bits_Data_Mem_Static >; +} + +#endif // #ifndef EMP_BITS_BITS_DATA_HPP_INCLUDE diff --git a/include/emp/bits/_bitset_helpers.hpp b/include/emp/bits/_bitset_helpers.hpp index a64bf242b0..a655b865d2 100644 --- a/include/emp/bits/_bitset_helpers.hpp +++ b/include/emp/bits/_bitset_helpers.hpp @@ -1,15 +1,17 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020. - * - * @file _bitset_helpers.hpp + * @file * @brief An internal Empirical class with tools to build collections of bits. */ #ifndef EMP_BITS__BITSET_HELPERS_HPP_INCLUDE #define EMP_BITS__BITSET_HELPERS_HPP_INCLUDE +#include namespace emp { diff --git a/include/emp/bits/bitset_utils.hpp b/include/emp/bits/bitset_utils.hpp index d0ea890b40..83a9641a58 100644 --- a/include/emp/bits/bitset_utils.hpp +++ b/include/emp/bits/bitset_utils.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2023. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2020. - * - * @file bitset_utils.hpp + * @file * @brief A set of simple functions to manipulate bitsets. * @note Status: BETA */ @@ -11,8 +12,46 @@ #ifndef EMP_BITS_BITSET_UTILS_HPP_INCLUDE #define EMP_BITS_BITSET_UTILS_HPP_INCLUDE +#include +#include // uint8_t, uint16_t, etc. +#include +#include +#include + +#include "../base/Ptr.hpp" + namespace emp { + /// @brief Use size_t as the default bits field type. + using bits_field_t = size_t; + + /// @brief Track the number of bits in a single bit field. + static constexpr size_t NUM_FIELD_BITS = sizeof(bits_field_t)*8; + + /// @brief Convert a bit count to the number of fields needed to store them. + [[nodiscard]] static constexpr size_t NumBitFields(size_t num_bits) noexcept { + return num_bits ? (1 + ((num_bits - 1) / NUM_FIELD_BITS)) : 0; + } + + /// @brief Convert a single bit field to a string. + /// @param field A single bit field to convert to a string. + [[nodiscard]] static std::string BitFieldToString(bits_field_t field) { + std::stringstream ss; + ss << '[' << std::hex << field << ']'; + return ss.str(); + } + + /// @brief Convert a series of bit fields to a string. + /// @param field A single bit field to convert to a string. + [[nodiscard]] static std::string BitFieldsToString(emp::Ptr bits, size_t count) { + std::stringstream ss; + for (size_t i = 0; i < count; ++i) { + if (i) ss << ' '; + ss << BitFieldToString(bits[i]); + } + return ss.str(); + } + /// Create a series of a specified number of ones (at compile time) in a uint. template constexpr uint32_t UIntMaskFirst() { return (UIntMaskFirst() << 1) | 1; } @@ -33,58 +72,77 @@ namespace emp { 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 }; - /// Count the number of bits in a 64-bit unsigned integer. - inline constexpr size_t count_bits(uint64_t val) { - return - ByteCount[ val >> 56 ] + - ByteCount[ (val >> 48) & 0xFF ] + - ByteCount[ (val >> 40) & 0xFF ] + - ByteCount[ (val >> 32) & 0xFF ] + - ByteCount[ (val >> 24) & 0xFF ] + - ByteCount[ (val >> 16) & 0xFF ] + - ByteCount[ (val >> 8) & 0xFF ] + - ByteCount[ val & 0xFF ]; - } - - // /// Count the number of bits in a 32-bit unsigned integer. - // inline constexpr size_t count_bits(uint32_t val) { - // return - // ByteCount[ val >> 24 ] + - // ByteCount[ (val >> 16) & 0xFF ] + - // ByteCount[ (val >> 8) & 0xFF ] + - // ByteCount[ val & 0xFF ]; - // } + /// Count the number of bits in an unsigned integer. + template + [[nodiscard]] inline constexpr size_t count_bits(T val) { + static_assert( std::is_unsigned_v, "Bit manipulation requires unsigned values." ); + constexpr size_t num_bytes = sizeof(T); + static_assert(num_bytes <= 8, "count_bits() requires 8 or fewer bytes."); + + size_t out_ones = ByteCount[ val & 0xFF ]; + if constexpr (num_bytes > 1) { + out_ones += ByteCount[ (val >> 8) & 0xFF ]; + } + if constexpr (num_bytes > 2) { + out_ones += ByteCount[ (val >> 24) & 0xFF ] + + ByteCount[ (val >> 16) & 0xFF ]; + } + if constexpr (num_bytes > 4) { + out_ones += ByteCount[ val >> 56 ] + + ByteCount[ (val >> 48) & 0xFF ] + + ByteCount[ (val >> 40) & 0xFF ] + + ByteCount[ (val >> 32) & 0xFF ]; + } + return out_ones; + } + /// Return the position of the first one bit template - inline constexpr size_t find_bit(T val) { return count_bits( (~val) & (val-1) ); } + [[nodiscard]] inline constexpr size_t find_bit(const T val) { + static_assert( std::is_unsigned_v, "Bit manipulation requires unsigned values." ); + return count_bits( (~val) & (val-1) ); + } + + /// Return the position of the first one bit + template + [[nodiscard]] inline constexpr size_t find_last_bit(T val) { + static_assert( std::is_unsigned_v, "Bit manipulation requires unsigned values." ); + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + if constexpr (sizeof(T) > 1) val |= val >> 8; + if constexpr (sizeof(T) > 2) val |= val >> 16; + if constexpr (sizeof(T) > 4) val |= val >> 32; + return count_bits(val) - 1; + } /// Return the position of the first one bit AND REMOVE IT. template inline size_t pop_bit(T & val) { + static_assert( std::is_unsigned_v, "Bit manipulation requires unsigned values." ); const size_t pos = find_bit(val); val &= ~(1 << pos); return pos; } - /// A compile-time bit counter. - template - static constexpr int CountOnes(TYPE x) { return x == 0 ? 0 : (CountOnes(x/2) + (x&1)); } - /// Quick bit-mask generator for low bits. - template - static constexpr TYPE MaskLow(std::size_t num_bits) { + template + [[nodiscard]] static constexpr TYPE MaskLow(std::size_t num_bits) { + static_assert( std::is_unsigned_v, "Bit manipulation requires unsigned values." ); return (num_bits == 8*sizeof(TYPE)) ? ((TYPE)-1) : ((((TYPE)1) << num_bits) - 1); } /// Quick bit-mask generator for high bits. - template - static constexpr TYPE MaskHigh(std::size_t num_bits) { + template + [[nodiscard]] static constexpr TYPE MaskHigh(std::size_t num_bits) { + static_assert( std::is_unsigned_v, "Bit manipulation requires unsigned values." ); return MaskLow(num_bits) << (8*sizeof(TYPE)-num_bits); } - template - static constexpr TYPE MaskUsed(TYPE val) { + template + [[nodiscard]] static constexpr TYPE MaskUsed(TYPE val) { + static_assert( std::is_unsigned_v, "Bit manipulation requires unsigned values." ); size_t shift = 1; TYPE last = 0; while (val != last) { // While the shift is making progress... @@ -96,6 +154,111 @@ namespace emp { return val; } + template + [[nodiscard]] constexpr T ReverseBits(T in) { + constexpr size_t num_bytes = sizeof(T); + + static_assert( std::is_unsigned_v, "Bit manipulation requires unsigned values." ); + static_assert( num_bytes == 1 || num_bytes == 2 || num_bytes == 4 || num_bytes == 8, + "ReverseBits() currently requires 1, 2, 4, or 8-byte values." ); + + if constexpr (num_bytes == 1) { + in = static_cast( (in & 0xF0) >> 4 | (in & 0x0F) << 4 ); + in = static_cast( (in & 0xCC) >> 2 | (in & 0x33) << 2 ); + in = static_cast( (in & 0xAA) >> 1 | (in & 0x55) << 1 ); + } + else if constexpr (num_bytes == 2) { + in = static_cast( (in & 0xFF00) >> 8 | (in & 0x00FF) << 8 ); + in = static_cast( (in & 0xF0F0) >> 4 | (in & 0x0F0F) << 4 ); + in = static_cast( (in & 0xCCCC) >> 2 | (in & 0x3333) << 2 ); + in = static_cast( (in & 0xAAAA) >> 1 | (in & 0x5555) << 1 ); + } + else if constexpr (num_bytes == 4) { + in = static_cast( (in & 0xFFFF0000) >> 16 | (in & 0x0000FFFF) << 16 ); + in = static_cast( (in & 0xFF00FF00) >> 8 | (in & 0x00FF00FF) << 8 ); + in = static_cast( (in & 0xF0F0F0F0) >> 4 | (in & 0x0F0F0F0F) << 4 ); + in = static_cast( (in & 0xCCCCCCCC) >> 2 | (in & 0x33333333) << 2 ); + in = static_cast( (in & 0xAAAAAAAA) >> 1 | (in & 0x55555555) << 1 ); + } + else /* if constexpr (num_bytes == 8) */ { + in = static_cast( (in & 0xFFFFFFFF00000000) >> 32 | (in & 0x00000000FFFFFFFF) << 32 ); + in = static_cast( (in & 0xFFFF0000FFFF0000) >> 16 | (in & 0x0000FFFF0000FFFF) << 16 ); + in = static_cast( (in & 0xFF00FF00FF00FF00) >> 8 | (in & 0x00FF00FF00FF00FF) << 8 ); + in = static_cast( (in & 0xF0F0F0F0F0F0F0F0) >> 4 | (in & 0x0F0F0F0F0F0F0F0F) << 4 ); + in = static_cast( (in & 0xCCCCCCCCCCCCCCCC) >> 2 | (in & 0x3333333333333333) << 2 ); + in = static_cast( (in & 0xAAAAAAAAAAAAAAAA) >> 1 | (in & 0x5555555555555555) << 1 ); + } + + return in; + } + + // Rotate all bits to the left (looping around) in a provided field. + template + [[nodiscard]] constexpr T RotateBitsLeft( + T in, + size_t rotate_size = 1 + ) { + static_assert( std::is_unsigned_v, "Bit manipulation requires unsigned values." ); + constexpr size_t FIELD_BITS = sizeof(T) * 8; + rotate_size %= FIELD_BITS; // Make sure rotate is in range. + return (in << rotate_size) | + (in >> (FIELD_BITS - rotate_size)); + } + + // Rotate lowest "bit_count" bits to the left (looping around) in a provided field. + template + [[nodiscard]] constexpr T RotateBitsLeft( + T in, + size_t rotate_size, + size_t bit_count + ) { + static_assert( std::is_unsigned_v, "Bit manipulation requires unsigned values." ); + [[maybe_unused]] constexpr size_t FIELD_BITS = sizeof(T) * 8; + emp_assert(bit_count <= FIELD_BITS, "Cannot have more bits than can fit in field."); + rotate_size %= bit_count; // Make sure rotate is in range. + const T out = (in << rotate_size) | (in >> (bit_count - rotate_size)); + return out & MaskLow(bit_count); // Zero out excess bits. + } + + // Rotate all bits to the left (looping around) in a provided field. + template + [[nodiscard]] constexpr T RotateBitsRight( + T in, + size_t rotate_size = 1 + ) { + static_assert( std::is_unsigned_v, "Bit manipulation requires unsigned values." ); + constexpr size_t FIELD_BITS = sizeof(T) * 8; + rotate_size %= FIELD_BITS; // Make sure rotate is in range. + return (in >> rotate_size) | + (in << (FIELD_BITS - rotate_size)); + } + + // Rotate lowest "bit_count" bits to the left (looping around) in a provided field. + template + [[nodiscard]] constexpr T RotateBitsRight( + T in, + size_t rotate_size, + size_t bit_count + ) { + static_assert( std::is_unsigned_v, "Bit manipulation requires unsigned values." ); + [[maybe_unused]] constexpr size_t FIELD_BITS = sizeof(T) * 8; + emp_assert(bit_count <= FIELD_BITS, "Cannot have more bits than can fit in field."); + rotate_size %= bit_count; // Make sure rotate is in range. + const T out = (in >> rotate_size) | (in << (bit_count - rotate_size)); + return out & MaskLow(bit_count); // Zero out excess bits. + } + + /// Count the number of bits ('0' or '1') found in a string. + static size_t CountBits(const std::string & bitstring) { + return static_cast( + std::count_if( + bitstring.begin(), + bitstring.end(), + [](char i) { return i == '0' || i == '1'; } + ) + ); + } + /* // Returns the position of the first set (one) bit or a -1 if none exist. template diff --git a/include/emp/compiler/DFA.hpp b/include/emp/compiler/DFA.hpp index c38936935e..83becd0841 100644 --- a/include/emp/compiler/DFA.hpp +++ b/include/emp/compiler/DFA.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021. - * - * @file DFA.hpp + * @file * @brief A Deterministic Finite Automata simulator. * @note Status: BETA */ @@ -11,6 +12,9 @@ #ifndef EMP_COMPILER_DFA_HPP_INCLUDE #define EMP_COMPILER_DFA_HPP_INCLUDE +#include +#include +#include #include #include "../base/array.hpp" @@ -25,13 +29,17 @@ namespace emp { private: emp::vector< emp::array > transitions; emp::vector< STOP_TYPE > is_stop; // 0=not stop; other values for STOP return value. + + using this_t = tDFA; public: tDFA(size_t num_states=0) : transitions(num_states), is_stop(num_states, 0) { for (auto & t : transitions) t.fill(-1); } - tDFA(const tDFA &) = default; + tDFA(const this_t &) = default; + tDFA(this_t &&) = default; ~tDFA() { ; } - tDFA & operator=(const tDFA &) = default; + this_t & operator=(const this_t &) = default; + this_t & operator=(this_t &&) = default; using stop_t = STOP_TYPE; diff --git a/include/emp/compiler/Lexer.hpp b/include/emp/compiler/Lexer.hpp index c6d7d4a8dd..8ba9070f1a 100644 --- a/include/emp/compiler/Lexer.hpp +++ b/include/emp/compiler/Lexer.hpp @@ -1,17 +1,40 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2023. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2019. - * - * @file Lexer.hpp + * @file * @brief A general-purpose, fast lexer. - * @note Status: ALPHA + * @note Status: BETA + * + * Build a lexer that can convert input strings or streams into a series of provided tokens. + * + * Use AddToken(name, regex) to list out the relevant tokens. + * 'name' is the unique name for this token. + * 'regex' is the regular expression that describes this token. + * It will return a unique ID associated with this lexeme. + * + * IgnoreToken(name, regex) uses the same arguments, but is used for tokens that + * should be skipped over. + * + * Names and IDs can be recovered later using GetTokenID(name) and GetTokenName(id). + * + * Tokens can be retrieved either one at a time with Process(string) or Process(stream), + * which will return the next (non-ignored) token, removing it from the input. + * + * Alternatively, an entire series of tokens can be processed with Tokenize(). + * + * Finally, GetLexeme() can be used to retrieve the lexeme from the most recent token found. */ #ifndef EMP_COMPILER_LEXER_HPP_INCLUDE #define EMP_COMPILER_LEXER_HPP_INCLUDE +#include +#include #include +#include #include #include "../base/map.hpp" @@ -32,9 +55,12 @@ namespace emp { bool save_lexeme; ///< Preserve the lexeme for this token? bool save_token; ///< Keep token at all? (Whitespace and comments are often discarded). + // Default constructor produces an error token. + TokenInfo() : name(""), desc("Unable to parse input!"), regex(""), + id(-1), save_lexeme(true), save_token(true) { } TokenInfo(const std::string & _name, const std::string & _regex, int _id, bool _save_l=true, bool _save_t=true, const std::string & _desc="") - : name(_name), desc(_desc), regex(_regex), id(_id), save_lexeme(_save_l), save_token(_save_t) { ; } + : name(_name), desc(_desc), regex(_regex), id(_id), save_lexeme(_save_l), save_token(_save_t) { } TokenInfo(const TokenInfo &) = default; TokenInfo(TokenInfo &&) = default; TokenInfo & operator=(const TokenInfo &) = default; @@ -53,52 +79,140 @@ namespace emp { /// Information about a token instance from an input stream. struct Token { - int token_id; ///< Which type of token is this? + int id; ///< Which type of token is this? std::string lexeme; ///< Sequence matched by this token (or empty if not saved) size_t line_id; ///< Which line did this token start on? - Token(int id, const std::string & str="", size_t _line=0) - : token_id(id), lexeme(str), line_id(_line) { ; } + Token(int _id, const std::string & str="", size_t _line=0) + : id(_id), lexeme(str), line_id(_line) { ; } Token(const Token &) = default; Token(Token &&) = default; Token & operator=(const Token &) = default; Token & operator=(Token &&) = default; /// Token will automatically convert to its ID if used as an int. - operator int() const { return token_id; } + operator int() const { return id; } /// Token will automatically convert to its matched sequence (lexeme) is used as a string. operator const std::string &() const { return lexeme; } }; + class TokenStream { + private: + std::string name = ""; + emp::vector tokens; + + public: + TokenStream(const std::string & in_name) : name(in_name) { } + TokenStream(const TokenStream &) = default; + TokenStream(TokenStream &&) = default; + TokenStream(const emp::vector & in_tokens, const std::string & in_name) + : name(in_name), tokens(in_tokens) { } + + TokenStream & operator=(const TokenStream &) = default; + TokenStream & operator=(TokenStream &&) = default; + + class Iterator { + private: + emp::Ptr ts; + size_t pos; + + public: + Iterator(const Iterator &) = default; + Iterator(const TokenStream & in_ts, size_t in_pos) : ts(&in_ts), pos(in_pos) { } + Iterator & operator=(const Iterator &) = default; + + const TokenStream & GetTokenStream() const { return *ts; } + size_t GetIndex() const { return pos; } + emp::Ptr ToPtr() const { return ts->GetPtr(pos); } + + Token operator*() const { return ts->tokens[pos]; } + const Token * operator->() const { return &(ts->tokens[pos]); } + + bool operator==(const Iterator & in) const { return ToPtr() == in.ToPtr(); } + bool operator!=(const Iterator & in) const { return ToPtr() != in.ToPtr(); } + bool operator< (const Iterator & in) const { return ToPtr() < in.ToPtr(); } + bool operator<=(const Iterator & in) const { return ToPtr() <= in.ToPtr(); } + bool operator> (const Iterator & in) const { return ToPtr() > in.ToPtr(); } + bool operator>=(const Iterator & in) const { return ToPtr() >= in.ToPtr(); } + + Iterator & operator++() { ++pos; return *this; } + Iterator operator++(int) { Iterator old(*this); ++pos; return old; } + Iterator & operator--() { --pos; return *this; } + Iterator operator--(int) { Iterator old(*this); --pos; return old; } + + bool IsValid() const { return pos < ts->size(); } + bool AtEnd() const { return pos == ts->size(); } + + operator bool() const { return IsValid(); } + }; + + size_t size() const { return tokens.size(); } + const Token & Get(size_t pos) const { return tokens[pos]; } + emp::Ptr GetPtr(size_t pos) const { return &(tokens.data()[pos]); } + const std::string & GetName() const { return name; } + Iterator begin() const { return Iterator(*this, 0); } + Iterator end() const { return Iterator(*this, tokens.size()); } + const Token & back() const { return tokens.back(); } + + void push_back(const Token & in) { tokens.push_back(in); } + + void Print(std::ostream & os=std::cout) const { + for (auto x : tokens) { + os << " [" << x.lexeme << "]"; + } + os << std::endl; + } + }; + + /// A lexer with a set of token types (and associated regular expressions) class Lexer { private: static constexpr int MAX_ID = 255; ///< IDs count down so that first ones have priority. static constexpr int ERROR_ID = -1; ///< Code for unknown token ID. - emp::vector token_set; ///< List of all active tokens. + emp::vector token_set; ///< List of all active tokens types. emp::map token_map; ///< Map of token names to id. int cur_token_id = MAX_ID; ///< Which ID should the next new token get? mutable bool generate_lexer = false; ///< Do we need to regenerate the lexer? mutable DFA lexer_dfa; ///< Table driven lexer implementation. - std::string lexeme; ///< Current state of lexeme being generated. + mutable std::string lexeme; ///< Current state of lexeme being generated. - const TokenInfo ERROR_TOKEN{"", "", ERROR_ID, true, true, "Unable to parse input!"}; + static const TokenInfo & ERROR_TOKEN() { + static const TokenInfo token; + return token; + } public: - Lexer() { ; } - ~Lexer() { ; } + Lexer() = default; + Lexer(const Lexer &) = default; + Lexer(Lexer &&) = default; + ~Lexer() = default; + + Lexer & operator=(const Lexer &) = default; + Lexer & operator=(Lexer &&) = default; /// How many types of tokens can be identified in this Lexer? size_t GetNumTokens() const { return token_set.size(); } + void Reset() { + token_set.resize(0); + token_map.clear(); + cur_token_id = MAX_ID; + generate_lexer = false; + } + bool TokenOK(int id) const { return id >= 0 && id < cur_token_id; } /// Add a new token, specified by a name and the regex used to identify it. /// Note that token ids count down with highest IDs having priority. - int AddToken(const std::string & name, const std::string & regex, - bool save_lexeme=true, bool save_token=true, const std::string & desc="") { + int AddToken(const std::string & name, + const std::string & regex, + bool save_lexeme = true, + bool save_token = true, + const std::string & desc = "") + { int id = cur_token_id--; // Grab the next available token id. generate_lexer = true; // Indicate the the lexer DFA needs to be rebuilt. token_set.emplace_back( name, regex, id, save_lexeme, save_token, desc ); @@ -124,7 +238,7 @@ namespace emp { /// Get the full information about a token (you provide the id) const TokenInfo & GetTokenInfo(int id) const { - if (id > MAX_ID || id <= cur_token_id) return ERROR_TOKEN; + if (id > MAX_ID || id <= cur_token_id) return ERROR_TOKEN(); return token_set[(size_t)(MAX_ID - id)]; } @@ -158,7 +272,7 @@ namespace emp { /// longest one we can find.) Every time we do hit a valid lexeme, store it as the current /// "best" and keep going. Once we hit a point where no other valid lexemes are possible, /// stop and return the best we've found so far. - Token Process(std::istream & is) { + Token Process(std::istream & is) const { // If we still need to generate the DFA for the lexer, do so. if (generate_lexer) Generate(); @@ -171,7 +285,7 @@ namespace emp { lexeme.resize(0); // Keep looking as long as: - // 1: We may still be able to contine the current lexeme. + // 1: We may still be able to continue the current lexeme. // 2: We have not entered an invalid state. // 3: Our input stream has more symbols. while (cur_stop >= 0 && cur_state >= 0 && is) { @@ -199,8 +313,8 @@ namespace emp { return { best_stop, lexeme }; } - /// Shortcut to process a string rather than a stream. - Token Process(std::string & in_str) { + /// Shortcut to process a string rather than a stream, chopping off one token each time. + Token Process(std::string & in_str) const { std::stringstream ss; ss << in_str; auto out_val = Process(ss); @@ -208,8 +322,16 @@ namespace emp { return out_val; } + /// Shortcut to just get a single token. + Token ToToken(std::string_view in_str) const { + std::stringstream ss; + ss << in_str; + auto out_val = Process(ss); + return out_val; + } + /// Turn an input stream of text into a vector of tokens. - emp::vector Tokenize(std::istream & is) { + TokenStream Tokenize(std::istream & is, const std::string & name="in_stream") const { emp::vector out_tokens; size_t cur_line = 1; emp::Token token = Process(is); @@ -219,28 +341,30 @@ namespace emp { if (GetSaveToken(token)) out_tokens.push_back(token); token = Process(is); } - return out_tokens; + return TokenStream{out_tokens, name}; } /// Turn an input string into a vector of tokens. - emp::vector Tokenize(const std::string & str) { + TokenStream Tokenize(std::string_view str, const std::string & name="in_view") const { std::stringstream ss; ss << str; - return Tokenize(ss); + return Tokenize(ss, name); } /// Turn a vector of strings into a vector of tokens. - emp::vector Tokenize(const emp::vector & str_v) { + TokenStream Tokenize(const emp::vector & str_v, + const std::string & name="in_string vector") const + { std::stringstream ss; for (auto & str : str_v) { - ss << str; + ss << str << '\n'; } - return Tokenize(ss); + return Tokenize(ss, name); } /// Get the lexeme associated with the last token identified. - const std::string & GetLexeme() { return lexeme; } + const std::string & GetLexeme() const { return lexeme; } /// Print the full information about this lexer (for debugging) void Print(std::ostream & os=std::cout) const { @@ -250,7 +374,7 @@ namespace emp { } /// Try out the lexer on a string and demonstrate how it's tokenized. - void DebugString(std::string test_string) { + void DebugString(std::string test_string) const { std::stringstream ss; ss << test_string; diff --git a/include/emp/compiler/NFA.hpp b/include/emp/compiler/NFA.hpp index ae3bd23dcd..ba7bbc24ab 100644 --- a/include/emp/compiler/NFA.hpp +++ b/include/emp/compiler/NFA.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file NFA.hpp + * @file * @brief A Non-deterministic Finite Automata simulator * @note Status: BETA * @@ -23,8 +24,10 @@ #define EMP_COMPILER_NFA_HPP_INCLUDE +#include #include #include +#include #include "../base/vector.hpp" #include "../bits/BitSet.hpp" diff --git a/include/emp/compiler/RegEx.hpp b/include/emp/compiler/RegEx.hpp index a38ec0f97e..dc800332b5 100644 --- a/include/emp/compiler/RegEx.hpp +++ b/include/emp/compiler/RegEx.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2022. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2019. - * - * @file RegEx.hpp + * @file * @brief Basic regular expression handler. * @note Status: BETA * @@ -29,8 +30,11 @@ * static DFA to_DFA(const RegEx & regex); * * - * @todo Need to implement ^ and $ (beginning and end of line) - * @todo Need to implement {n}, {n,} and {n,m} (exactly n, at least n, and n-m copies, respecitvely) + * @todo Implement ^ and $ (beginning and end of line) + * @todo Implement {n}, {n,} and {n,m} (exactly n, at least n, and n-m copies, respectively) + * @todo Implement \d (for digits), \s (for whitespace), etc. + * @todo Consider a separator (maybe backtick?) to divide up a regex expression; + * the result can be returned by each section as a vector of strings. */ #ifndef EMP_COMPILER_REGEX_HPP_INCLUDE @@ -39,6 +43,7 @@ #include #include +#include #include #include "../base/Ptr.hpp" @@ -205,9 +210,11 @@ namespace emp { // If blocks are nested, merge them into a single block. if (nodes[i]->AsBlock()) { auto old_node = nodes[i]->AsBlock(); // Save the old node for merging. - nodes.erase(nodes.begin() + (int) i); // Remove block from nodes. - nodes.insert(nodes.begin() + (int) i, old_node->nodes.begin(), old_node->nodes.end()); - old_node->nodes.resize(0); // Don't recurse delete since nodes were moved! + nodes.erase(nodes.begin() + (int) i); // Remove block from nodes. + if (old_node->nodes.size()) { + nodes.insert(nodes.begin() + (int) i, old_node->nodes.begin(), old_node->nodes.end()); + old_node->nodes.resize(0); // Don't recurse delete since nodes were moved! + } old_node.Delete(); i--; modify = true; @@ -345,7 +352,7 @@ namespace emp { case '-': case '\\': case ']': - case '[': + case '[': // technically doesn't need to be escaped, but allowed. case '^': break; default: @@ -455,7 +462,7 @@ namespace emp { /// Process the input regex into a tree representaion. Ptr Process(Ptr cur_block=nullptr) { - emp_assert(pos >= 0 && pos < regex.size(), pos, regex.size()); + emp_assert(pos < regex.size(), pos, regex.size()); // If caller does not provide current block, create one (and return it.) if (cur_block==nullptr) cur_block = NewPtr(); diff --git a/include/emp/compiler/lexer_utils.hpp b/include/emp/compiler/lexer_utils.hpp index 458f0485d4..503b4c4673 100644 --- a/include/emp/compiler/lexer_utils.hpp +++ b/include/emp/compiler/lexer_utils.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file lexer_utils.hpp + * @file * @brief A set of utilities to convert between NFAs and DFAs * @note Status: BETA */ @@ -13,6 +14,7 @@ #include +#include #include // std::pair #include "../base/vector.hpp" @@ -23,6 +25,10 @@ namespace emp { + /** \addtogroup + * @{ + */ + /// Converting DFA to DFA -- no change needed. static inline const DFA & to_DFA(const DFA & dfa) { return dfa; } @@ -52,7 +58,7 @@ namespace emp { std::set next_state = nfa.GetNext(sym, cur_state); if (next_state.size() == 0 && !keep_invalid) continue; // Discard invalid transitions. - // Remove NFA states with ONLY free transisions (they will all have been taken already) + // Remove NFA states with ONLY free transitions (they will all have been taken already) // @CAO do more elegantly! emp::vector remove_set; for (auto x : next_state) if (nfa.IsEmpty(x)) remove_set.push_back(x); @@ -91,7 +97,7 @@ namespace emp { } - /// Merge multiple automata into one NFA (base case, single converstion) + /// Merge multiple automata into one NFA (base case, single conversion) template static NFA MergeNFA(T1 && in) { return to_NFA(std::forward(in)); @@ -143,6 +149,9 @@ namespace emp { return ""; } + + // Close Doxygen group + /** @}*/ } #endif // #ifndef EMP_COMPILER_LEXER_UTILS_HPP_INCLUDE diff --git a/include/emp/compiler/regex_utils.hpp b/include/emp/compiler/regex_utils.hpp new file mode 100644 index 0000000000..cf70984900 --- /dev/null +++ b/include/emp/compiler/regex_utils.hpp @@ -0,0 +1,48 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2021 +*/ +/** + * @file + * @brief Helper functions for building regular expressions. + * @note Status: BETA + */ + +#ifndef EMP_COMPILER_REGEX_UTILS_HPP_INCLUDE +#define EMP_COMPILER_REGEX_UTILS_HPP_INCLUDE + +#include + +#include "RegEx.hpp" + +namespace emp { + + using namespace std::string_literals; + + std::string regex_nested(char open='(', + char close=')', + size_t depth=0, + bool stop_at_newline=true + ) + { + // Setup open and close as literal strings. + std::string open_re = emp::to_string('"', open, '"'); + std::string close_re = emp::to_string('"', close, '"'); + + // Base version has open_re and close_re at either end. + const std::string no_parens = "[^"s + open_re + close_re + (stop_at_newline ? "\n\r]*" : "]*"); + const std::string matched = open_re + no_parens + close_re; + + for (size_t level = 0; level < depth; level++) { + const std::string multi = no_parens + "("s + matched + no_parens + ")*"s; + const std::string matched = open_re + multi + close_re; + } + + return matched; + } + + +} + +#endif // #ifndef EMP_COMPILER_REGEX_UTILS_HPP_INCLUDE diff --git a/include/emp/config/ArgManager.hpp b/include/emp/config/ArgManager.hpp index 176b7a5a7a..e0da716708 100644 --- a/include/emp/config/ArgManager.hpp +++ b/include/emp/config/ArgManager.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file ArgManager.hpp + * @file * @brief A tool for sythesizing command-line arguments, URL query params, and config files. * @note Status: BETA */ @@ -18,6 +19,7 @@ #include #include #include +#include #include #include @@ -187,8 +189,8 @@ namespace emp { } else if (args[i].size() == 2) { // in POSIX, -- means treat subsequent words as literals // so we remove the -- and stop deflagging subsequent words - res.erase(std::next(std::begin(res),i)); - args.erase(std::next(std::begin(args),i)); + res.erase(std::next(std::begin(res),(int) i)); + args.erase(std::next(std::begin(args),(int) i)); break; } // " ", -, ---, ----, etc. left in place and treated as non-flags @@ -300,17 +302,14 @@ namespace emp { ); // store the argument pack + bool is_special = command == "_positional" + || command == "_unknown" + || command == "_invalid"; res.insert({ command, pack_t( - std::next( - std::begin(args), - command == "_positional" - || command == "_unknown" - || command == "_invalid" - ? i : i+1 - ), - j+1 < args.size() ? std::next(std::begin(args), j+1) : std::end(args) + std::next( std::begin(args), (int) (is_special ? i : i+1) ), + j+1 < args.size() ? std::next(std::begin(args), (int) j+1) : std::end(args) ) }); i = j; @@ -683,11 +682,17 @@ namespace emp { }; + #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace cl { + #endif // DOXYGEN_SHOULD_SKIP_THIS /// A simple class to manage command-line arguments that were passed in. /// Derived from emp::vector, but with added functionality for argument handling. - class ArgManager : public emp::vector { + class + #ifdef DOXYGEN_SHOULD_SKIP_THIS + cl:: + #endif + ArgManager : public emp::vector { private: using parent_t = emp::vector; emp::vector arg_names; @@ -795,8 +800,9 @@ namespace emp { }; - + #ifndef DOXYGEN_SHOULD_SKIP_THIS } + #endif // DOXYGEN_SHOULD_SKIP_THIS } #endif // #ifndef EMP_CONFIG_ARGMANAGER_HPP_INCLUDE diff --git a/include/emp/config/ConfigManager.hpp b/include/emp/config/ConfigManager.hpp index 4c760d555d..c8e8afb458 100644 --- a/include/emp/config/ConfigManager.hpp +++ b/include/emp/config/ConfigManager.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file ConfigManager.hpp + * @file * @brief The ConfigManager templated class handles the building and configuration of new objects * of the target type. * diff --git a/include/emp/config/FlagManager.hpp b/include/emp/config/FlagManager.hpp new file mode 100644 index 0000000000..7f93db13f5 --- /dev/null +++ b/include/emp/config/FlagManager.hpp @@ -0,0 +1,173 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ +/** + * @file + * @brief This file contains tools for dealing with command-line flags (from argv and argc). + * @note Status: ALPHA + * + * The FlagManager class will take command line arguments (either in its constructor or with + * the AddFlags() function) and process them appropriately. + * + * For setup, the user must run AddOption with the function to call. Functions to call can take + * zero, one, or two Strings as arguments OR they can take a vector of Strings and the range of + * allowed arguments should be specified. When Process() is run, the appropriate function will + * be called on each and any invalid arguments will trigger an error. + * + * Flags are expected to begin with a '-' and non-flags are expected to NOT begin with a '-'. + * + * If a single dash is followed by multiple characters, each will be processed independently. + * So, "-abc" will be the same as "-a -b -c". + * + * Extra command line arguments will be saves as a vector of strings called "extras" and must + * be processed manually. They can be retrieved with GetExtras(). + * + * + * @todo: Make variable numbers of flag arguments work. + */ + +#ifndef EMP_CONFIG_FLAGMANAGER_HPP_INCLUDE +#define EMP_CONFIG_FLAGMANAGER_HPP_INCLUDE + +#include +#include +#include +#include + +#include "../base/vector.hpp" +#include "../datastructs/map_utils.hpp" +#include "../tools/String.hpp" + +namespace emp { + + class FlagManager { + private: + emp::vector args; + emp::vector extras; + + struct FlagInfo { + String desc; + size_t min_args = 0; + size_t max_args = 0; + std::function &)> fun; + char shortcut = '\0'; + }; + + std::map flag_options; + std::map shortcuts; + + public: + FlagManager() { } + FlagManager(int argc, char* argv[]) { AddFlags(argc, argv); } + + constexpr static size_t npos = static_cast(-1); + + [[nodiscard]] String & operator[](size_t pos) { return args[pos]; } + [[nodiscard]] const String & operator[](size_t pos) const { return args[pos]; } + + emp::vector GetExtras() const { return extras; } + + [[nodiscard]] size_t Find(String pattern) const { + for (size_t i = 0; i < args.size(); ++i) if (args[i] == pattern) return i; + return npos; + } + + [[nodiscard]] bool Has(String pattern) const { return Find(pattern) != npos; } + + // Return true/false if a specific argument is present and REMOVE IT. + bool Use(String pattern) { + size_t pos = Find(pattern); + if (pos == npos) return false; + args.erase(args.begin() + pos); + return true; + } + + void AddOption(String name, String desc="") { + flag_options[name] = FlagInfo{desc, 0, 0, [](const emp::vector &){} }; + } + void AddOption(String name, std::function fun, String desc="") { + flag_options[name] = FlagInfo{desc, 0,0, [fun](const emp::vector &){fun();}}; + } + void AddOption(String name, std::function fun, String desc="") { + flag_options[name] = FlagInfo{desc, 1,1, [fun](const emp::vector & in){fun(in[0]);}}; + } + void AddOption(String name, std::function fun, String desc="") { + flag_options[name] = FlagInfo{desc, 2,2, [fun](const emp::vector & in){fun(in[0],in[1]);}}; + } + void AddOption(String name, std::function &)> fun, + size_t min_args=0, size_t max_args=npos, String desc="") { + flag_options[name] = FlagInfo{desc, min_args,max_args, fun}; + } + + // Allow an option to have a single-letter flag (e.g. "-h" is short for "--help") + template + void AddOption(char shortcut, String name, FUN_T fun, String desc="") { + AddOption(name, fun, desc); + shortcuts[shortcut] = name; + flag_options[name].shortcut = shortcut; + } + + void AddFlags(int argc, char* argv[]) { + for (size_t i = 0; i < (size_t) argc; i++) { + args.push_back(argv[i]); + } + } + + // Process an argument associated with a particular name; return num additional args used. + size_t ProcessArg(String name, size_t cur_pos=0) { + if (!emp::Has(flag_options, name)) { emp::notify::Error("Unknown flag '", name , "'."); } + auto option = flag_options[name]; + emp::vector flag_args; + for (size_t i = 1; i <= option.min_args; ++i) { + flag_args.push_back(args[cur_pos+i]); + } + option.fun(flag_args); + return option.min_args; + } + + // Process an argument associated with a particular character; return num additional args used. + size_t ProcessArg(char c, size_t cur_pos=0) { + if (!emp::Has(shortcuts, c)) { emp::notify::Error("Unknown flag '-", c , "'."); } + return ProcessArg(shortcuts[c], cur_pos); + } + + // Process the argument at a given position. Return number of additional args consumed. + size_t ProcessFlagSet(String name, size_t cur_pos=0) { + size_t offset = 0; + for (size_t i = 1; i < name.size(); ++i) { + offset += ProcessArg(name[i], cur_pos+offset); + } + return offset; + } + + // Process all of the flag data that we have. + void Process() { + for (size_t i = 1; i < args.size(); ++i) { + String & arg = args[i]; + if (arg[0] == '-') { // We have a flag! + if (arg.size() > 1 && arg[1] == '-') i += ProcessArg(arg, i); + else i += ProcessFlagSet(arg, i); + } + else extras.push_back(arg); + } + } + + void PrintOptions(std::ostream & os=std::cout) const { + for (const auto & [name, options] : flag_options) { + os << " " << name; + if (options.shortcut) { + os << " (or '-" << options.shortcut << "')"; + } + if (options.desc.size()) { + os << " : " << options.desc; + } + os << "\n"; + } + } + }; + +} + +#endif // #ifndef EMP_CONFIG_FLAGMANAGER_HPP_INCLUDE diff --git a/include/emp/config/SettingCombos.hpp b/include/emp/config/SettingCombos.hpp index 879c878946..d317b09882 100644 --- a/include/emp/config/SettingCombos.hpp +++ b/include/emp/config/SettingCombos.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file SettingCombos.hpp + * @file * @brief A tool for exploring all parameter combinations * @note Status: ALPHA */ @@ -12,6 +13,7 @@ #define EMP_CONFIG_SETTINGCOMBOS_HPP_INCLUDE #include +#include #include #include #include diff --git a/include/emp/config/SettingConfig.hpp b/include/emp/config/SettingConfig.hpp index 7b059c8a4a..f307c30ef0 100644 --- a/include/emp/config/SettingConfig.hpp +++ b/include/emp/config/SettingConfig.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file SettingConfig.hpp + * @file * @brief A tool for collecting settings, including from files and the command line. * @note Status: DEPRECATED! */ @@ -12,6 +13,7 @@ #define EMP_CONFIG_SETTINGCONFIG_HPP_INCLUDE #include +#include #include #include #include @@ -381,7 +383,7 @@ namespace emp { } /// Convert all of the current values into a comma-separated string. - std::string CurComboString(const std::string & separator=",", + std::string CurComboString(const std::string & separator=",", ///< Delimiter separating values in string bool use_labels=false, ///< Print name with each value? bool multi_only=false ///< Only print values that can change? ) const { diff --git a/include/emp/config/command_line.hpp b/include/emp/config/command_line.hpp index 1bc0c251e5..0ea7b1eb9b 100644 --- a/include/emp/config/command_line.hpp +++ b/include/emp/config/command_line.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file command_line.hpp + * @file * @brief This file contains tools for dealing with command-line arguments (argv and argc). * * Functions here include: @@ -34,6 +35,7 @@ #ifndef EMP_CONFIG_COMMAND_LINE_HPP_INCLUDE #define EMP_CONFIG_COMMAND_LINE_HPP_INCLUDE +#include #include #include "../base/vector.hpp" diff --git a/include/emp/config/config.hpp b/include/emp/config/config.hpp index 4f0fe3c93d..92b86c9dad 100644 --- a/include/emp/config/config.hpp +++ b/include/emp/config/config.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2019. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2019. - * - * @file config.hpp + * @file * @brief Maintains a set of configuration options. * * This file defines a master configuration option Config, whose values can be loaded @@ -42,6 +43,7 @@ #include #include #include +#include #include #include @@ -402,7 +404,9 @@ namespace emp { for (auto & x : type_manager_map) delete x.second; } + #ifndef DOXYGEN_SHOULD_SKIP_THIS friend class ConfigWebUI; + #endif /*DOXYGEN_SHOULD_SKIP_THIS*/ ConfigEntry * operator[](const std::string & name) { return var_map[name]; } auto begin() -> decltype(var_map.begin()) { return var_map.begin(); } diff --git a/include/emp/config/config_utils.hpp b/include/emp/config/config_utils.hpp index e6fb43fb7c..522ac4fc17 100644 --- a/include/emp/config/config_utils.hpp +++ b/include/emp/config/config_utils.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file config_utils.hpp + * @file * @brief Helper functions for working with emp::Config objects. * */ diff --git a/include/emp/config/config_web_interface.hpp b/include/emp/config/config_web_interface.hpp index 647539fcd1..1ea9f4d07f 100644 --- a/include/emp/config/config_web_interface.hpp +++ b/include/emp/config/config_web_interface.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file config_web_interface.hpp + * @file * @brief TODO. */ @@ -12,6 +13,7 @@ #include #include +#include #include "../datastructs/set_utils.hpp" #include "../tools/string_utils.hpp" @@ -31,11 +33,13 @@ namespace emp { std::set exclude; std::map group_divs; std::map input_map; + #ifndef DOXYGEN_SHOULD_SKIP_THIS std::function on_change_fun = [](const std::string & val){;}; std::function format_label_fun = [](std::string name){ emp::vector sliced = slice(name, '_'); return to_titlecase(join(sliced, " ")); }; + #endif // DOXYGEN_SHOULD_SKIP_THIS public: ConfigWebUI(Config & c, const std::string & div_name = "settings_div") : config(c), settings_div(div_name) {;} diff --git a/include/emp/control/Action.hpp b/include/emp/control/Action.hpp index 2004ef2037..25896e5591 100644 --- a/include/emp/control/Action.hpp +++ b/include/emp/control/Action.hpp @@ -1,20 +1,22 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file Action.hpp + * @file * @brief A mechanism to abstract functions from their underlying type and provide run-time names. * @note Status: Beta * * @todo Create an ActionDefaults class that can take fewer args than expected and fill in rest. - * @todo Allow for named arguments to facilite intepreted functions. + * @todo Allow for named arguments to facilitate interpreted functions. */ #ifndef EMP_CONTROL_ACTION_HPP_INCLUDE #define EMP_CONTROL_ACTION_HPP_INCLUDE #include +#include #include namespace emp { @@ -22,8 +24,7 @@ namespace emp { /// BaseActions abstract functions and allow for signals to be setup at runtime; they can be /// called with types specified in the call. /// - /// Actions can be a bit heavyweight, but can easily be converted to more lightweight - /// std:function objects. + /// Actions can be a bit heavyweight, but can easily be converted to std::function objects. class ActionBase { protected: @@ -59,11 +60,11 @@ namespace emp { size_t GetArgCount() const { return ARG_COUNT; } }; - /// The declaration for Action has any template types; the only definined specilizations require + /// The declaration for Action has any template types; the only defined specializations require /// a function type to be specified (with void and non-void return type variants.) template class Action; - /// This Action class specialization takes a function with a void return tyime and builds it off + /// This Action class specialization takes a function with a void return type and builds it off /// of the action base classes. template class Action : public ActionSize { @@ -95,7 +96,7 @@ namespace emp { }; - /// This Action class specialization takes a function with any non-void return tyime and builds it + /// This Action class specialization takes a function with any non-void return type and builds it /// off of the action base classes. template class Action : public ActionSize { diff --git a/include/emp/control/ActionManager.hpp b/include/emp/control/ActionManager.hpp index 32bf6df875..40c71b11f1 100644 --- a/include/emp/control/ActionManager.hpp +++ b/include/emp/control/ActionManager.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file ActionManager.hpp + * @file * @brief ActionManager collects sets of Actions to be looked up or manipulated later. * @note Status: Beta */ @@ -11,6 +12,7 @@ #ifndef EMP_CONTROL_ACTIONMANAGER_HPP_INCLUDE #define EMP_CONTROL_ACTIONMANAGER_HPP_INCLUDE +#include #include #include diff --git a/include/emp/control/Signal.hpp b/include/emp/control/Signal.hpp index d347df85e1..012c331893 100644 --- a/include/emp/control/Signal.hpp +++ b/include/emp/control/Signal.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file Signal.hpp + * @file * @brief Allow functions to be bundled (as Actions) and triggered enmasse. * @note Status: Beta * @@ -15,7 +16,9 @@ #define EMP_CONTROL_SIGNAL_HPP_INCLUDE +#include #include +#include #include #include "../datastructs/map_utils.hpp" @@ -75,12 +78,13 @@ namespace emp { operator bool() { return key_id > 0; } }; + #ifndef DOXYGEN_SHOULD_SKIP_THIS // Forward declarations. class SignalBase; // ...for pointers to signals. class SignalManager; // ...for setting up as friend. // Mechanisms for Signals to report to a manager. - #ifndef DOXYGEN_SHOULD_SKIP_THIS + namespace internal { struct SignalManager_Base { virtual void NotifyConstruct(SignalBase * sig_ptr) = 0; @@ -103,7 +107,7 @@ namespace emp { std::string name; ///< What is the unique name of this signal? uint32_t signal_id; ///< What is the unique ID of this signal? - uint32_t next_link_id; ///< What ID shouild the next link have? + uint32_t next_link_id; ///< What ID should the next link have? std::map link_key_map; ///< Map unique link keys to link index for actions. emp::vector managers; ///< What manager is handling this signal? man_t * prime_manager; ///< Which manager leads deletion? (nullptr for self) @@ -111,7 +115,7 @@ namespace emp { // Helper Functions SignalKey NextSignalKey() { return SignalKey(signal_id,++next_link_id); } - // SignalBase should only be constructable from derrived classes. + // SignalBase should only be constructable from derived classes. SignalBase(const std::string & n, internal::SignalManager_Base * manager=nullptr) : name(n), signal_id(0), next_link_id(0), link_key_map(), managers(), prime_manager(nullptr) { @@ -359,9 +363,10 @@ namespace emp { }; + #ifndef DOXYGEN_SHOULD_SKIP_THIS template inline void SignalBase::BaseTrigger(ARGS... args) { - // Make sure this base class is really of the correct derrived type (but do so in an + // Make sure this base class is really of the correct derived type (but do so in an // assert since triggers may be called frequently and should be fast!) emp_assert(dynamic_cast< Signal * >(this)); ((Signal *) this)->Trigger(args...); @@ -373,7 +378,7 @@ namespace emp { emp_assert(dynamic_cast< Signal * >(this)); return ((Signal *) this)->AddAction(in_fun); } - + #endif // DOXYGEN_SHOULD_SKIP_THIS } #endif // #ifndef EMP_CONTROL_SIGNAL_HPP_INCLUDE diff --git a/include/emp/control/SignalControl.hpp b/include/emp/control/SignalControl.hpp index 82bf0a7982..f7bd48a427 100644 --- a/include/emp/control/SignalControl.hpp +++ b/include/emp/control/SignalControl.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file SignalControl.hpp + * @file * @brief The SignalControl class manages all of the signals and actions, linking them together * upon request (by name, base class, or derived class). * @@ -17,6 +18,8 @@ #ifndef EMP_CONTROL_SIGNALCONTROL_HPP_INCLUDE #define EMP_CONTROL_SIGNALCONTROL_HPP_INCLUDE +#include +#include #include #include diff --git a/include/emp/control/SignalManager.hpp b/include/emp/control/SignalManager.hpp index 570c26bd18..6ed8a76881 100644 --- a/include/emp/control/SignalManager.hpp +++ b/include/emp/control/SignalManager.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file SignalManager.hpp + * @file * @brief This file defines the SignalManager class, which collects sets of Signals to be looked up * or manipulated later. */ @@ -11,6 +12,8 @@ #ifndef EMP_CONTROL_SIGNALMANAGER_HPP_INCLUDE #define EMP_CONTROL_SIGNALMANAGER_HPP_INCLUDE +#include +#include #include #include diff --git a/include/emp/data/AnnotatedType.hpp b/include/emp/data/AnnotatedType.hpp new file mode 100644 index 0000000000..073fd8152f --- /dev/null +++ b/include/emp/data/AnnotatedType.hpp @@ -0,0 +1,91 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ +/** + * @file + * @brief A base class to provide a DataMap and accessors to derived classes. + * @note Status: ALPHA + * + */ + +#ifndef EMP_DATA_ANNOTATEDTYPE_HPP_INCLUDE +#define EMP_DATA_ANNOTATEDTYPE_HPP_INCLUDE + +#include "../base/assert.hpp" +#include "../meta/TypeID.hpp" +#include "../tools/string_utils.hpp" + +#include "DataMap.hpp" + +namespace emp { + + /// A generic base class implementing the use of dynamic traits via DataMaps. + class AnnotatedType { + private: + emp::DataMap data_map; ///< Dynamic variables assigned to this class. + + public: + emp::DataMap & GetDataMap() { return data_map; } + const emp::DataMap & GetDataMap() const { return data_map; } + + void SetDataMap(emp::DataMap & in_dm) { data_map = in_dm; } + + emp::DataLayout & GetDataLayout() { return data_map.GetLayout(); } + const emp::DataLayout & GetDataLayout() const { return data_map.GetLayout(); } + + bool HasTraitID(size_t id) const { return data_map.HasID(id); } + bool HasTrait(const std::string & name) const { return data_map.HasName(name); } + template + bool TestTraitType(size_t id) const { return data_map.IsType(id); } + template + bool TestTraitType(const std::string & name) const { return data_map.IsType(name); } + + size_t GetTraitID(const std::string & name) const { return data_map.GetID(name); } + + template + auto & GetTrait(KEY_T && key) { + return data_map.Get(std::forward(key)); + } + + template + auto GetTrait(KEY_T && key, size_t count) { + return data_map.Get(std::forward(key), count); + } + + template + const auto & GetTrait(KEY_T && key) const { + return data_map.Get(std::forward(key)); + } + + template + auto GetTrait(KEY_T && key, size_t count) const { + return data_map.Get(std::forward(key), count); + } + + template + T & SetTrait(size_t id, const T & val) { return data_map.Set(id, val); } + + template + T & SetTrait(const std::string & name, const T & val) { return data_map.Set(name, val); } + + emp::TypeID GetTraitType(size_t id) const { return data_map.GetType(id); } + emp::TypeID GetTraitType(const std::string & name) const { return data_map.GetType(name); } + + double GetTraitAsDouble(size_t id) const { return data_map.GetAsDouble(id); } + + double GetTraitAsDouble(size_t trait_id, emp::TypeID type_id) const { + return data_map.GetAsDouble(trait_id, type_id); + } + + std::string GetTraitAsString(size_t id) const { return data_map.GetAsString(id); } + + std::string GetTraitAsString(size_t trait_id, emp::TypeID type_id, size_t count=1) const { + return data_map.GetAsString(trait_id, type_id, count); + } + }; + +} + +#endif // #ifndef EMP_DATA_ANNOTATEDTYPE_HPP_INCLUDE diff --git a/include/emp/data/DataFile.hpp b/include/emp/data/DataFile.hpp index b7ee56300a..6a1ecc307a 100644 --- a/include/emp/data/DataFile.hpp +++ b/include/emp/data/DataFile.hpp @@ -1,18 +1,21 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file DataFile.hpp + * @file * @brief DataFile objects use DataNode objects to dynamically fill out file contents. */ #ifndef EMP_DATA_DATAFILE_HPP_INCLUDE #define EMP_DATA_DATAFILE_HPP_INCLUDE +#include #include #include #include +#include #include #include "../base/assert.hpp" @@ -62,8 +65,11 @@ namespace emp { , line_begin(b), line_spacer(s), line_end(e) { ; } DataFile(std::ostream & in_os, const std::string & b="", const std::string & s=",", const std::string & e="\n") - : filename(), os(&in_os), funs(), pre_funs(), keys(), descs(), timing_fun([](size_t){return true;}) - , line_begin(b), line_spacer(s), line_end(e) { ; } + : filename(), os(&in_os), funs(), pre_funs(), keys(), descs(), timing_fun( + #ifndef DOXYGEN_SHOULD_SKIP_THIS + [](size_t){return true;} + #endif + ), line_begin(b), line_spacer(s), line_end(e) { ; } DataFile(const DataFile &) = default; DataFile(DataFile &&) = default; @@ -88,9 +94,10 @@ namespace emp { /// Setup this file to print only once, at the specified update. Note that this timing /// function can be replaced at any time, even after being triggered. + /// @param print_time The update the file should print at void SetTimingOnce(size_t print_time) { - timing_fun = [print_time](size_t update) { return update == print_time; }; - } + timing_fun = [print_time](size_t update) { return update == print_time; }; + } /// Setup this file to print every 'step' updates. void SetTimingRepeat(size_t step) { @@ -136,7 +143,7 @@ namespace emp { /// Print a header containing comments describing all of the columns virtual void PrintHeaderComment(const std::string & cstart = "# ") { for (size_t i = 0; i < keys.size(); i++) { - *os << cstart << i << ": " << descs[i] << " (" << keys[i] << ")" << std::endl; + *os << cstart << i << ": " << descs[i] << " (" << keys[i] << ")\n"; } os->flush(); } @@ -612,10 +619,10 @@ namespace emp { /// Print a header containing comments describing all of the columns void PrintHeaderComment(const std::string & cstart = "# ") override { for (size_t i = 0; i < keys.size(); i++) { - *os << cstart << i << ": " << descs[i] << " (" << keys[i] << ")" << std::endl; + *os << cstart << i << ": " << descs[i] << " (" << keys[i] << ")\n"; } for (size_t i = 0; i < container_keys.size(); i++) { - *os << cstart << i+keys.size() << ": " << container_descs[i] << " (" << container_keys[i] << ")" << std::endl; + *os << cstart << i+keys.size() << ": " << container_descs[i] << " (" << container_keys[i] << ")\n"; } os->flush(); diff --git a/include/emp/data/DataInterface.hpp b/include/emp/data/DataInterface.hpp index 14b42e0978..a7a6434cea 100644 --- a/include/emp/data/DataInterface.hpp +++ b/include/emp/data/DataInterface.hpp @@ -1,15 +1,17 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file DataInterface.hpp + * @file * @brief DataInterface is a *generic* interface to a DataNode. */ #ifndef EMP_DATA_DATAINTERFACE_HPP_INCLUDE #define EMP_DATA_DATAINTERFACE_HPP_INCLUDE +#include #include "DataNode.hpp" @@ -21,29 +23,64 @@ namespace emp { public: virtual ~DataInterface() { ; } + /// Returns the number values added to this node since the last reset. virtual size_t GetCount() const = 0; + + /// Returns the number of times this node has been reset. virtual size_t GetResetCount() const = 0; + /// Returns the sum of values added since the last reset. + /// Requires that the data::Range or data::FullRange be added to the DataNode virtual double GetTotal() const = 0; + /// Returns the mean of the values added since the last reset. + /// Requires that the data::Range or data::FullRange be added to the DataNode virtual double GetMean() const = 0; + /// Returns the minimum of the values added since the last reset. + /// Requires that the data::Range or data::FullRange be added to the DataNode virtual double GetMin() const = 0; + /// Returns the maximum of the values added since the last reset. + /// Requires that the data::Range or data::FullRange be added to the DataNode virtual double GetMax() const = 0; + /// Returns the variance of the values added since the last reset. + /// Requires that the data::Stats or data::FullStats be added to the DataNode virtual double GetVariance() const = 0; + /// Returns the standard deviation of the values added since the last reset. + /// Requires that the data::Stats or data::FullStats be added to the DataNode virtual double GetStandardDeviation() const = 0; + /// Returns the skewness of the values added since the last reset. + /// Requires that the data::Stats or data::FullStats be added to the DataNode virtual double GetSkew() const = 0; + /// Returns the kurtosis of the values added since the last reset. + /// Requires that the data::Stats or data::FullStats be added to the DataNode virtual double GetKurtosis() const = 0; + /// Runs the Pull function for this DataNode and records the resulting values. + /// Requires that the data::Pull module was added to this DataNode, and that a pull function + /// was specified. virtual void PullData() = 0; + /// Reset this node. The exact effects of this depend on the modules that this node has, + /// but in general it prepares the node to receive a new set of data. virtual void Reset() = 0; + /// Print debug information about this node to the provided stream + /// Useful for tracking which modifiers are included. + /// @param os The stream to print debug information to virtual void PrintDebug(std::ostream & os=std::cout) = 0; + /// Returns this node's name. Requires that the data::Info module was + /// added to this DataNode, and that a name was set. virtual void GetName() = 0; + /// Returns this node's description. Requires that the data::Info module was + /// added to this DataNode, and that a description was set. virtual void GetDescription() = 0; + /// Returns this node's keyword. Requires that the data::Info module was + /// added to this DataNode, and that a keyword was set. virtual void GetKeyword() = 0; }; + #ifndef DOXYGEN_SHOULD_SKIP_THIS + template class DataInterface_Impl : public DataInterface { public: @@ -128,6 +165,7 @@ namespace emp { }; + #endif /*DOXYGEN_SHOULD_SKIP_THIS*/ template DataInterface * MakeDataInterface() { diff --git a/include/emp/data/DataLayout.hpp b/include/emp/data/DataLayout.hpp index 6e0700167f..661a29c418 100644 --- a/include/emp/data/DataLayout.hpp +++ b/include/emp/data/DataLayout.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019-2022 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019. - * - * @file DataLayout.hpp + * @file * @brief A mapping of names to variables stored in a MemoryImage. * @note Status: ALPHA */ @@ -11,12 +12,14 @@ #ifndef EMP_DATA_DATALAYOUT_HPP_INCLUDE #define EMP_DATA_DATALAYOUT_HPP_INCLUDE +#include #include #include #include "../base/assert.hpp" #include "../base/vector.hpp" #include "../datastructs/map_utils.hpp" +#include "../math/constants.hpp" #include "../meta/TypeID.hpp" #include "MemoryImage.hpp" @@ -32,6 +35,7 @@ namespace emp { std::string name; ///< Name of this setting. std::string desc; ///< Full description of this setting. std::string notes; ///< Any additional notes about this setting. + size_t count; ///< Number of objects in this entry. bool is_log; ///< Is this setting a current value or a log of all values? }; @@ -74,13 +78,55 @@ namespace emp { /// Determine if we have an ID. bool HasID(size_t id) const { return emp::Has(setting_map, id); } - /// Detemine if we have the correct type of a specific variable ID. + /// Determine if we have the correct type of a specific variable ID. template bool IsType(size_t id) const { - emp_assert(Has(setting_map, id), id); + emp_assert(emp::Has(setting_map, id), id); return setting_map.find(id)->second.type == emp::GetTypeID(); } + // Verify type, position, AND count. + template + bool Has(size_t id, size_t count=1) const { + auto it = setting_map.find(id); + return it != setting_map.end() && + it->second.type == emp::GetTypeID() && + it->second.count == count; + } + + // Verify name, position, AND count. + template + bool Has(const std::string & name, size_t count=1) const { + auto it = id_map.find(name); + return (it != id_map.end()) && Has(it->second, count); + } + + template + std::string DiagnoseHas(KEY_T key, size_t count=1) const { + size_t id = 0; + if constexpr (std::is_arithmetic()) { + id = key; + } else { // key is name. + auto it = id_map.find(key); + if (it == id_map.end()) return emp::to_string("Unknown trait name '", key, "'"); + id = it->second; + } + + auto setting_it = setting_map.find(id); + if (setting_it == setting_map.end()) { + if (id == emp::MAX_SIZE_T) return emp::to_string("Unknown ID ", id, " (aka -1)"); + return emp::to_string("Unknown ID ", id); + } + if (setting_it->second.type != emp::GetTypeID()) { + return emp::to_string("Checking for type as ", emp::GetTypeID(), + ", but recorded as ", setting_it->second.type); + } + if (setting_it->second.count != count) { + return emp::to_string("Checking for count of ", count, + ", but recorded as ", setting_it->second.count); + } + return emp::to_string("Has<", emp::GetTypeID(), ">(", key, ",", count, ") should be true."); + } /// Return the number of bytes in the default image. size_t GetImageSize() const { return image_size; } @@ -97,31 +143,44 @@ namespace emp { return setting_map.find(id)->second.type; } + // What is the count associated with a given entry. + size_t GetCount(size_t id) const { + emp_assert(HasID(id), id); + return setting_map.find(id)->second.count; + } + + /// Determine is entry is some form of numeric type. + bool IsNumeric(size_t id) const { + return GetType(id).IsArithmetic(); + } + + bool IsNumeric(const std::string & name) const { + return IsNumeric(GetID(name)); + } + /// Prevent this layout from being modified. void Lock() { is_locked = true; } /// Add a new variable with a specified type, name and value. template - size_t Add(MemoryImage & base_memory, - const std::string & name, - const T & default_value, - const std::string & desc="", - const std::string & notes="") { - emp_assert(!HasName(name), name); // Make sure this doesn't already exist. - emp_assert(is_locked == false); // Cannot add to a locked layout. - - // std::cout << "\nL: Adding var '" << name - // << "' of type " << emp::GetTypeID() - // << " to DataMap with " << id_map.size() << " elements" - // << " totalling " << image_size << " bytes." - // << std::endl; - - // Analyze the size of the new object and where it will go. + size_t Add(MemoryImage & base_memory, // Memory to store prototype objects. + const std::string & name, // Lookup name for this variable. + const T & default_value, // Initial value for each object in this entry. + const std::string & desc="", // Description associated with this variable + const std::string & notes="", // Additional information. + const size_t count = 1 // Number of values to store with this entry. + ) { + emp_assert(!HasName(name), name); // Make sure this doesn't already exist. + emp_assert(count >= 1); // Must add at least one instance of an object. + emp_assert(is_locked == false); // Cannot add to a locked layout. + + // Analyze the size of the new object(s) and where it will go. constexpr const size_t obj_size = sizeof(T); + const size_t entry_size = obj_size * count; const size_t pos = image_size; // Create a new image with enough room for the new object and move the old data over. - MemoryImage new_memory(image_size + obj_size); + MemoryImage new_memory(image_size + entry_size); MoveImageContents(base_memory, new_memory); // Now that the data is moved, cleanup the old image and put the new one in place. @@ -129,18 +188,22 @@ namespace emp { // Setup this new object. image_size = base_memory.GetSize(); - base_memory.Construct(pos, default_value); + for (size_t i = 0; i < count; ++i) { + base_memory.Construct(pos + i*obj_size, default_value); + } base_memory.init_to = image_size; // Store the information about this object. id_map[name] = pos; - setting_map[pos] = { emp::GetTypeID(), name, desc, notes, false }; + setting_map[pos] = { emp::GetTypeID(), name, desc, notes, count, false }; // Store copy constructor if needed. if (std::is_trivially_copyable() == false) { copy_constructors.push_back( - [pos](const MemoryImage & from_image, MemoryImage & to_image) { - to_image.CopyObj(pos, from_image); + [pos,count](const MemoryImage & from_image, MemoryImage & to_image) { + for (size_t i = 0; i < count; ++i) { + to_image.CopyObj(pos + i*sizeof(T), from_image); + } } ); } @@ -148,15 +211,21 @@ namespace emp { // Store destructor if needed. if (std::is_trivially_destructible() == false) { destructors.push_back( - [pos](MemoryImage & image) { image.Destruct(pos); } + [pos,count](MemoryImage & image) { + for (size_t i = 0; i < count; ++i) { + image.Destruct(pos + i*sizeof(T)); + } + } ); } // Store move constructor if needed. if (std::is_trivially_destructible() == false) { move_constructors.push_back( - [pos](MemoryImage & from_image, MemoryImage & to_image) { - to_image.MoveObj(pos, from_image); + [pos,count](MemoryImage & from_image, MemoryImage & to_image) { + for (size_t i = 0; i < count; ++i) { + to_image.MoveObj(pos + i*sizeof(T), from_image); + } } ); } @@ -176,7 +245,7 @@ namespace emp { image.init_to = 0; } - /// Destruct and delete all memomry assocated with this DataMap. + /// Destruct and delete all memory associated in the provided image. void ClearImage(MemoryImage & image) const { // If this memory image is already clear, stop. if (image.GetSize() == 0) return; diff --git a/include/emp/data/DataLog.hpp b/include/emp/data/DataLog.hpp index 20022e530b..05e56f70af 100644 --- a/include/emp/data/DataLog.hpp +++ b/include/emp/data/DataLog.hpp @@ -1,12 +1,15 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file DataLog.hpp + * @file * @brief Tools for processing a single set of data. * @note Status: ALPHA * + * A DataLog takes in a continuous series of data and allows for easy analysis, both by + * performing calculations on those values and by outputting ascii histograms, etc. */ #ifndef EMP_DATA_DATALOG_HPP_INCLUDE @@ -14,6 +17,7 @@ #include #include +#include #include "../base/assert.hpp" #include "../base/vector.hpp" diff --git a/include/emp/data/DataManager.hpp b/include/emp/data/DataManager.hpp index 58f1febb98..fa0b2bc03c 100644 --- a/include/emp/data/DataManager.hpp +++ b/include/emp/data/DataManager.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2018 - * - * @file DataManager.hpp + * @file * @brief DataManager handles a set of DataNode objects with the same tracking settings. */ @@ -11,6 +12,7 @@ #define EMP_DATA_DATAMANAGER_HPP_INCLUDE #include +#include #include #include "../base/assert.hpp" @@ -36,22 +38,12 @@ namespace emp { } // so we can use range-based for loops - auto begin() -> decltype(std::begin(node_map)) { - return std::begin(node_map); - } - // so we can use range-based for loops - auto end() -> decltype(std::end(node_map)) { - return std::end(node_map); - } + auto begin() { return std::begin(node_map); } + auto end() { return std::end(node_map); } // so we can use range-based for loops with const - auto begin() const -> const decltype(std::begin(node_map)) { - return std::begin(node_map); - } - // so we can use range-based for loops with const - auto end() const -> const decltype(std::end(node_map)) { - return std::end(node_map); - } + auto begin() const { return std::begin(node_map); } + auto end() const { return std::end(node_map); } /// @returns the number of DataNodes in this DataManager size_t GetSize() const { return node_map.size(); } @@ -87,10 +79,10 @@ namespace emp { } /// @returns a reference to the node with the specified name - /// Throws an error if there is no node with that name in this manager /// @param name the name of the DataNode node_t & Get(const std::string & name) { - emp_assert(Has(node_map, name), name, emp::to_string(Keys(node_map))); + emp_assert(Has(node_map, name), name); + emp_assert(node_map[name] != nullptr); return *(node_map[name]); } diff --git a/include/emp/data/DataMap.hpp b/include/emp/data/DataMap.hpp index a7d7deb435..baa41ae205 100644 --- a/include/emp/data/DataMap.hpp +++ b/include/emp/data/DataMap.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018-2022. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018-2021. - * - * @file DataMap.hpp + * @file * @brief A DataMap links names to arbitrary object types. * @note Status: ALPHA * @@ -46,17 +47,17 @@ * to be stored elsewhere (presumably in the memory image, but possibly in the layout.) * 5. The memory is a LOG of values, not a single value. This allows for quick identification * of when something special needs to be done. - * 6-8. Limited type information (8 types that can be handled more effectively?) + * 6-8. Limited type information (7 types that can be handled more effectively?) * * - We should be able to keep a series of values, not just a single one. This can be done with * a series of new functions: - * AddLog() instead of AddVar() when new veriable is created. - * Get() should still work for latest value. Ideally keep lates in first position. + * AddLog() instead of AddVar() when new variable is created. + * Get() should still work for latest value. Ideally keep latest in first position. * Change non-const Get() to GetRef() which cannot be used for a log. - * Add GetAve() function for logs as well as GetLog() for the full vector. + * Add GetAve() function for logs as well as GetLog() for the full series (as std::span?). * * - Settings for all entries should have more information on how they are dealt with, such as if - * they should be included in output an how. Perhaps a system of tags for dynamic use? + * they should be included in output and how. Perhaps a system of tags for dynamic use? * * - After everything else is working, build a LocalDataMap that locks in the size at * compile time, providing more localized memory. Otherwise DataMap as a whole can be built @@ -67,25 +68,30 @@ * * - A user should be able to override copy constructors (though probably not move constructors * or destructors?). Then the copy process can be more customizable, for example having some - * settings retrun to the default value or be further processed. It's also possible to have + * settings return to the default value or be further processed. It's also possible to have * multiple types of copies, so if we indicate a "Copy birth" we get the above, but if we * indicate a "Copy clone" or "Copy inject" we do something different. We also probably need * to allow for multiple parents... * * - An OptimizeLayout() function that can reorder entries so that they are somehow more sensible? + * Does DataMap need to worry about memory alignment? * - * - A MemoryImage factory to speed up allocation, deallocation if we're using the same size + * - A MemoryImage factory to speed up allocation and deallocation if we're using the same size * images repeatedly. * * - Some way of grouping memory across DataMaps so that a particular entry for many maps has all - * of its instances consecutive in memory? This seems really tricky to pull of, but if we can + * of its instances consecutive in memory? This seems really tricky to pull off, but if we can * do it, the improvement in cache performance could be dramatic. + * + * - Rename DataLayout and MemoryImage to DataMap_Layout and DataMap_Memory? */ #ifndef EMP_DATA_DATAMAP_HPP_INCLUDE #define EMP_DATA_DATAMAP_HPP_INCLUDE #include // For std::memcpy +#include +#include #include #include "../base/assert.hpp" @@ -94,6 +100,7 @@ #include "../tools/string_utils.hpp" #include "DataLayout.hpp" +#include "Datum.hpp" #include "MemoryImage.hpp" namespace emp { @@ -106,6 +113,8 @@ namespace emp { DataMap(emp::Ptr in_layout_ptr, size_t in_size) : memory(in_size), layout_ptr(in_layout_ptr) { ; } + // -- Helper functions -- + /// If the current layout is shared, make a copy of it. void MakeLayoutUnique() { // Make sure we have a layout, even if empty. @@ -117,6 +126,7 @@ namespace emp { layout_ptr.New(*layout_ptr); } } + public: DataMap() : layout_ptr(nullptr) { ; } DataMap(const DataMap & in_map) : layout_ptr(in_map.layout_ptr) { @@ -131,44 +141,20 @@ namespace emp { } // Copy Operator... - DataMap & operator=(const DataMap & in_map) { - // If we have a layout pointer, use it to clear our memory image and update it if needed. - if (layout_ptr) { - layout_ptr->ClearImage(memory); - - // If layout pointer doesn't match the new one, shift over. - if (layout_ptr != in_map.layout_ptr) { - layout_ptr->DecMaps(); // Remove self from counter. - if (layout_ptr->GetNumMaps() == 0) layout_ptr.Delete(); // Delete layout if now unused. - layout_ptr = in_map.layout_ptr; // Shift to new layout. - if (layout_ptr) layout_ptr->IncMaps(); // Add self to new counter. - } - } - - // Otherwise we DON'T have a layout pointer, so setup the new one. - else { - layout_ptr = in_map.layout_ptr; // Shift to new layout. - if (layout_ptr) layout_ptr->IncMaps(); // Add self to new counter. - } - - // Now that we know we have a good layout, copy over the image. - layout_ptr->CopyImage(in_map.memory, memory); - - return *this; - } + DataMap & operator=(const DataMap & in_map); ~DataMap() { /// If we have a layout pointer, clean up! if (!layout_ptr.IsNull()) { - // Clean up the current MemoryImage. - layout_ptr->ClearImage(memory); - - // Clean up the DataLayout - layout_ptr->DecMaps(); + layout_ptr->ClearImage(memory); // Clean up the current MemoryImage. + layout_ptr->DecMaps(); // Clean up the DataLayout if (layout_ptr->GetNumMaps() == 0) layout_ptr.Delete(); } } + // Built-in types. + using key_type = std::string; + /// Determine how many Bytes large this image is. size_t GetSize() const { return memory.GetSize(); } @@ -196,19 +182,26 @@ namespace emp { return layout_ptr->IsType(GetID(name)); } + /// Verify settings + template + bool Has(ARGS &&... args) const { + emp_assert(layout_ptr); + return layout_ptr->Has(std::forward(args)...); + } + /// Retrieve a variable by its type and position. template T & Get(size_t id) { - emp_assert(HasID(id), "Can only get IDs the are available in DataMap.", id, GetSize()); - emp_assert(IsType(id)); + emp_assert(Has(id), "Can only get IDs/types that match DataMap in type and count.", + id, GetSize(), layout_ptr->DiagnoseHas(id)); return memory.Get(id); } /// Retrieve a const variable by its type and position. template const T & Get(size_t id) const { - emp_assert(HasID(id), id, GetSize()); - emp_assert(IsType(id)); + emp_assert(Has(id), "Can only get IDs/types that match DataMap in type and count.", + id, GetSize(), layout_ptr->DiagnoseHas(id)); return memory.Get(id); } @@ -216,20 +209,51 @@ namespace emp { /// Retrieve a variable by its type and name. (Slower!) template T & Get(const std::string & name) { - emp_assert(HasName(name), name); - emp_assert(IsType(name), "DataMap::Get() must be provided the correct type.", - name, GetType(name), emp::GetTypeID()); + emp_assert(Has(name), "Can only get name/types that match DataMap in type and count.", + name, GetSize(), layout_ptr->DiagnoseHas(name)); return memory.Get(GetID(name)); } /// Retrieve a const variable by its type and name. (Slower!) template const T & Get(const std::string & name) const { - emp_assert(HasName(name)); - emp_assert(IsType(name), name, GetType(name), emp::GetTypeID()); + emp_assert(Has(name), "Can only get name/types that match DataMap in type and count.", + name, GetSize(), layout_ptr->DiagnoseHas(name)); return memory.Get(GetID(name)); } + // Retrieve a set of variables by id (as an std::span) + template + std::span Get(size_t id, size_t count) { + emp_assert(Has(id, count), "Can only get name/types that match DataMap.", + id, count, GetSize(), layout_ptr->DiagnoseHas(id,count)); + return memory.Get(id, count); + } + + // Retrieve a const set of variables by id (as an std::span) + template + std::span Get(size_t id, size_t count) const { + emp_assert(Has(id, count), "Can only get name/types that match DataMap.", + id, GetSize(), layout_ptr->DiagnoseHas(id,count)); + return memory.Get(id, count); + } + + // Retrieve a set of variables by name (as an std::span) + template + std::span Get(const std::string & name, size_t count) { + emp_assert(HasName(name), "Cannot get names not stored in DataMap.", + name, layout_ptr->DiagnoseHas(name, count)); + return Get(GetID(name), count); + } + + // Retrieve a const set of variables by name (as an std::span) + template + std::span Get(const std::string & name, size_t count) const { + emp_assert(HasName(name), "Cannot get names not stored in DataMap.", + name, layout_ptr->DiagnoseHas(name, count)); + return Get(GetID(name), count); + } + /// Set a variable by ID. template T & Set(size_t id, const T & value) { return (Get(id) = value); @@ -252,6 +276,9 @@ namespace emp { return layout_ptr->GetType(GetID(name)); } + bool IsNumeric(size_t id) const { return GetType(id).IsArithmetic(); } + bool IsNumeric(const std::string & name) const { return IsNumeric(GetID(name)); } + /// Get the memory at the target position, assume it is the provided type, and convert the /// value found there to double. double GetAsDouble(size_t id, TypeID type_id) const { @@ -268,16 +295,25 @@ namespace emp { /// Get the memory at the target position, assume it is the provided type, and convert the /// value found there to string. - std::string GetAsString(size_t id, TypeID type_id) const { + std::string GetAsString(size_t id, TypeID type_id, size_t count=1) const { emp_assert(HasID(id), "Can only Get IDs that are available in DataMap.", id, GetSize()); emp_assert(type_id == layout_ptr->GetType(id)); - return type_id.ToString(memory.GetPtr(id)); + emp_assert(count = layout_ptr->GetCount(id)); + if (count == 1) return type_id.ToString(memory.GetPtr(id)); + else { + size_t obj_size = type_id.GetSize(); + std::stringstream ss; + for (size_t i = 0; i < count; ++i) { + ss << '[' << type_id.ToString(memory.GetPtr(id+i*obj_size)) << ']'; + } + return ss.str(); + } } /// Get the memory at the target position, lookup it's type, and convert the value to string. std::string GetAsString(size_t id) const { emp_assert(HasID(id), "Can only get IDs the are available in DataMap.", id, GetSize()); - return GetAsString(id, layout_ptr->GetType(id)); + return GetAsString(id, layout_ptr->GetType(id), layout_ptr->GetCount(id)); } /// Add a new variable with a specified type, name and value. @@ -285,9 +321,17 @@ namespace emp { size_t AddVar(const std::string & name, const T & default_value, const std::string & desc="", - const std::string & notes="") { + const std::string & notes="", + size_t count=1) { + MakeLayoutUnique(); // If the current layout is shared, first make a copy of it. + return layout_ptr->Add(memory, name, default_value, desc, notes, count); + } + + /// Add a new variable with just a specified type and name; must be able to default. + template + size_t AddVar(const std::string & name) { MakeLayoutUnique(); // If the current layout is shared, first make a copy of it. - return layout_ptr->Add(memory, name, default_value, desc, notes); + return layout_ptr->Add(memory, name, T{}, "", "", 1); } /// Test if this DataMap uses the specified layout. @@ -295,6 +339,9 @@ namespace emp { return layout_ptr == &in_layout; } + /// Test if this DataMap has ANY layout. + bool HasLayout() const { return layout_ptr; } + /// Test if this DataMap is using the identical layout as another DataMap. bool SameLayout(const emp::DataMap & in_dm) const { return layout_ptr == in_dm.layout_ptr; @@ -302,7 +349,10 @@ namespace emp { } /// Get the DataLayout so that it can be used elsewhere. - const emp::DataLayout & GetLayout() { return *layout_ptr; } + emp::DataLayout & GetLayout() { return *layout_ptr; } + + /// Get the DataLayout so that it can be used elsewhere. + const emp::DataLayout & GetLayout() const { return *layout_ptr; } /// Test if this layout is locked (i.e., it cannot be changed.) bool IsLocked() const { return layout_ptr && layout_ptr->IsLocked(); } @@ -312,8 +362,83 @@ namespace emp { MakeLayoutUnique(); layout_ptr->Lock(); } + + + ///////////////////////////////////////////////////////////////// + // Tools for working with DataMaps.... + + + /// Return a function that takes in a data map and (efficiently) returns a Datum using the + /// specified entry. + static std::function + MakeDatumAccessor(const emp::DataLayout & layout, size_t id) { + // This must be a DataLayout entry name. + emp_assert(layout.HasID(id), "DatumAccessor pointing to invalid id", id); + emp_assert(layout.GetCount(id) == 1, + "DatumAccessors must have a count of 1 for proper conversion.", + layout.GetCount(id)); + TypeID type_id = layout.GetType(id); + + // Return an appropriate accessor for this value. + if (type_id.IsType()) { // Explicit STRING + return [id](const emp::DataMap & dm){ + return emp::Datum(dm.Get(id)); + }; + } + else if (type_id.IsType()) { // Explicit DOUBLE + return [id](const emp::DataMap & dm){ + return emp::Datum(dm.Get(id)); + }; + } + else if (type_id.IsArithmetic()) { // Other NUMERIC type + return [id,type_id](const emp::DataMap & dm){ + return emp::Datum(type_id.ToDouble(dm.memory.GetPtr(id))); + }; + } + else { // Resort to STRING + return [id,type_id](const emp::DataMap & dm){ + return emp::Datum(type_id.ToString(dm.memory.GetPtr(id))); + }; + } + } + + /// Return a function that takes in a data map and (efficiently) returns a Datum using the + /// specified name. + static auto MakeDatumAccessor(const emp::DataLayout & layout, const std::string & name) { + emp_assert(layout.HasName(name), "DatumAccessor not pointing to valid name", name); + return MakeDatumAccessor(layout, layout.GetID(name)); + } }; + + // Copy Operator... + DataMap & DataMap::operator=(const DataMap & in_map) { + // If we have a layout pointer, use it to clear our memory image and update it if needed. + if (layout_ptr) { + layout_ptr->ClearImage(memory); + + // If layout pointer doesn't match the new one, shift over. + if (layout_ptr != in_map.layout_ptr) { + layout_ptr->DecMaps(); // Remove self from counter. + if (layout_ptr->GetNumMaps() == 0) layout_ptr.Delete(); // Delete layout if now unused. + layout_ptr = in_map.layout_ptr; // Shift to new layout. + if (layout_ptr) layout_ptr->IncMaps(); // Add self to new counter. + } + } + + // Otherwise we DON'T have a layout pointer, so setup the new one. + else { + layout_ptr = in_map.layout_ptr; // Shift to new layout. + if (layout_ptr) layout_ptr->IncMaps(); // Add self to new counter. + } + + // Now that we know we have a good layout, copy over the image. + layout_ptr->CopyImage(in_map.memory, memory); + + return *this; + } + + } #endif // #ifndef EMP_DATA_DATAMAP_HPP_INCLUDE diff --git a/include/emp/data/DataNode.hpp b/include/emp/data/DataNode.hpp index 031db1ab00..7fb9bf1413 100644 --- a/include/emp/data/DataNode.hpp +++ b/include/emp/data/DataNode.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file DataNode.hpp + * @file * @brief DataNode objects track a specific type of data over the course of a run. * * Collection: New data can be pushed or pulled. @@ -26,6 +27,7 @@ #include #include +#include #include "../base/assert.hpp" #include "../base/vector.hpp" @@ -65,6 +67,7 @@ namespace emp { }; + #ifndef DOXYGEN_SHOULD_SKIP_THIS /// A shortcut for converting DataNode mod ID's to ValPacks. template using ModPack = emp::ValPack<(int) MODS...>; @@ -86,13 +89,14 @@ namespace emp { using type = typename next_type::template append; }; - /// Generic form of DataNodeModule (should never be used; trigger error!) template class DataNodeModule { public: DataNodeModule() { emp_assert(false, "Unknown module used in DataNode!"); } }; + #endif /*DOXYGEN_SHOULD_SKIP_THIS*/ + /// Base form of DataNodeModule (available in ALL data nodes.) template class DataNodeModule { @@ -126,7 +130,10 @@ namespace emp { /// Calculate the median of observed values double GetMedian() const {emp_assert(false, "Calculating median requires a DataNode with the Log modifier"); return 0;} /// Calculate a percentile of observed values - double GetPercentile(const double pct) const {emp_assert(false, "Calculating percentile requires a DataNode with the Log modifier"); return 0;} + double GetPercentile(const double /*pct*/) const { + emp_assert(false, "Calculating percentile requires a DataNode with the Log modifier"); + return 0; + } const std::string & GetName() const { return emp::empty_string(); } const std::string & GetDescription() const { return emp::empty_string(); } @@ -141,7 +148,7 @@ namespace emp { emp_assert(false, "Invalid call for DataNode config."); } - void AddDatum(const VAL_TYPE & val) { val_count++; } + void AddDatum(const VAL_TYPE & /*val*/) { val_count++; } void Reset() { val_count = 0; } @@ -155,8 +162,15 @@ namespace emp { /// == data::Current == /// This module lets you track the current (i.e. most recently added) value + #ifndef DOXYGEN_SHOULD_SKIP_THIS template class DataNodeModule : public DataNodeModule { + #else + /// To use this class, add data::Current to the template arguments on your DataNode. Do not use the + /// CurrentModule class directly - it is a simplification for documentation purposes and does not + /// actually exist. + class CurrentModule { + #endif // DOXYGEN_SHOULD_SKIP_THIS protected: VAL_TYPE cur_val; ///< Most recent value passed to this node. @@ -183,8 +197,15 @@ namespace emp { /// == data::Info == /// This module adds information such as a name, description, and keyword for this node. + #ifndef DOXYGEN_SHOULD_SKIP_THIS template class DataNodeModule : public DataNodeModule { + #else + /// To use this class, add data::Info to the template arguments on your DataNode. Do not use the + /// InfoModule class directly - it is a simplification for documentation purposes and does not + /// actually exist. + class InfoModule { + #endif // DOXYGEN_SHOULD_SKIP_THIS protected: std::string name; ///< Name of this data category. std::string desc; ///< Description of this type of data. @@ -226,8 +247,15 @@ namespace emp { /// == data::Log == /// This module lets you log all of the values that have been added since the last re-set + #ifndef DOXYGEN_SHOULD_SKIP_THIS template class DataNodeModule : public DataNodeModule { + #else + /// To use this class, add data::Log to the template arguments on your DataNode. Do not use the + /// LogModule class directly - it is a simplification for documentation purposes and does not + /// actually exist. + class LogModule { + #endif // DOXYGEN_SHOULD_SKIP_THIS protected: emp::vector val_set; ///< All values saved since last reset. @@ -291,8 +319,15 @@ namespace emp { /// This module keeps track of historical values in addition to those added since the last re-set. /// Every time Reset() is called, all values that have been added since the previous time Reset() /// are stored in a vector in the archive. + #ifndef DOXYGEN_SHOULD_SKIP_THIS template class DataNodeModule : public DataNodeModule { + #else + /// To use this class, add data::Archive to the template arguments on your DataNode. Do not use the + /// ArchiveModule class directly - it is a simplification for documentation purposes and does not + /// actually exist. + class ArchiveModule { + #endif // DOXYGEN_SHOULD_SKIP_THIS protected: emp::vector> archive; ///< Data archived from before most recent reset. @@ -338,8 +373,15 @@ namespace emp { /// == data::Range == /// This module allows this DataNode to store information (min, max, mean, count, and total) about /// the distribution of the values that have been added since the last call to Reset(). + #ifndef DOXYGEN_SHOULD_SKIP_THIS template class DataNodeModule : public DataNodeModule { + #else + /// To use this class, add data::Range to the template arguments on your DataNode. Do not use the + /// RangeModule class directly - it is a simplification for documentation purposes and does not + /// actually exist. + class RangeModule { + #endif // DOXYGEN_SHOULD_SKIP_THIS protected: double total; ///< Total of all data since last reset. double min; ///< Smallest value passed in since last reset. @@ -392,8 +434,15 @@ namespace emp { /// This module makes the DataNode store a history of distributional information measured by /// data::Range between calls to Reset(). Series of historical values are stored in vectors /// (except mean, which is calculated from total and count). + #ifndef DOXYGEN_SHOULD_SKIP_THIS template class DataNodeModule : public DataNodeModule { + #else + /// To use this class, add data::Histogram to the template arguments on your DataNode. Do not use the + /// FullRangeModule class directly - it is a simplification for documentation purposes and does not + /// actually exist. + class FullRangeModule { + #endif // DOXYGEN_SHOULD_SKIP_THIS protected: emp::vector total_vals; ///< Totals from previous resets. emp::vector num_vals; ///< Value counts from previous resets. @@ -464,9 +513,15 @@ namespace emp { /// /// Note 2: Kurtosis is calculated using Snedecor and Cochran (1967)'s formula. A perfect normal /// distribution has a kurtosis of 0. - + #ifndef DOXYGEN_SHOULD_SKIP_THIS template class DataNodeModule : public DataNodeModule { + #else + /// To use this class, add data::Stats to the template arguments on your DataNode. Do not use the + /// StatsModule class directly - it is a simplification for documentation purposes and does not + /// actually exist. + class StatsModule { + #endif // DOXYGEN_SHOULD_SKIP_THIS protected: // Running variance, skew, and kurtosis calculations based off of this class: // https://www.johndcook.com/blog/skewness_kurtosis/ @@ -543,8 +598,16 @@ namespace emp { /// == data::Histogram == /// Make the DataNode track a histogram of values observed since the last reset. + #ifndef DOXYGEN_SHOULD_SKIP_THIS template class DataNodeModule : public DataNodeModule { + #else + /// To use this class, add data::Histogram to the template arguments on your DataNode. Do not use the + /// HistogramModule class directly - it is a simplification for documentation purposes and does not + /// actually exist. + class HistogramModule { + #endif // DOXYGEN_SHOULD_SKIP_THIS + protected: VAL_TYPE offset; ///< Min value in first bin; others are offset by this much. VAL_TYPE width; ///< How wide is the overall histogram? @@ -583,7 +646,7 @@ namespace emp { /// upper bound on the histogram int GetOverflow() const {return overflow;} - /// Return the count of numbers added to this histogram that were belowed the + /// Return the count of numbers added to this histogram that were below the /// allowed lower bound int GetUnderflow() const {return underflow;} @@ -628,7 +691,7 @@ namespace emp { parent_t::AddDatum(val); } - /// Reset the DataNode (empties the historgram) + /// Reset the DataNode (empties the histogram) void Reset() { for (size_t & x : counts) x = 0.0; parent_t::Reset(); @@ -647,9 +710,17 @@ namespace emp { /// new values or sets of values that it will then track. These functions are called every time /// the PullData method is called on this node, and the values they return are measured as /// specified by the other modules in this node. + #ifndef DOXYGEN_SHOULD_SKIP_THIS template class DataNodeModule : public DataNodeModule { + #else + /// To use this class, add data::Pull to the template arguments on your DataNode. Do not use the + /// PullModule class directly - it is a simplification for documentation purposes and does not + /// actually exist. + class PullModule { + #endif // DOXYGEN_SHOULD_SKIP_THIS protected: + #ifndef DOXYGEN_SHOULD_SKIP_THIS emp::FunctionSet pull_funs; ///< Functions to pull data. emp::FunctionSet()> pull_set_funs; ///< Functions to pull sets of data. @@ -666,6 +737,7 @@ namespace emp { in_vals.insert(in_vals.end(), x.begin(), x.end()); } } + #endif /*DOXYGEN_SHOULD_SKIP_THIS*/ public: DataNodeModule() : pull_funs(), pull_set_funs() { ; } @@ -679,6 +751,8 @@ namespace emp { } }; + #ifndef DOXYGEN_SHOULD_SKIP_THIS + template class DataNode_Interface; /// Outermost interface to all DataNode modules. @@ -688,7 +762,7 @@ namespace emp { using parent_t = DataNodeModule; }; - /// A template that will determing requisites, sort, make unique the data mods provided. + /// A template that will determine requisites, sort, make unique the data mods provided. /// The final, sorted ValPack of the requisites plus originals is in 'sorted'. template struct FormatDataMods { @@ -696,6 +770,7 @@ namespace emp { using full = typename ModPack::template append; ///< Requisites + originals using sorted = pack::RUsort; ///< Unique and in order }; + #endif /*DOXYGEN_SHOULD_SKIP_THIS*/ template class DataNode : public DataNode_Interface< VAL_TYPE, typename FormatDataMods::sorted > { diff --git a/include/emp/data/Datum.hpp b/include/emp/data/Datum.hpp new file mode 100644 index 0000000000..6abf9ce04c --- /dev/null +++ b/include/emp/data/Datum.hpp @@ -0,0 +1,201 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021-2023 +*/ +/** + * @file + * @brief A single piece of data, either a value or a string. + * @note Status: ALPHA + * + * DEVELOPER NOTES: + * - For now, using unions, but this creates complications with non-trivial constructor/destructor + * for strings, so could try shifting over. + */ + +#ifndef EMP_DATA_DATUM_HPP_INCLUDE +#define EMP_DATA_DATUM_HPP_INCLUDE + +#include + +#include "../base/assert.hpp" +#include "../base/notify.hpp" +#include "../math/math.hpp" + +namespace emp { + + class Datum { + private: + union { + double num; + std::string str; + }; + bool is_num = true; + + void InitString() { new (&str) std::string; is_num = false; } + template + void InitString(T && in) { new (&str) std::string(std::forward(in)); is_num = false; } + void FreeString() { if (!is_num) str.~basic_string(); } + public: + Datum() : num(0.0), is_num(true) { } + Datum(double in) : num(in), is_num(true) { } + Datum(const std::string & in) { InitString(in); } + Datum(std::string && in) { InitString(in); } + Datum(const char * in) { InitString(in); } + Datum(const Datum & in) { + if (in.is_num) num = in.num; + else InitString(in.str); + } + Datum(Datum && in) { + if (in.is_num) num = in.num; + else InitString(std::move(in.str)); + } + ~Datum() { FreeString(); } + + bool IsDouble() const { return is_num; } ///< Is this natively stored as a double? + bool IsString() const { return !is_num; } ///< Is this natively stored as a string? + + /// If we know Datum is a Double, we can request its native form. + double & NativeDouble() { emp_assert(is_num); return num; } + double NativeDouble() const { emp_assert(is_num); return num; } + + /// If we know Datum is a String, we can request its native form. + std::string & NativeString() { emp_assert(!is_num); return str; } + const std::string & NativeString() const { emp_assert(!is_num); return str; } + + double AsDouble() const { + if (is_num) return num; + // Make sure we have a value here; otherwise provide a warning and return 0.0. + if (str.size() > 0 && + (std::isdigit(str[0]) || (str[0] == '-' && str.size() > 1 && std::isdigit(str[1])))) { + return std::stod(str); + } + + // Otherwise this string is invalid. + emp::notify::Warning("Cannot convert string '", str, "' to double."); + return 0.0; + } + + std::string AsString() const { + if (!is_num) return str; + std::stringstream ss; + ss << num; + return ss.str(); + //return std::to_string(num); + } + + operator double() const { return AsDouble(); } + operator std::string() const { return AsString(); } + + Datum & SetDouble(double in) { // If this were previously a string, clean it up! + FreeString(); // If there was previously a string, make sure to free it. + is_num = true; + num = in; + return *this; + } + + Datum & SetString(const std::string & in) { + if (is_num) InitString(in); // Convert to string. + else str = in; // Already a string. + return *this; + } + + Datum & Set(const Datum & in) { + if (in.is_num) return SetDouble(in.num); + else return SetString(in.str); + } + + Datum & operator=(double in) { return SetDouble(in); } + Datum & operator=(const std::string & in) { return SetString(in); } + Datum & operator=(const char * in) { return SetString(in); } + Datum & operator=(const Datum & in) { return Set(in); } + + // Unary operators + Datum operator+() const { return AsDouble(); } + Datum operator-() const { return -AsDouble(); } + Datum operator!() const { return AsDouble() == 0.0; } + + // Comparison operators + int CompareNumber(double rhs) const { + const double val = AsDouble(); + return (val == rhs) ? 0 : ((val < rhs) ? -1 : 1); + } + + int CompareString(const std::string & rhs) const { + if (is_num) { + const std::string val = std::to_string(num); + return (val == rhs) ? 0 : ((val < rhs) ? -1 : 1); + } + return (str == rhs) ? 0 : ((str < rhs) ? -1 : 1); + } + + int Compare(double rhs) const { return CompareNumber(rhs); } + int Compare(const std::string & rhs) const { return CompareString(rhs); } + int Compare(const char * rhs) const { return CompareString(rhs); } + int Compare(const Datum & rhs) const { return (rhs.is_num) ? CompareNumber(rhs) : CompareString(rhs); } + + template bool operator==(const T & rhs) const { return Compare(rhs) == 0; } + template bool operator!=(const T & rhs) const { return Compare(rhs) != 0; } + template bool operator< (const T & rhs) const { return Compare(rhs) == -1; } + template bool operator>=(const T & rhs) const { return Compare(rhs) != -1; } + template bool operator> (const T & rhs) const { return Compare(rhs) == 1; } + template bool operator<=(const T & rhs) const { return Compare(rhs) != 1; } + + // Binary Operators + + Datum operator+(double in) const { + if (IsDouble()) return NativeDouble() + in; + return NativeString() + std::to_string(in); + } + Datum operator*(double in) const { + if (IsDouble()) return NativeDouble() * in; + std::string out_string; + const size_t count = static_cast(in); + out_string.reserve(NativeString().size() * count); + for (size_t i = 0; i < count; ++i) out_string += NativeString(); + return out_string; + } + Datum operator-(double in) const { return AsDouble() - in; } + Datum operator/(double in) const { return AsDouble() / in; } + Datum operator%(double in) const { return emp::Mod(AsDouble(), in); } + + Datum operator+(const Datum & in) const { + if (IsDouble()) return NativeDouble() + in.AsDouble(); + return NativeString() + in.AsString(); + } + Datum operator*(const Datum & in) const { + if (IsDouble()) return NativeDouble() * in.AsDouble(); + std::string out_string; + size_t count = static_cast(in.AsDouble()); + out_string.reserve(NativeString().size() * count); + for (size_t i = 0; i < count; i++) out_string += NativeString(); + return out_string; + } + Datum operator-(const Datum & in) const { return AsDouble() - in.AsDouble(); } + Datum operator/(const Datum & in) const { return AsDouble() / in.AsDouble(); } + Datum operator%(const Datum & in) const { return emp::Mod(AsDouble(), in.AsDouble()); } + + template + Datum operator+=(T && in) { return *this = operator+(std::forward(in)); } + template + Datum operator-=(T && in) { return *this = operator-(std::forward(in)); } + template + Datum operator*=(T && in) { return *this = operator*(std::forward(in)); } + template + Datum operator/=(T && in) { return *this = operator/(std::forward(in)); } + template + Datum operator%=(T && in) { return *this = operator%(std::forward(in)); } + }; + + std::ostream & operator<<(std::ostream & out, const emp::Datum & d) { + out << d.AsString(); + return out; + } + +} + +emp::Datum operator%(double value1, emp::Datum value2) { + return emp::Mod(value1, value2.AsDouble()); +} + +#endif // #ifndef EMP_DATA_DATUM_HPP_INCLUDE diff --git a/include/emp/data/MemoryImage.hpp b/include/emp/data/MemoryImage.hpp index 1bc0078afa..044a3f7c0c 100644 --- a/include/emp/data/MemoryImage.hpp +++ b/include/emp/data/MemoryImage.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019-2022 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file MemoryImage.hpp + * @file * @brief A managed set of Bytes to store any kind of data. * @note Status: ALPHA * @@ -14,6 +15,8 @@ #include // For std::memcpy #include // For placement new +#include +#include #include "../base/assert.hpp" #include "../base/Ptr.hpp" @@ -27,7 +30,7 @@ namespace emp { private: emp::Ptr image = nullptr; ///< Current memory image. size_t size = 0; ///< Size of current image. - size_t init_to = 0; ///< How far if the current image initialized? + size_t init_to = 0; ///< How far of the current image is initialized? // Setup all of the uninitialized memory to be non-zero. void Fuzz() { @@ -76,6 +79,16 @@ namespace emp { return *GetPtr(pos); } + /// Get proper spans to sets of same-type objects represented in this image. + template std::span Get(size_t pos, size_t count) { + emp_assert(pos < GetInitSize(), "Only get a span from initialized memory."); + return std::span( GetPtr(pos).Raw(), count ); + } + template std::span Get(size_t pos, size_t count) const { + emp_assert(pos < GetInitSize(), "Only get a span from initialized memory."); + return std::span( GetPtr(pos).Raw(), count ); + } + /// Change the size of this memory. Assume all cleanup and setup is done elsewhere. void RawResize(size_t new_size) { // If the size is already good, stop here. @@ -91,7 +104,7 @@ namespace emp { } /// Copy all of the bytes directly from another memory image. Size manipulation must be - /// done beforehand to ensure sufficient space is availabe. + /// done beforehand to ensure sufficient space is available. void RawCopy(const MemoryImage & from_memory) { emp_assert(GetSize() >= from_memory.GetSize()); if (from_memory.GetSize() == 0) return; // Nothing to copy! diff --git a/include/emp/data/README.md b/include/emp/data/README.md new file mode 100644 index 0000000000..b851d57b7c --- /dev/null +++ b/include/emp/data/README.md @@ -0,0 +1,62 @@ +# Data Management Tools + +This directory contains a set of tools for managing more or less genetic data. + + +## Individual pieces of data + +* Datum.hpp - emp::Datum holds a single value, which can be a string or a float. It shifts + between these types as needed. + + +## Tracking series of Data + +* DataNode.hpp - Manage a stream of data of a specific type; can specify at compile time how + data should be handled (tracking averages, modes, entropy, etc.) Can also be made to + pull data when needed. + +* DataInterface.hpp - A generic interface to DataNodes to make the easy to manage collectively. + +* DataManager.hpp - Manages a collection of DataNodes that all have the same settings. + +* DataFile.hpp - A collection of DataNodes that automatically output desired information to + an output file. + +* DataLog.hpp - Manage a series of data, tracking calculations and printing histograms. + + +## Tracking arbitrary named data + +* DataMap.hpp - Links variable names to arbitrary type values, these are stored in a single + memory block for locality and easy group copying. + +* DataLayout.hpp - Keeps track of information associated with each variable in a data map, + including location, type, description, etc. + +* MemoryImage.hpp - Block of memory managed using a given DataLayout. + +* AnnotatedType.hpp - Base class for objects that have a linked DataMap. + +* VarMap.hpp - Similar to DataMap, but types are stored with variables and not consecutive + in memory. + +* DataMapParser.hpp - A parser to take an equation based on variables in a DataLayout that + will produce a lambda. If a DataMap is passed into the lambda the equation will be + calculated and the result returned. + +* Trait.hpp - ? + + +## To add? + +DataFrame - rows are entries, columns are types, stored by column for fast calculation. +DataColumn = vector from DataFrame with type information. +DataRow - Same interface as DataMap; refers to associated DataFrame. + +DataTracker - Handles all of the functionality of DataNode, DataLog, etc., but more dynamic + using lambdas to deal with values as needed. + + +## To modify? + +Datum - should be able to do uint64_t? diff --git a/include/emp/data/SimpleParser.hpp b/include/emp/data/SimpleParser.hpp new file mode 100644 index 0000000000..fa8d86aafa --- /dev/null +++ b/include/emp/data/SimpleParser.hpp @@ -0,0 +1,627 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021-2022 +*/ +/** + * @file + * @brief Parser to convert function descriptions to lambdas using maps for variable lookup. + * @note Status: ALPHA + * + * A fully functional parser that will convert a string-description of a function to a C++ + * lambda. A map-typed object should be passed in to provide values associated with variables. + * Allowed map types include std::map, std::unordered_map, + * emp::DataMap, and (soon) derivations from emp::AnnotatedType. For standard maps, T must be + * convertable to emp::Datum. + * + * Developer TODO: + * - Setup operator RegEx to be built dynamically + * - Setup LVALUES as a type, and allow assignment + * - Allow types other than Datum (string and double)? + */ + +#ifndef EMP_DATA_SIMPLEPARSER_HPP_INCLUDE +#define EMP_DATA_SIMPLEPARSER_HPP_INCLUDE + +#include +#include +#include +#include + +#include "../base/notify.hpp" +#include "../compiler/Lexer.hpp" +#include "../compiler/regex_utils.hpp" +#include "../data/Datum.hpp" +#include "../datastructs/ra_map.hpp" +#include "../math/Random.hpp" +#include "../meta/meta.hpp" + +#include "AnnotatedType.hpp" +#include "DataMap.hpp" + +namespace emp { + + class SimpleParser { + private: + + template + struct ValueType { + using fun_t = std::function; + enum type_t { ERROR=0, VALUE, FUNCTION }; + + type_t type; + emp::Datum value; + fun_t fun; + + ValueType() : type(ERROR) {} + ValueType(const ValueType &) = default; + ValueType(double in_val) : type(VALUE), value(in_val) { } + ValueType(std::string in_val) : type(VALUE), value(in_val) { } + ValueType(emp::Datum in_val) : type(VALUE), value(in_val) { } + ValueType(fun_t in_fun) : type(FUNCTION), fun(in_fun) { } + + ValueType & operator=(const ValueType &) = default; + ValueType & operator=(emp::Datum in_val) { type = VALUE; value = in_val; return *this; } + ValueType & operator=(double in_val) { type = VALUE; value = in_val; return *this; } + ValueType & operator=(const std::string & in_val) { type = VALUE; value = in_val; return *this; } + ValueType & operator=(fun_t in_fun) { type = FUNCTION; fun = in_fun; return *this; } + + fun_t AsFunction() { + if (type==FUNCTION) return fun; + else return [v=value](ARG_T){ return v; }; + } + }; + + template + struct SymbolTable { + using arg_t = const MAP_T &; + using fun_t = std::function; + using value_t = ValueType; + + SymbolTable() { } + SymbolTable(arg_t) { } + + static_assert( std::is_same(), + "Any map type used by the parser must have a key type of std::string"); + + static fun_t MakeDatumAccessor(const std::string & name) { + return [name](arg_t symbol_vals){ + auto val_it = symbol_vals.find(name); + emp_assert(val_it != symbol_vals.end()); + return emp::Datum(val_it->second); + }; + } + + /// By default, let the value handle its own converstion to a function. + auto AsFunction(ValueType & val) const { return val.AsFunction(); } + }; + + template + struct SymbolTable, DUMMY_T> { + using map_t = emp::ra_map; + using arg_t = const map_t &; + using fun_t = std::function; + using value_t = ValueType; + + const typename map_t::layout_t & layout; + + SymbolTable(const emp::ra_map & in_map) + : layout(in_map.GetLayout()) { } + + fun_t MakeDatumAccessor(const std::string & name) const { + emp_assert(layout.find(name) != layout.end()); + size_t id = layout.find(name)->second; + #ifdef NDEBUG + return [id](arg_t symbol_vals){ + #else + return [id,name](arg_t symbol_vals){ // Keep name in debug mode to check id. + emp_assert(symbol_vals.GetID(name) == id); + #endif + return emp::Datum(symbol_vals.AtID(id)); + }; + } + + /// By default, let the value handle its own converstion to a function. + auto AsFunction(ValueType & val) const { + // @CAO: Could check layout correctness in debug mode. + return val.AsFunction(); + } + }; + + /// Specialty implementation for DataLayouts. + template + struct SymbolTable { + using arg_t = const emp::DataMap &; + using fun_t = std::function; + using value_t = ValueType; + + const emp::DataLayout & layout; + + SymbolTable(const emp::DataLayout & in_layout) : layout(in_layout) { } + + auto MakeDatumAccessor(const std::string & name) const { + return emp::DataMap::MakeDatumAccessor(layout, name); + } + + auto AsFunction(ValueType & val) const { + #ifdef NDEBUG + return val.AsFunction(); + #else + // If we are in debug mode, add wrapper to ensure DataMap with has correct layout. + return [fun=val.AsFunction(),layout_ptr=&layout](arg_t dm) { + emp_assert(dm.HasLayout(*layout_ptr)); + return fun(dm); + }; + #endif + } + + }; + + /// Special DataMap implementation that just converts to underlying layout. + template + struct SymbolTable : public SymbolTable { + SymbolTable(const emp::DataMap & dm) : SymbolTable(dm.GetLayout()) { } + }; + + + using pos_t = emp::TokenStream::Iterator; + + static constexpr const bool verbose = false; + + class MapLexer : public emp::Lexer { + private: + int token_identifier; ///< Token id for identifiers + int token_number; ///< Token id for literal numbers + int token_string; ///< Token id for literal strings + int token_char; ///< Token id for literal characters + int token_external; ///< Token id for an external value that was passed in + int token_symbol; ///< Token id for other symbols + + public: + MapLexer() { + // Whitespace and comments should always be dismissed (top priority) + IgnoreToken("Whitespace", "[ \t\n\r]+"); + IgnoreToken("//-Comments", "//.*"); + IgnoreToken("/*...*/-Comments", "/[*]([^*]|([*]+[^*/]))*[*]+/"); + + // Meaningful tokens have next priority. + + // An identifier must begin with a letter, underscore, or dot, and followed by + // more of the same OR numbers or brackets. + token_identifier = AddToken("Identifier", "[a-zA-Z_.][a-zA-Z0-9_.[\\]]*"); + + // A literal number must begin with a digit; it can have any number of digits in it and + // optionally a decimal point. + token_number = AddToken("Literal Number", "[0-9]+(\\.[0-9]+)?"); + + // A string must begin and end with a quote and can have an escaped quote in the middle. + token_string = AddToken("Literal String", "\\\"([^\"\\\\]|\\\\.)*\\\""); + + // A literal char must begin and end with a single quote. It will always be treated as + // its ascii value. + token_char = AddToken("Literal Character", "'([^'\n\\\\]|\\\\.)+'"); + + // An external value that was passed in will be a dollar sign ('$') followed by the + // position of the value to be used (e.g., '$3'). + token_external = AddToken("External Value", "[$][0-9]+"); + + // Symbols should have least priority. They include any solitary character not listed + // above, or pre-specified multi-character groups. + token_symbol = AddToken("Symbol", ".|\"==\"|\"!=\"|\"<=\"|\">=\"|\"~==\"|\"~!=\"|\"~<\"|\"~>\"|\"~<=\"|\"~>=\"|\"&&\"|\"||\"|\"**\"|\"%%\""); + } + + bool IsID(const emp::Token & token) const noexcept { return token.id == token_identifier; } + bool IsNumber(const emp::Token & token) const noexcept { return token.id == token_number; } + bool IsString(const emp::Token & token) const noexcept { return token.id == token_string; } + bool IsChar(const emp::Token & token) const noexcept { return token.id == token_char; } + bool IsExternal(const emp::Token & token) const noexcept { return token.id == token_external; } + bool IsSymbol(const emp::Token & token) const noexcept { return token.id == token_symbol; } + }; + + struct BinaryOperator { + using fun_t = std::function; + size_t prec; + fun_t fun; + void Set(size_t in_prec, fun_t in_fun) { prec = in_prec; fun = in_fun; } + }; + + struct Function { + using fun0_t = std::function; + using fun1_t = std::function; + using fun2_t = std::function; + using fun3_t = std::function; + + size_t num_args = 0; + fun0_t fun0; fun1_t fun1; fun2_t fun2; fun3_t fun3; + + void Set0(fun0_t in_fun) { num_args = 0; fun0 = in_fun; } + void Set1(fun1_t in_fun) { num_args = 1; fun1 = in_fun; } + void Set2(fun2_t in_fun) { num_args = 2; fun2 = in_fun; } + void Set3(fun3_t in_fun) { num_args = 3; fun3 = in_fun; } + }; + + // --------- MEMBER VARIABLES ----------- + MapLexer lexer; + + // Operators and functions that should be used when parsing. + std::unordered_map> unary_ops; + std::unordered_map binary_ops; + std::unordered_map functions; + emp::vector external_vals; + + // The set of data map entries accessed when the last function was parsed. + std::set var_names; + + // Track the number of errors and the function to call when errors occur. + template + size_t ParseError(Ts &&... args) { + emp::notify::Exception("SimpleParser::PARSE_ERROR", emp::to_string(args...), this); + return 1; + } + + public: + SimpleParser(bool use_defaults=true) { + if (use_defaults) { + AddDefaultOperators(); + AddDefaultFunctions(); + } + } + + /// Construct with a random number generator to automatically include random functions. + SimpleParser(bool use_defaults, emp::Random & random) : SimpleParser(use_defaults) + { AddRandomFunctions(random); } + + /// Get the set of variable names that the most recently generated function used. + const std::set & GetNamesUsed() const { return var_names; } + + /// Get the set of names used in the provided equation. + const std::set & GetNamesUsed(const std::string & expression) { + var_names.clear(); + emp::TokenStream tokens = lexer.Tokenize(expression, std::string("Expression: ") + expression); + for (emp::Token token : tokens) { + if (lexer.IsID(token) && !emp::Has(functions, token.lexeme)) { + var_names.insert(token.lexeme); + } + } + return var_names; + } + + + /// Add a unary operator + void AddOp(const std::string & op, std::function fun) { + unary_ops[op] = fun; + } + + /// Add a binary operator + void AddOp(const std::string & op, size_t prec, + std::function fun) { + binary_ops[op].Set(prec, fun); + } + + + static int ApproxCompare(double x, double y) { + static constexpr double APPROX_FRACTION = 8192.0; + double margin = y / APPROX_FRACTION; + if (x < y - margin) return -1; + if (x > y + margin) return 1; + return 0; + } + + void AddDefaultOperators() { + // Setup the unary operators for the parser. + AddOp("+", [](emp::Datum x) { return x; }); + AddOp("-", [](emp::Datum x) { return -x; }); + AddOp("!", [](emp::Datum x) { return !x; }); + + + // Setup the default binary operators for the parser. + size_t prec = 0; // Precedence level of each operator... + AddOp("||", ++prec, [](emp::Datum x, emp::Datum y){ return (x!=0.0)||(y!=0.0); } ); + AddOp("&&", ++prec, [](emp::Datum x, emp::Datum y){ return (x!=0.0)&&(y!=0.0); } ); + AddOp("==", ++prec, [](emp::Datum x, emp::Datum y){ return x == y; } ); + AddOp("!=", prec, [](emp::Datum x, emp::Datum y){ return x != y; } ); + AddOp("~==", prec, [](emp::Datum x, emp::Datum y){ return ApproxCompare(x,y) == 0; } ); + AddOp("~!=", prec, [](emp::Datum x, emp::Datum y){ return ApproxCompare(x,y) != 0; } ); + AddOp("<", ++prec, [](emp::Datum x, emp::Datum y){ return x < y; } ); + AddOp("<=", prec, [](emp::Datum x, emp::Datum y){ return x <= y; } ); + AddOp(">", prec, [](emp::Datum x, emp::Datum y){ return x > y; } ); + AddOp(">=", prec, [](emp::Datum x, emp::Datum y){ return x >= y; } ); + AddOp("~<", prec, [](emp::Datum x, emp::Datum y){ return ApproxCompare(x,y) == -1; } ); + AddOp("~<=", prec, [](emp::Datum x, emp::Datum y){ return ApproxCompare(x,y) != 1; } ); + AddOp("~>", prec, [](emp::Datum x, emp::Datum y){ return ApproxCompare(x,y) == 1; } ); + AddOp("~>=", prec, [](emp::Datum x, emp::Datum y){ return ApproxCompare(x,y) != -1; } ); + AddOp("+", ++prec, [](emp::Datum x, emp::Datum y){ return x + y; } ); + AddOp("-", prec, [](emp::Datum x, emp::Datum y){ return x - y; } ); + AddOp("*", ++prec, [](emp::Datum x, emp::Datum y){ return x * y; } ); + AddOp("/", prec, [](emp::Datum x, emp::Datum y){ return x / y; } ); + AddOp("%", prec, [](emp::Datum x, emp::Datum y){ return emp::Mod(x, y); } ); + AddOp("**", ++prec, [](emp::Datum x, emp::Datum y){ return emp::Pow(x, y); } ); + AddOp("%%", prec, [](emp::Datum x, emp::Datum y){ return emp::Log(x, y); } ); + } + + void AddDefaultFunctions() { + // Setup the default functions. + functions["ABS"].Set1( [](emp::Datum x){ return std::abs(x); } ); + functions["EXP"].Set1( [](emp::Datum x){ return emp::Pow(emp::E, x); } ); + functions["LOG"].Set1( [](emp::Datum x){ return std::log(x); } ); + functions["LOG2"].Set1( [](emp::Datum x){ return std::log2(x); } ); + functions["LOG10"].Set1( [](emp::Datum x){ return std::log10(x); } ); + + functions["SQRT"].Set1( [](emp::Datum x){ return std::sqrt(x); } ); + functions["CBRT"].Set1( [](emp::Datum x){ return std::cbrt(x); } ); + + functions["SIN"].Set1( [](emp::Datum x){ return std::sin(x); } ); + functions["COS"].Set1( [](emp::Datum x){ return std::cos(x); } ); + functions["TAN"].Set1( [](emp::Datum x){ return std::tan(x); } ); + functions["ASIN"].Set1( [](emp::Datum x){ return std::asin(x); } ); + functions["ACOS"].Set1( [](emp::Datum x){ return std::acos(x); } ); + functions["ATAN"].Set1( [](emp::Datum x){ return std::atan(x); } ); + functions["SINH"].Set1( [](emp::Datum x){ return std::sinh(x); } ); + functions["COSH"].Set1( [](emp::Datum x){ return std::cosh(x); } ); + functions["TANH"].Set1( [](emp::Datum x){ return std::tanh(x); } ); + functions["ASINH"].Set1( [](emp::Datum x){ return std::asinh(x); } ); + functions["ACOSH"].Set1( [](emp::Datum x){ return std::acosh(x); } ); + functions["ATANH"].Set1( [](emp::Datum x){ return std::atanh(x); } ); + + functions["CEIL"].Set1( [](emp::Datum x){ return std::ceil(x); } ); + functions["FLOOR"].Set1( [](emp::Datum x){ return std::floor(x); } ); + functions["ROUND"].Set1( [](emp::Datum x){ return std::round(x); } ); + + functions["ISINF"].Set1( [](emp::Datum x){ return std::isinf(x); } ); + functions["ISNAN"].Set1( [](emp::Datum x){ return std::isnan(x); } ); + + // Default 2-input functions + functions["HYPOT"].Set2( [](emp::Datum x, emp::Datum y){ return std::hypot(x,y); } ); + functions["EXP"].Set2( [](emp::Datum x, emp::Datum y){ return emp::Pow(x,y); } ); + functions["LOG"].Set2( [](emp::Datum x, emp::Datum y){ return emp::Log(x,y); } ); + functions["MIN"].Set2( [](emp::Datum x, emp::Datum y){ return (xy) ? x : y; } ); + functions["POW"].Set2( [](emp::Datum x, emp::Datum y){ return emp::Pow(x,y); } ); + + // Default 3-input functions. + functions["IF"].Set3( [](emp::Datum x, emp::Datum y, emp::Datum z){ + return (x!=0.0) ? y : z; + } ); + functions["CLAMP"].Set3( [](emp::Datum x, emp::Datum y, emp::Datum z){ + return (xz) ? z : x; + } ); + functions["TO_SCALE"].Set3( [](emp::Datum x, emp::Datum y, emp::Datum z){ + return (z-y)*x+y; + } ); + functions["FROM_SCALE"].Set3( [](emp::Datum x, emp::Datum y, emp::Datum z){ + return (x-y) / (z-y); + } ); + } + + void AddRandomFunctions(Random & random) { + functions["RAND"].Set0( [&random](){ return random.GetDouble(); } ); + functions["RAND"].Set1( [&random](emp::Datum x){ return random.GetDouble(x); } ); + functions["RAND"].Set2( [&random](emp::Datum x, emp::Datum y){ return random.GetDouble(x,y); } ); + } + + /// Helpers for parsing. + template + typename SYMBOLS_T::value_t ParseValue(const SYMBOLS_T & symbols, pos_t & pos) { + if constexpr (verbose) { + std::cout << "ParseValue at position " << pos.GetIndex() << " : " << pos->lexeme << std::endl; + } + + using arg_t = typename SYMBOLS_T::arg_t; + using fun_t = typename SYMBOLS_T::fun_t; + using value_t = typename SYMBOLS_T::value_t; + + // Deal with any unary operators... + if (emp::Has(unary_ops, pos->lexeme)) { + if constexpr (verbose) std::cout << "Found UNARY OP: " << pos->lexeme << std::endl; + auto op = unary_ops[pos->lexeme]; + ++pos; + value_t val = ParseValue(symbols, pos); + if (val.type == value_t::VALUE) { return op(val.value); } + else { + return static_cast( + [fun=val.fun,op](arg_t arg){ return op(fun(arg)); } + ); + } + } + + // If we have parentheses, process the contents + if (pos->lexeme == "(") { + if constexpr (verbose) std::cout << "Found: OPEN PAREN" << std::endl; + ++pos; + value_t val = ParseMath(symbols, pos); + if (pos->lexeme != ")") return ParseError("Expected ')', but found '", pos->lexeme, "'."); + ++pos; + return val; + } + + // If this is a value, set it and return. + if (lexer.IsNumber(*pos)) { + double result = emp::from_string(pos->lexeme); + ++pos; + return result; + } + + // Similar for an external value + if (lexer.IsExternal(*pos)) { + size_t id = emp::from_string(pos->lexeme.substr(1)); + ++pos; + if (id >= external_vals.size()) { + ParseError("Invalid access into external variable (\"$", id, "\"): Does not exist."); + } + return external_vals[id]; + } + + // Otherwise it should be and identifier! + const std::string & name = pos->lexeme; + ++pos; + + // If it is followed by a parenthesis, it should be a function. + const bool is_fun = (pos.IsValid() && pos->lexeme == "("); + + if (is_fun) { + if (!emp::Has(functions, name)) return ParseError("Call to unknown function '", name,"'."); + ++pos; + emp::vector args; + while(pos->lexeme != ")") { + args.push_back(ParseMath(symbols, pos)); + if (pos->lexeme == ",") ++pos; + } + ++pos; + + // Now build the function based on its argument count. + fun_t out_fun; + switch (args.size()) { + case 0: + if (!functions[name].fun0) ParseError("Function '", name, "' requires arguments."); + out_fun = [fun=functions[name].fun0](arg_t /*sym_arg*/) { return fun(); }; + break; + case 1: + if (!functions[name].fun1) ParseError("Function '", name, "' cannot have 1 arguments."); + out_fun = [fun=functions[name].fun1,arg0=args[0].AsFunction()](arg_t sym_arg) { + return fun(arg0(sym_arg)); + }; + break; + case 2: + if (!functions[name].fun2) ParseError("Function '", name, "' cannot have 2 arguments."); + out_fun = [fun=functions[name].fun2, + arg0=args[0].AsFunction(), + arg1=args[1].AsFunction()](arg_t sym_arg) { + return fun(arg0(sym_arg), arg1(sym_arg)); + }; + break; + case 3: + if (!functions[name].fun3) ParseError("Function '", name, "' cannot have 3 arguments."); + out_fun = [fun=functions[name].fun3, + arg0=args[0].AsFunction(), + arg1=args[1].AsFunction(), + arg2=args[2].AsFunction()](arg_t sym_arg) { + return fun(arg0(sym_arg), arg1(sym_arg), arg2(sym_arg)); + }; + break; + default: + ParseError("Too many arguments (", args.size(), ") for function '", name, "'."); + } + return out_fun; + } + + var_names.insert(name); // Store this name in the list of those used. + return symbols.MakeDatumAccessor(name); // Return an accessor for this name. + } + + template + typename SYMBOLS_T::value_t ParseMath(const SYMBOLS_T & symbols, pos_t & pos, size_t prec_limit=0) { + using value_t = typename SYMBOLS_T::value_t; + using arg_t = typename SYMBOLS_T::arg_t; + value_t val1 = ParseValue(symbols, pos); + + if constexpr (verbose) { + if (pos.IsValid()) { + std::cout << "ParseMath at " << pos.GetIndex() << " : " << pos->lexeme << std::endl; + } else std::cout << "PROCESSED!" << std::endl; + } + + while (pos.IsValid() && pos->lexeme != ")" && pos->lexeme != ",") { + if constexpr (verbose) { std::cout << "...Scanning for op... [" << pos->lexeme << "]" << std::endl; } + + // If we have an operator, act on it! + if (Has(binary_ops, pos->lexeme)) { + const BinaryOperator & op = binary_ops[pos->lexeme]; + if (prec_limit >= op.prec) return val1; // Precedence not allowed; return currnet value. + ++pos; + value_t val2 = ParseMath(symbols, pos, op.prec); + if (val1.type == value_t::VALUE) { + if (val2.type == value_t::VALUE) { val1 = op.fun(val1.value, val2.value); } + else { + val1 = [val1_num=val1.value,val2_fun=val2.fun,op_fun=op.fun](arg_t symbol_vals){ + return op_fun(val1_num, val2_fun(symbol_vals)); + }; + } + } else { + if (val2.type == value_t::VALUE) { + val1 = [val1_fun=val1.fun,val2_num=val2.value,op_fun=op.fun](arg_t symbol_vals){ + return op_fun(val1_fun(symbol_vals), val2_num); + }; + } else { + val1 = [val1_fun=val1.fun,val2_fun=val2.fun,op_fun=op.fun](arg_t symbol_vals){ + return op_fun(val1_fun(symbol_vals), val2_fun(symbol_vals)); + }; + } + } + } + + else ParseError("Operator '", pos->lexeme, "' NOT found!"); + } + + // @CAO Make sure there's not a illegal lexeme here. + + return val1; + } + + /// Take a set of variables and use them to replace $0, $1, etc. in any function. + template + void SetupStaticValues(T1 arg1, Ts... args) { + // If we have a vector of incoming values, make sure it is valid and then just pass it along. + if constexpr (sizeof...(Ts) == 0 && emp::is_emp_vector()) { + using value_t = typename T1::value_type; + static_assert(std::is_same(), + "If BuildMathFunction is provided a vector, it must contain only emp::Datum."); + external_vals = arg1; + return; + } + + else { + // Otherwise convert all args to emp::Datum. + external_vals = emp::vector{ + static_cast(arg1), + static_cast(args)... + }; + } + } + + /// If there are no input args, just clear external values. + void SetupStaticValues() { external_vals.resize(0); } + + /// Parse a function description that will take a map and return the results. + /// For example, if the string "foo * 2 + bar" is passed in, a function will be returned + /// that takes a map (of the proper type) loads in the values of "foo" and "bar", and + /// returns the result of the above equation. + + template + auto BuildMathFunction( + const MAP_T & symbol_map, ///< The map or layout to use, specifying variables. + const std::string & expression, ///< The primary expression to convert. + EXTRA_Ts... extra_args ///< Extra value arguments (accessed as $1, $2, etc.) + ) { + // If we have incoming values, store them appropriately. + SetupStaticValues(extra_args...); + + using value_t = typename SymbolTable::value_t; + SymbolTable symbol_table(symbol_map); + + // Tokenize the expression. + emp::TokenStream tokens = lexer.Tokenize(expression, std::string("Expression: ") + expression); + if constexpr (verbose) tokens.Print(); + var_names.clear(); // Reset the names used from data map. + pos_t pos = tokens.begin(); + value_t val = ParseMath(symbol_table, pos); + + // Return the value as a function. + return symbol_table.AsFunction(val); + } + + + /// Generate a temporary math function and immediately run it on the provided arguments. + /// @param symbol_map The map containing the required variables. + /// @param expression The mathematical expression to be run on the data map. + /// @param extras Any extra values to fill in a $0, $1, etc. + template + emp::Datum RunMathFunction(const MAP_T & symbol_map, ARG_Ts... args) { + auto fun = BuildMathFunction(symbol_map, std::forward(args)...); + return fun(symbol_map); + } + + }; + +} + +#endif // #ifndef EMP_DATA_SIMPLEPARSER_HPP_INCLUDE diff --git a/include/emp/data/Trait.hpp b/include/emp/data/Trait.hpp index 92cf00f316..8acaab5aa0 100644 --- a/include/emp/data/Trait.hpp +++ b/include/emp/data/Trait.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Trait.hpp + * @file * @brief Directly measure a target quality about a type of object. * * These objects are able to measure a specific trait on another object. They @@ -13,6 +14,7 @@ #ifndef EMP_DATA_TRAIT_HPP_INCLUDE #define EMP_DATA_TRAIT_HPP_INCLUDE +#include #include #include "../base/assert.hpp" @@ -83,7 +85,7 @@ namespace emp { void SetMax(value_t max) { range.SetUpper(max); } value_t Eval(target_t & target) const { return fun(target); } - value_t EvalLimit(target_t & target) const { return range.Limit(fun(target)); } + value_t EvalLimit(target_t & target) const { return range.Clamp(fun(target)); } std::string EvalString(target_t & target) const { return std::to_string(EvalLimit(target)); } double EvalValue(target_t & target) const { return (double) EvalLimit(target); } diff --git a/include/emp/data/VarMap.hpp b/include/emp/data/VarMap.hpp index 30132b2ac1..48a0b25727 100644 --- a/include/emp/data/VarMap.hpp +++ b/include/emp/data/VarMap.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file VarMap.hpp + * @file * @brief VarMaps track arbitrary data by name (slow) or id (faster). * @note Status: ALPHA */ @@ -11,6 +12,7 @@ #ifndef EMP_DATA_VARMAP_HPP_INCLUDE #define EMP_DATA_VARMAP_HPP_INCLUDE +#include #include #include "../base/assert.hpp" diff --git a/include/emp/datastructs/BloomFilter.hpp b/include/emp/datastructs/BloomFilter.hpp index f699e64ae9..b4c59407d7 100644 --- a/include/emp/datastructs/BloomFilter.hpp +++ b/include/emp/datastructs/BloomFilter.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file BloomFilter.hpp + * @file * @brief A Bloom filter implementation * * @note This file is included in Empirical (https://github.com/devosoft/Empirical) for convenience. @@ -15,7 +16,7 @@ #ifndef EMP_DATASTRUCTS_BLOOMFILTER_HPP_INCLUDE #define EMP_DATASTRUCTS_BLOOMFILTER_HPP_INCLUDE -/********************************************************************* +/* * Open Bloom Filter * * * * Author: Arash Partow - 2000 * @@ -53,6 +54,7 @@ static const unsigned char bit_mask[bits_per_char] = { 0x80 //10000000 }; +#ifndef DOXYGEN_SHOULD_SKIP_THIS /// This class keeps track of the parameters for a Bloom filter class BloomParameters { @@ -171,6 +173,8 @@ class BloomParameters }; +#endif // DOXYGEN_SHOULD_SKIP_THIS + /// This class implements a Bloom filter, which is a /// memory-efficient data structure for identifying /// values that have been seen before (with a tunable diff --git a/include/emp/datastructs/Bool.hpp b/include/emp/datastructs/Bool.hpp index ff5397e0fc..44ebd30afc 100644 --- a/include/emp/datastructs/Bool.hpp +++ b/include/emp/datastructs/Bool.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file Bool.hpp + * @file * @brief A bool representation that doesn't trip up std::vector * @note Status: ALPHA * diff --git a/include/emp/datastructs/Cache.hpp b/include/emp/datastructs/Cache.hpp index 70e465de4e..7219b9fb45 100644 --- a/include/emp/datastructs/Cache.hpp +++ b/include/emp/datastructs/Cache.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file Cache.hpp + * @file * @brief similar to an std::unordered_map, but all lookups come with a function to generate the result should the lookup fail. * @note Status: BETA */ @@ -12,6 +13,7 @@ #define EMP_DATASTRUCTS_CACHE_HPP_INCLUDE #include +#include #include namespace emp { diff --git a/include/emp/datastructs/DynamicString.hpp b/include/emp/datastructs/DynamicString.hpp index a17457c8bf..4e30dbe90b 100644 --- a/include/emp/datastructs/DynamicString.hpp +++ b/include/emp/datastructs/DynamicString.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file DynamicString.hpp + * @file * @brief A string handler where sections update dynamically based on functions. * @note Status: BETA */ @@ -13,6 +14,7 @@ #include +#include #include #include "../base/vector.hpp" @@ -31,7 +33,7 @@ namespace emp { DynamicString() { ; } DynamicString(const DynamicString &) = default; - /// How many string components (funcations or continuous substrings) are in this DynamicString? + /// How many string components (functions or continuous substrings) are in this DynamicString? size_t GetSize() const { return fun_set.size(); } /// Index in to a specific component (not a specific character, since size is variable) @@ -83,6 +85,7 @@ namespace emp { } +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace std { /// Make sure that DynamicString works with with std::ostream. std::ostream & operator<<( std::ostream & os, const emp::DynamicString & strings ) @@ -93,5 +96,6 @@ namespace std { return os; } } +#endif // DOXYGEN_SHOULD_SKIP_THIS #endif // #ifndef EMP_DATASTRUCTS_DYNAMICSTRING_HPP_INCLUDE diff --git a/include/emp/datastructs/Graph.hpp b/include/emp/datastructs/Graph.hpp index 2ba86b5935..8a3b02184c 100644 --- a/include/emp/datastructs/Graph.hpp +++ b/include/emp/datastructs/Graph.hpp @@ -1,10 +1,11 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file Graph.hpp - * @brief A simple, fast class for managing verticies (nodes) and edges. + * @file + * @brief A simple, fast class for managing vertices (nodes) and edges. * @note Status: BETA */ @@ -12,6 +13,7 @@ #define EMP_DATASTRUCTS_GRAPH_HPP_INCLUDE #include +#include #include "../base/assert.hpp" #include "../base/vector.hpp" @@ -22,6 +24,7 @@ namespace emp { /// A graph class that maintains a set of vertices (nodes) and edges (connecting pairs of nodes) class Graph { public: + #ifndef DOXYGEN_SHOULD_SKIP_THIS /// Information about nodes within a graph. class Node { private: @@ -74,6 +77,7 @@ namespace emp { } }; + #endif // DOXYGEN_SHOULD_SKIP_THIS protected: emp::vector nodes; ///< Set of vertices in this graph. @@ -188,7 +192,7 @@ namespace emp { return nodes[from].HasEdge(to) && nodes[to].HasEdge(from); } - /// Add a pair of edges between two vertieces (in both directions) + /// Add a pair of edges between two vertices (in both directions) void AddEdgePair(size_t from, size_t to) { emp_assert(from < nodes.size() && to < nodes.size()); nodes[from].AddEdge(to); diff --git a/include/emp/datastructs/IndexMap.hpp b/include/emp/datastructs/IndexMap.hpp index c737694d81..a19c78c42a 100644 --- a/include/emp/datastructs/IndexMap.hpp +++ b/include/emp/datastructs/IndexMap.hpp @@ -1,20 +1,31 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2022 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2018 - * - * @file IndexMap.hpp + * @file * @brief A simple class to weight items differently within a container and return the correct index. * @note Status: BETA * + * An IndexMap is a container where each item has a specified weight (specified as a double). + * The total weight of the container determines the max index point. When indexing into the + * container, each item is represented by a range of values equal to it's weight. Randomly + * indexing into the container will provide either item with a probability proportional to its + * weight. + * + * In this regular IndexMap, all items are kept in order (so the map starts at 0, then 1, then + * 2, etc.) If order is not required, UnorderedIndexMap is slightly faster. + * * @todo Convert to a template that acts as a glorified vector, simplifying random selection? - * @todo Should operator[] index by element count or by weight? * @todo Make Raw*() function private. */ #ifndef EMP_DATASTRUCTS_INDEXMAP_HPP_INCLUDE #define EMP_DATASTRUCTS_INDEXMAP_HPP_INCLUDE +#include + #include "../base/vector.hpp" namespace emp { @@ -41,13 +52,14 @@ namespace emp { /// Which ID is the right child of the ID provided? size_t RightID(size_t id) const { return 2*id + 2; } - /// Sift through the nodes to find the where index zero maps to. + /// Sift through the nodes to find where index zero maps to. size_t CalcZeroOffset() const { size_t id = 0; while (id < num_items - 1) id = LeftID(id); return id - (num_items - 1); } + /// Convert an item ID to the internal position where it's stored. size_t ToInternalID(size_t id) const { return (id + zero_offset) % num_items + num_items-1; } @@ -56,10 +68,34 @@ namespace emp { return (id + _offset) % _items + _items-1; } + /// Convert and internal position to the item ID to which it refers. size_t ToExternalID(size_t id) const { return (id + 1 - zero_offset) % num_items; } + // Collect the weight at the specified index of the array (no conversions) + double RawWeight(size_t id) const { return weights[id]; } + + // Collect the probability at the specified index of the array (no conversions) + double RawProb(size_t id) const { ResolveRefresh(); return weights[id] / weights[0]; } + + /// Adjust the weight associated with a particular index in the map. + /// @param id is the identification number of the item whose weight is being adjusted. + /// @param new_weight is the new weight for that entry. + void RawAdjust(size_t id, const double new_weight) { + // Update this node. + const double weight_diff = new_weight - weights[id]; // Track change size for tree weights. + weights[id] = new_weight; // Update THIS item weight + + if (needs_refresh) return; // If we already need a refresh don't update tree weights! + + // Update tree to root. + while (id > 0) { + id = ParentID(id); + weights[id] += weight_diff; + } + } + /// A Proxy class so that an index can be treated as an l-value. class Proxy { private: @@ -94,7 +130,10 @@ namespace emp { } IndexMap(size_t _items, double init_weight) : num_items(_items), zero_offset(CalcZeroOffset()), needs_refresh(true) - , weights(num_items, init_weight) { ; } + , weights(num_items*2-1, 0.0) + { + if (init_weight != 0.0) AdjustAll(init_weight); + } IndexMap(const IndexMap &) = default; IndexMap(IndexMap &&) = default; ~IndexMap() = default; @@ -108,11 +147,9 @@ namespace emp { double GetWeight() const { ResolveRefresh(); return weights[0]; } /// What is the current weight of the specified index? - double RawWeight(size_t id) const { return weights[id]; } double GetWeight(size_t id) const { return RawWeight(ToInternalID(id)); } /// What is the probability of the specified index being selected? - double RawProb(size_t id) const { ResolveRefresh(); return weights[id] / weights[0]; } double GetProb(size_t id) const { return RawProb(ToInternalID(id)); } /// Change the number of indices in the map. @@ -161,23 +198,6 @@ namespace emp { Clear(); } - /// Adjust the weight associated with a particular index in the map. - /// @param id is the identification number of the item whose weight is being adjusted. - /// @param new_weight is the new weight for that entry. - void RawAdjust(size_t id, const double new_weight) { - // Update this node. - const double weight_diff = new_weight - weights[id]; // Track change size for tree weights. - weights[id] = new_weight; // Update THIS item weight - - if (needs_refresh) return; // If we already need a refresh don't update tree weights! - - // Update tree to root. - while (id > 0) { - id = ParentID(id); - weights[id] += weight_diff; - } - } - void Adjust(size_t id, const double new_weight) { RawAdjust(ToInternalID(id), new_weight); } /// Adjust all index weights to the set provided. diff --git a/include/emp/datastructs/IndexSet.hpp b/include/emp/datastructs/IndexSet.hpp new file mode 100644 index 0000000000..4d34e72a22 --- /dev/null +++ b/include/emp/datastructs/IndexSet.hpp @@ -0,0 +1,529 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ +/** + * @file + * @brief Collection of indices, ideally optimized for memory size. + * @note Status: ALPHA + */ + +#ifndef EMP_DATASTRUCTS_INDEXSET_HPP_INCLUDE +#define EMP_DATASTRUCTS_INDEXSET_HPP_INCLUDE + +#include "../base/Ptr.hpp" +#include "../bits/BitVector.hpp" +#include "../math/constants.hpp" + +namespace emp { + + /// Index range is a simple pair of values indicating the start and end of a series of indices. + class IndexRange { + size_t start = 0; // First value in this range. + size_t end = 0; // First value after start NOT in this range; zero for empty range. + + public: + IndexRange() = default; + IndexRange(size_t val) : start(val), end(val+1) { } + IndexRange(size_t _start, size_t _end) : start(_start), end(_end) { } + IndexRange(const IndexRange &) = default; + + IndexRange & operator=(const IndexRange &) = default; + + auto operator<=>(const IndexRange &) const = default; + + size_t GetStart() const { return start; } + size_t GetEnd() const { return end; } + size_t GetSize() const { return end-start; } + + void SetStart(size_t in) { start = in; } + void SetEnd(size_t in) { end = in; } + + bool Has(size_t val) const { return val >= start && val < end; } + bool Has(IndexRange in) const { return in.start >= start && in.end < end; } + + /// Will identify if two ranges are next to each other or overlapping. + bool IsConnected(IndexRange in) const { + return (in.start >= start && in.start <= end || + start >= in.start && start <= in.end); + } + + /// Grow this range (default, by one) + void Grow(size_t count=1) { end += count; } + + /// Insert a value into a range if valid; return false if not. + bool Insert(size_t val) { + if (val == end) { end++; return true; } + if (val == start - 1) { start--; return true; } + return Has(val); + } + + /// Extend the current range with a new one. Must be perfectly adjacent! + bool Append(IndexRange in) { + if (end == in.start) { end = in.end; return true; } + return false; + } + + /// @brief Expand this range to encompass a provided value. + /// @param val Value to expand through. + /// @return Whether the range has changed due to this expansion. + bool Expand(size_t val) { + if (val < start) start = val; + else if (val > end) end = val; + else return false; + return true; + } + + /// @brief Expand this range to encompass all provided values. + /// @param vals Values to expand through + /// @return Whether the range has changed due to this expansion. + template + bool Expand(size_t val1, size_t val2, Ts... args) { + return Expand(val1) + Expand(val2, args...); + } + + + /// Merge this range with another. Must be adjacent or overlap! + bool Merge(IndexRange in) { + if (!IsConnected(in)) return false; + return Expand(in.start) + Expand(in.end); // Use + to avoid short-circuiting. + } + }; + + /// IndexRanges is a class to maintain a series of ranges of indexes. The ranges will + /// always be kept sorted and non-adjacent (i.e., there will always be at least one index + /// missing between two ranges). + class IndexRangeSet { + emp::vector range_set; + + // Helper function to find the id of an IndexRange that a value belongs in or can extend; + // returns next-higher index if none fit perfectly. + // @CAO - consider doing a binary search. + size_t _FindRange(size_t val) const { + for (size_t id = 0; id < range_set.size(); ++id) { + if (id <= range_set[id].GetEnd()) return id; + } + return range_set.size(); + } + + // Helper function to grow a range by one, possibly merging it with the next range. + void _GrowRange(size_t id) { + emp_assert(id < range_set.size()); + range_set[id].Grow(); + + // Test if we need to merge with the next range. + if (id+1 < range_set.size() && range_set[id].GetEnd() == range_set[id+1].GetStart()) { + range_set[id].SetEnd(range_set[id+1].GetEnd()); + range_set.erase(range_set.begin()+id+1); // Delete next range (now merged in) + } + } + + public: + IndexRangeSet() = default; + IndexRangeSet(const IndexRangeSet &) = default; + IndexRangeSet(IndexRangeSet &&) = default; + + IndexRangeSet & operator=(const IndexRangeSet &) = default; + IndexRangeSet & operator=(IndexRangeSet &&) = default; + + bool Has(size_t val) const { + size_t id = _FindRange(val); + if (id >= range_set.size()) return false; + return range_set[id].Has(val); + } + + size_t GetStart() const { + return range_set.size() ? range_set[0].GetStart() : emp::MAX_SIZE_T; + } + size_t GetEnd() const { + return range_set.size() ? range_set.back().GetEnd() : 0; + } + + size_t GetNumRanges() const { return range_set.size(); } + + /// @brief Calculate the total combined size of all ranges. + size_t GetSize() const { + size_t total = 0; + for (const auto & x : range_set) total += x.GetSize(); + return total; + } + + // Return all of the internal ranges. + const emp::vector & GetRanges() & { return range_set; } + + /// @brief Add a new value that belongs at the end of the sets. + /// @param val Value to add + /// @return Did the append work? If it's not at the end, returns false. + bool Append(size_t val) { + if (range_set.size() == 0 || val > GetEnd()) range_set.emplace_back(val); // New Range + else if (val == GetEnd()) range_set.back().SetEnd(val+1); // Extend range + else return false; // Not at end + + return true; + } + + /// @brief Add an entire range that belongs at the end of the sets. + /// @param val Range to add + /// @return Did the append work? If it's not at the end, returns false. + bool Append(IndexRange in) { + // Are we adding on a new range? + if (range_set.size() == 0 || in.GetStart() > GetEnd()) { + range_set.emplace_back(in); + } + + // Are we extending an existing range? + else if (in.GetEnd() > GetEnd()) { + // Are we encompassing ALL existing ranges? + if (in.GetStart() <= range_set[0].GetStart()) { + range_set.resize(1); + range_set[0] = in; + } + + // Otherwise find the start and convert from there. + else { + const size_t start_id = _FindRange(in.GetStart()); + range_set[start_id].SetEnd(in.GetEnd()); + } + } + + else return false; // Not at end + + return true; + } + + /// @brief Insert a value into this range set + /// @param val Value to insert. + /// @return Was there a change due to this insertion (or was it already there?) + bool Insert(size_t val) { + // Are we inserting a new range onto the end? + if (Append(val)) return true; + + // Do we already have the value? + size_t id = _FindRange(val); + if (range_set[id].Has(val)) return false; + + // Are we extending the range (and possibly merging)? + else if (range_set[id].GetEnd() == val) _GrowRange(id); + + // Are we extending the beginning of the next range? + else if (range_set[id].GetStart() == val+1) range_set[id].Insert(val); + + // Otherwise we must insert an entirely new range. + else range_set.emplace(id, val); + + return true; + } + + /// @brief Insert a whole range into this set, merging other ranges as needed. + /// @param in New range to include. + /// @return Was there a change due to this insertion (or were they already there?) + bool Insert(IndexRange in) { + // If the new range goes past the end, Append will take care of it. + if (Append(in)) return true; + + size_t start_id = _FindRange(in.GetStart()); + size_t end_id = _FindRange(in.GetEnd()); + emp_assert(start_id <= end_id); + + // If both are in the same range id, either insert a new range or modify an existing one. + if (start_id == end_id) { + // If the end of the new range is before the start of the found range, insert the new one! + if (in.GetEnd() < range_set[start_id].GetStart() - 1) { + range_set.insert(range_set.begin() + start_id, in); + } + + // Otherwise try to merge it into the existing range (will return false if already there) + else return range_set[start_id].Merge(in); + } + + // We are across multiple ranges. Collapse into first! + else { + if (in.GetEnd()+1 < range_set[end_id].GetStart()) --end_id; // Don't include end id. + range_set[start_id].Expand(in.GetStart(), in.GetEnd(), range_set[end_id].GetEnd()); + range_set.erase(range_set.begin()+start_id+1, range_set.begin()+end_id+1); + } + + return true; + } + + /// @brief Remove a single value from this index range. + /// @param val Value to remove + /// @return Did the range change due to this removal? + bool Remove(size_t val) { + if (!Has(val)) return false; + size_t id = _FindRange(val); + IndexRange & cur_range = range_set[id]; + if (cur_range.GetSize() == 1) range_set.erase(range_set.begin()+id); + else if (cur_range.GetStart() == val) cur_range.SetStart(cur_range.GetStart()+1); + else if (cur_range.GetEnd()-1 == val) cur_range.SetEnd(cur_range.GetEnd()-1); + else { + // Need to split the range. + range_set.insert(range_set.begin()+id+1, IndexRange{val+1,cur_range.GetEnd()}); + cur_range.SetEnd(val); + } + } + }; + + /// @brief A class to maintain a set of indices with a bit vector to represent them. + class IndexBits { + emp::BitVector bits; + size_t offset = 0; // Always a multiple of 64. + + // Figure out the best offset for a given value. + size_t _CalcOffset(size_t val) const { + return (val >> 6) << 6; + } + + /// @brief Increase the range of valid values + /// @param val Value to make sure can be set. + void _ExpandRange(size_t val) { + if (bits.GetSize() == 0) { // Must setup bits + offset = _CalcOffset(val); + bits.Resize(64); + } + else if (val < offset) { // Value is before offset... + const size_t new_offset = _CalcOffset(val); + bits.PushFront(offset - new_offset); + offset = new_offset; + } + else if (bits.GetSize() <= val-offset) { // Value is out of range... + bits.Resize(_CalcOffset(val) + 64 - offset); + } + } + public: + IndexBits() = default; + IndexBits(const IndexBits &) = default; + IndexBits(IndexBits &&) = default; + IndexBits(size_t min_val, size_t max_val) : offset(_CalcOffset(min_val)) { + bits.Resize(_CalcOffset(max_val) + 64 - offset); + } + + IndexBits & operator=(const IndexBits &) = default; + IndexBits & operator=(IndexBits &&) = default; + + bool Has(size_t val) const { return (val < offset) ? false : bits[val-offset]; } + size_t GetStart() const { return static_cast(bits.FindOne()) + offset; } + size_t GetEnd() const { return static_cast(bits.FindMaxOne()) + offset; } + size_t GetNumRanges() const { + return (bits & ~(bits >> 1)).CountOnes(); + } + size_t GetSize() const { return bits.CountOnes(); } + bool Insert(size_t val) { + _ExpandRange(val); // Make sure there is room for the new value. + bits.Set(val-offset); + } + bool Insert(IndexRange in) { + _ExpandRange(in.GetStart()); + _ExpandRange(in.GetEnd()); + bits.SetRange(in.GetStart()-offset, in.GetEnd()-offset); + } + bool Remove(size_t val) { + bits.Clear(val - offset); + } + }; + + /// @brief IndexSet maintains a collection of indices that can be easily manipulated. + /// It will try to adjust representation to maintain speed and memory efficiency + class IndexSet { + private: + // For zero to three entries, it will maintain values directly. + // For more than four entries it will use either bits or ranges based on how + // packed the values are into ranges. + enum class index_t { NONE=0, VALS1, VALS2, VALS3, RANGES, BITS }; + struct _Index_Vals { size_t id1; size_t id2; size_t id3; }; // Few values + + union { + _Index_Vals vals; + IndexRangeSet ranges; + IndexBits bits; + }; + index_t type = index_t::NONE; + + // --- Helper functions --- + + /// Free whatever type we currently have. + void _ReleaseUnion() { + if (type == index_t::BITS) bits.~IndexBits(); + else if (type == index_t::RANGES) ranges.~IndexRangeSet(); + } + + /// Convert the internal representation to use bits. + void _ToBits() { + emp_assert(type != index_t::NONE, "Cannot start IndexSet as type BITS"); + if (type == index_t::BITS) return; // Already bits! + + IndexBits new_bits(GetMin(), GetMax()); + + switch (type) { + case index_t::VALS3: new_bits.Insert(vals.id3); [[fallthrough]]; + case index_t::VALS2: new_bits.Insert(vals.id2); [[fallthrough]]; + case index_t::VALS1: new_bits.Insert(vals.id1); + break; + case index_t::RANGES: + for (const auto & range : ranges.GetRanges()) { + new_bits.Insert(range); + } + break; + } + + _ReleaseUnion(); + new (&bits) IndexBits(std::move(new_bits)); + } + + /// Convert the internal representation to use a ranges. + void _ToRanges() { + emp_assert(type != index_t::NONE, "Cannot start IndexSet as type RANGES"); + if (type == index_t::RANGES) return; // Already ranges format! + + IndexRangeSet new_ranges; + + switch (type) { + case index_t::VALS3: new_ranges.Insert(vals.id3); [[fallthrough]]; + case index_t::VALS2: new_ranges.Insert(vals.id2); [[fallthrough]]; + case index_t::VALS1: new_ranges.Insert(vals.id1); + break; + case index_t::BITS: + for (const auto & range : ranges.GetRanges()) { + new_ranges.Insert(range); + } + break; + } + + _ReleaseUnion(); + new (&bits) IndexBits(std::move(new_bits)); + + } + + public: + static constexpr const size_t npos = static_cast(-1); + + IndexSet() = default; + ~IndexSet() { + switch (type) { + case index_t::ARRAY: + ids.array.ids.DeleteArray(); + break; + case index_t::BITS: + ids.bits.bits.DeleteArray(); + break; + } + } + + size_t GetSize() const { + switch (type) { + case index_t::NONE: return 0; + case index_t::VALS1: return 1; + case index_t::VALS2: return 2; + case index_t::VALS3: return 3; + case index_t::RANGE: return ids.range.end - ids.range.start; + case index_t::ARRAY: return ids.array.num_ids; + case index_t::BITS: + size_t count = 0; + for (size_t i = 0; i < ids.bits.num_fields; ++i) { + count += emp::count_bits(ids.bits.bits[i]); + } + return count; + } + } + + bool Has(size_t id) const { + switch (type) { + case index_t::NONE: return false; + case index_t::VALS1: return ids.vals.id1 == id; + case index_t::VALS2: return ids.vals.id1 == id || ids.vals.id2 == id; + case index_t::VALS3: return ids.vals.id1 == id || ids.vals.id2 == id || ids.vals.id3 == id; + case index_t::RANGE: return id >= ids.range.start && id < ids.range.end; + case index_t::ARRAY: return ids.array.num_ids; + case index_t::BITS: { + if (id < ids.bits.offset) return false; + id = id - ids.bits.offset; + const size_t field = id / NUM_FIELD_BITS; + if (field >= ids.bits.num_fields) return false; + const size_t shift = id % NUM_FIELD_BITS; + return (ids.bits.bits[field] >> shift) & 1; + } + } + } + + size_t GetMin() const { + switch (type) { + case index_t::NONE: return npos; + case index_t::VALS1: return ids.vals.id1; + case index_t::VALS2: return ids.vals.id1; + case index_t::VALS3: return ids.vals.id1; + case index_t::RANGE: return ids.range.start; + case index_t::ARRAY: return ids.array.ids[0]; + case index_t::BITS: + return emp::find_bit(ids.bits.bits[0]) + ids.bits.offset; + } + } + + size_t GetMax() const { + switch (type) { + case index_t::NONE: return npos; + case index_t::VALS1: return ids.vals.id1; + case index_t::VALS2: return ids.vals.id2; + case index_t::VALS3: return ids.vals.id3; + case index_t::RANGE: return ids.range.end - 1; + case index_t::ARRAY: return ids.array.ids[ids.array.num_ids-1]; + case index_t::BITS: { + const size_t field_id = ids.bits.num_fields - 1; + const size_t offset = field_id * NUM_FIELD_BITS + ids.bits.offset; + return emp::find_last_bit(ids.bits.bits[field_id]) + offset; + } + } + } + + // Are all of the indicies one after the next? + bool IsConsecutive() const { + switch (type) { + case index_t::NONE: return true; + case index_t::VALS1: return true; + case index_t::VALS2: return ids.vals.id2 == ids.vals.id1+1; + case index_t::VALS3: return ids.vals.id3 == ids.vals.id2+1 && ids.vals.id2 == ids.vals.id1+1; + case index_t::RANGE: return true; + case index_t::ARRAY: + case index_t::BITS: + return GetSize() == GetMax() - GetMin() + 1; + } + } + + void Set(size_t id) { + switch (type) { + case index_t::NONE: ids.vals.id1 = id; type=index_t::VALS1; break; + case index_t::VALS1: if (!Has(id)) { ids.vals.id2 = id; type=index_t::VALS2; } break; + case index_t::VALS2: if (!Has(id)) { ids.vals.id3 = id; type=index_t::VALS3; } break; + case index_t::VALS3: + if (!Has(id)) { + // If current values are consecutive, try to make a range. + if (IsConsecutive()) { + if (id == ids.vals.id1 - 1) { + size_t max_val = ids.vals.id3; + ids.range.start = id; + ids.range.end = max_val+1; + break; + } else if (id == ids.vals.id3 + 1) { + size_t min_val = ids.vals.id1; + ids.range.start = min_val; + ids.range.end = id+1; + break; + } + } + + // If we made it here, values are not consecutive. + // For now: ASSUME we shift to BITS. + const size_t min_val = std::min(id, ids.vals.id1); + const size_t max_val = std::max(id, ids.vals.id3); + const size_t num_bits = max_val - min_val + 1; + const size_t num_fields = (num_bits / NUM_FIELD_BITS + 1) * 2; + } + case index_t::RANGE: return ids.range.end - ids.range.start; + case index_t::ARRAY: return ids.array.num_ids; + case index_t::BITS: return ids.bits.num_ids; + } + } + }; +} + +#endif // #ifndef EMP_DATASTRUCTS_INDEXSET_HPP_INCLUDE diff --git a/include/emp/datastructs/QueueCache.hpp b/include/emp/datastructs/QueueCache.hpp index 2205c73282..105484e777 100644 --- a/include/emp/datastructs/QueueCache.hpp +++ b/include/emp/datastructs/QueueCache.hpp @@ -1,11 +1,13 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020-23 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file QueueCache.hpp + * @file * @brief A simple implementation of a Least-Recently Used Cache. - * It orders elements by access time and removes the stalest ones in case maximum capacity is reached. + * It orders elements by access time and removes the stalest ones in case + * maximum capacity is reached. */ #ifndef EMP_DATASTRUCTS_QUEUECACHE_HPP_INCLUDE @@ -14,12 +16,15 @@ #include #include #include +#include #include #include #include #include "../base/assert.hpp" +#include "map_utils.hpp" + namespace emp { template < class Key, @@ -40,6 +45,7 @@ // maximum number of elements the cache can hold size_t capacity; + #ifndef DOXYGEN_SHOULD_SKIP_THIS // Put the iterator at the beginning of the list, and returns its value // @param it Iterator to element to update // @return Reference to value of updated element @@ -52,6 +58,7 @@ ); return it->second; } + #endif // DOXYGEN_SHOULD_SKIP_THIS // Shrink cache to its capacity by removing elements at the end of it void Shrink() { @@ -71,8 +78,8 @@ // Delete given iterator from cache // @param it cache_map iterator to element to be deleted from cache void Delete(const typename cache_map_t::iterator it) { - cache_map.erase(it); cache_list.erase(it->second); + cache_map.erase(it); } public: @@ -103,9 +110,8 @@ /// Delete element from cache. /// @param key Key to delete from cache void Delete(const Key& key) { - Delete( - cache_map.find(key) - ); + emp_assert(emp::Has(cache_map, key)); + Delete(cache_map.find(key)); } /// Does cache contain key? @@ -120,17 +126,15 @@ /// @param val Value of element to store /// @return Iterator to newly-added element in cache queue typename cache_list_t::iterator Put(const Key& key, const Value& val) { - // try to find element in map + // If the element is already in the cache, delete it. const auto found = cache_map.find(key); if (found != cache_map.end()) { Delete(found); } - // put element into our cache - cache_list.emplace_front(key, val); - // add pointer to this element to our map - cache_map.emplace(key, cache_list.begin()); - // make sure we don't have more elements than our capacity - Shrink(); + + cache_list.emplace_front(key, val); // Put element into the cache + cache_map.emplace(key, cache_list.begin()); // Add element pointer to map + Shrink(); // Reduce if we are over capacity return cache_list.begin(); } diff --git a/include/emp/datastructs/SmallFifoMap.hpp b/include/emp/datastructs/SmallFifoMap.hpp index 82bfb5c642..93983881e5 100644 --- a/include/emp/datastructs/SmallFifoMap.hpp +++ b/include/emp/datastructs/SmallFifoMap.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file SmallFifoMap.hpp + * @file * @brief Store key value pairs in a fixed-sized array, bumping out the oldest * value when full. Optimized for small N. Requires N < 256. * @@ -13,6 +14,7 @@ #define EMP_DATASTRUCTS_SMALLFIFOMAP_HPP_INCLUDE #include +#include #include #include "../base/array.hpp" diff --git a/include/emp/datastructs/SmallVector.hpp b/include/emp/datastructs/SmallVector.hpp index 03a9e58df1..84a6929faf 100644 --- a/include/emp/datastructs/SmallVector.hpp +++ b/include/emp/datastructs/SmallVector.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file SmallVector.hpp + * @file * @brief A drop-in replacement for std::vector with optimization to handle * small vector sizes without dynamic allocation. It contains some number of * elements in-place, which allows it to avoid heap allocation when the actual @@ -36,6 +37,7 @@ namespace emp { // helpers for AlignedCharArrayUnion +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace detail { template class AlignerImpl { @@ -56,6 +58,7 @@ template union SizerImpl { template union SizerImpl { char arr[sizeof(T)]; }; } // end namespace detail +#endif // DOXYGEN_SHOULD_SKIP_THIS /// A suitably aligned and sized character array member which can hold elements /// of any type. @@ -125,11 +128,13 @@ class SmallVectorBase { } }; +#ifndef DOXYGEN_SHOULD_SKIP_THIS /// Figure out the offset of the first element. template struct SmallVectorAlignmentAndSize { emp::AlignedCharArrayUnion Base; emp::AlignedCharArrayUnion FirstEl; }; +#endif // DOXYGEN_SHOULD_SKIP_THIS /// This is the part of SmallVectorTemplateBase which does not depend on whether /// the type T is a POD. The extra dummy template argument is used by ArrayRef @@ -282,6 +287,7 @@ class SmallVectorTemplateBase : public SmallVectorTemplateCommon { } }; +#ifndef DOXYGEN_SHOULD_SKIP_THIS // Define this out-of-line to dissuade the C++ compiler from inlining it. template void SmallVectorTemplateBase::grow(size_t MinSize) { @@ -307,6 +313,7 @@ void SmallVectorTemplateBase::grow(size_t MinSize) { this->BeginX = NewElts; this->Capacity = NewCapacity; } +#endif // DOXYGEN_SHOULD_SKIP_THIS /// SmallVectorTemplateBase - This is where we put /// method implementations that are designed to work with POD-like T's. @@ -363,6 +370,8 @@ class SmallVectorTemplateBase : public SmallVectorTemplateCommon { void pop_back() { this->set_size(this->size() - 1); } }; +#ifndef DOXYGEN_SHOULD_SKIP_THIS + /// This class consists of common code factored out of the SmallVector class to /// reduce code duplication based on the SmallVector 'N' template parameter. template @@ -878,6 +887,8 @@ struct SmallVectorStorage { /// well-defined. template struct alignas(alignof(T)) SmallVectorStorage {}; +#endif /*DOXYGEN_SHOULD_SKIP_THIS*/ + /// This is a 'vector' (really, a variable-sized array), optimized /// for the case when the array is small. It contains some number of elements /// in-place, which allows it to avoid heap allocation when the actual number of @@ -956,6 +967,7 @@ inline size_t capacity_in_bytes(const SmallVector &X) { } // end namespace emp +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace std { /// Implement std::swap in terms of SmallVector swap. @@ -973,5 +985,6 @@ namespace std { } } // end namespace std +#endif // DOXYGEN_SHOULD_SKIP_THIS #endif // #ifndef EMP_DATASTRUCTS_SMALLVECTOR_HPP_INCLUDE diff --git a/include/emp/datastructs/StringMap.hpp b/include/emp/datastructs/StringMap.hpp index 14fc22af87..292ef5d23f 100644 --- a/include/emp/datastructs/StringMap.hpp +++ b/include/emp/datastructs/StringMap.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018-2021. - * - * @file StringMap.hpp + * @file * @brief An std::unordered_map wrapper that deals smoothly with strings and fast compile-time optimizations. * @note Status: ALPHA * @@ -11,7 +12,7 @@ * desginated type. It is more powerful than std::unordered_map because it will accept strings wrapped in * the EMP_STRING_ID macro, which is hashed at compile-time instead of run-time. * - * @CO2: + * \@CO2: * StringMap = PAdictionary (PA = Perfectly accurate) * StringMap = HIDL (HIDL = having an identification of linking) * SMID = String.Map…e112th][string (SMID = StringMap identification) @@ -22,6 +23,7 @@ #ifndef EMP_DATASTRUCTS_STRINGMAP_HPP_INCLUDE #define EMP_DATASTRUCTS_STRINGMAP_HPP_INCLUDE +#include #include "../base/unordered_map.hpp" #include "../tools/string_utils.hpp" diff --git a/include/emp/datastructs/TimeQueue.hpp b/include/emp/datastructs/TimeQueue.hpp index ce2bb6e70f..72c1170eaa 100644 --- a/include/emp/datastructs/TimeQueue.hpp +++ b/include/emp/datastructs/TimeQueue.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file TimeQueue.hpp + * @file * @brief A priority queue for timings, always marching forward. * @note Status: ALPHA * @@ -49,6 +50,7 @@ #include #include +#include #include #include "../base/assert.hpp" diff --git a/include/emp/datastructs/TypeMap.hpp b/include/emp/datastructs/TypeMap.hpp index a7327dae44..f3d0915717 100644 --- a/include/emp/datastructs/TypeMap.hpp +++ b/include/emp/datastructs/TypeMap.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file TypeMap.hpp + * @file * @brief A class that will map types to values of a designated type. * @note Status: BETA */ @@ -11,6 +12,7 @@ #ifndef EMP_DATASTRUCTS_TYPEMAP_HPP_INCLUDE #define EMP_DATASTRUCTS_TYPEMAP_HPP_INCLUDE +#include #include #include "../meta/TypeID.hpp" diff --git a/include/emp/datastructs/UnorderedIndexMap.hpp b/include/emp/datastructs/UnorderedIndexMap.hpp index c24a72570b..b503598e14 100644 --- a/include/emp/datastructs/UnorderedIndexMap.hpp +++ b/include/emp/datastructs/UnorderedIndexMap.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2022. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2021. - * - * @file UnorderedIndexMap.hpp + * @file * @brief A simple class to weight items differently within a container and return the correct index. * @note Status: BETA * @@ -14,6 +15,8 @@ #ifndef EMP_DATASTRUCTS_UNORDEREDINDEXMAP_HPP_INCLUDE #define EMP_DATASTRUCTS_UNORDEREDINDEXMAP_HPP_INCLUDE +#include + #include "../base/vector.hpp" namespace emp { @@ -44,7 +47,7 @@ namespace emp { class Proxy { private: UnorderedIndexMap & index_map; ///< Which index map is this proxy from? - size_t id; ///< Which id does it represent? + size_t id; ///< Which id does it represent? public: Proxy(UnorderedIndexMap & _im, size_t _id) : index_map(_im), id(_id) { ; } operator double() const { return index_map.RawWeight(id); } @@ -65,11 +68,21 @@ namespace emp { } public: - /// Construct an UnorderedIndexMap where num_items is the maximum number of items that can be placed - /// into the data structure. All item weights default to zero. + /// Construct an UnorderedIndexMap where num_items is the maximum number of items that + /// can be placed into the data structure. All item weights default to zero. UnorderedIndexMap(size_t _items=0, double init_weight=0.0) - : num_items(_items), num_nodes(_items-1), needs_refresh(_items && (init_weight > 0.0)), weights(0) + : num_items(_items), num_nodes(_items-1), + needs_refresh(_items && (init_weight > 0.0)), weights(0) { if (_items > 0) weights.resize(_items*2-1, init_weight); } + /// Construct an UnorderedIndexMap with a specified initial set of weights. + UnorderedIndexMap(const emp::vector & in_weights) + : num_items(in_weights.size()), num_nodes(num_items-1), needs_refresh(true) + , weights(num_items*2 - 1) + { + emp_assert(num_items > 0, "UnorderedIndexMaps should not be initialized with empty weights"); + for (size_t i = 0; i < num_items; i++) weights[i + num_nodes] = in_weights[i]; + } + UnorderedIndexMap(const UnorderedIndexMap &) = default; UnorderedIndexMap(UnorderedIndexMap &&) = default; ~UnorderedIndexMap() = default; @@ -153,7 +166,7 @@ namespace emp { void Adjust(size_t id, const double new_weight) { RawAdjust(id + num_nodes, new_weight); } - /// Adjust all index weights to the set provided. + /// Adjust all index & new_weights) { num_items = new_weights.size(); num_nodes = num_items - 1; @@ -164,7 +177,7 @@ namespace emp { needs_refresh = true; } - /// Adjust all index weights to the set provided. + /// Adjust all index weights to the single weight provided. void AdjustAll(double new_weight) { for (size_t i = 0; i < num_items; i++) weights[i + num_nodes] = new_weight; needs_refresh = true; @@ -214,7 +227,7 @@ namespace emp { } /// Indicate that we need to adjust weights before relying on them in the future; this will - /// prevent refreshes from occuring immediately and is useful when many updates to weights are + /// prevent refreshes from occurring immediately and is useful when many updates to weights are /// likely to be done before any are accessed again. void DeferRefresh() { needs_refresh = true; diff --git a/include/emp/datastructs/graph_utils.hpp b/include/emp/datastructs/graph_utils.hpp index ab5439d01b..76fd4235b3 100644 --- a/include/emp/datastructs/graph_utils.hpp +++ b/include/emp/datastructs/graph_utils.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file graph_utils.hpp + * @file * @brief This file provides a number of tools for manipulating graphs. * @note Status: BETA */ @@ -13,6 +14,7 @@ #include #include +#include #include #include diff --git a/include/emp/datastructs/hash_utils.hpp b/include/emp/datastructs/hash_utils.hpp index 363fd7f930..8e66f461e8 100644 --- a/include/emp/datastructs/hash_utils.hpp +++ b/include/emp/datastructs/hash_utils.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019-2022. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019-2021. - * - * @file hash_utils.hpp + * @file * @brief This file provides tools for hashing values and containers. * @note Status: BETA */ @@ -12,13 +13,14 @@ #define EMP_DATASTRUCTS_HASH_UTILS_HPP_INCLUDE #include +#include #include #include +#include #include #include #include "../base/Ptr.hpp" -#include "../polyfill/span.hpp" namespace emp { @@ -106,7 +108,7 @@ namespace emp { // helper functions for murmur hash #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace internal { - constexpr inline uint64_t rotate(const size_t x, const size_t r) noexcept { + constexpr inline uint64_t rotate(const uint64_t x, const uint64_t r) noexcept { return (x << r) | (x >> (64 - r)); } constexpr inline void fmix64(uint64_t& k) noexcept { @@ -127,13 +129,13 @@ namespace emp { /// @param key Span of bytes to hash. /// @param seed Optional seed. /// @return Hash of key. - constexpr inline size_t murmur_hash( + constexpr inline uint64_t murmur_hash( const std::span key, - const size_t seed = 0 + const uint64_t seed = 0 ) noexcept { // define constants - const size_t numbytes = key.size(); - const size_t nblocks = numbytes / 16; + const uint64_t numbytes = key.size(); + const uint64_t nblocks = numbytes / 16; const uint64_t c1 = 0x87c37b91114253d5LLU; const uint64_t c2 = 0x4cf5ad432745937fLLU; diff --git a/include/emp/datastructs/map_utils.hpp b/include/emp/datastructs/map_utils.hpp index 567317affc..9f97841274 100644 --- a/include/emp/datastructs/map_utils.hpp +++ b/include/emp/datastructs/map_utils.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file map_utils.hpp + * @file * @brief A set of simple functions to manipulate maps. * @note Status: BETA */ @@ -11,7 +12,10 @@ #ifndef EMP_DATASTRUCTS_MAP_UTILS_HPP_INCLUDE #define EMP_DATASTRUCTS_MAP_UTILS_HPP_INCLUDE +#include #include +#include +#include #include #include "../base/map.hpp" @@ -19,23 +23,73 @@ namespace emp { + template + std::string MapToString(const MAP_T & in_map) { + std::stringstream ss; + bool use_comma = false; + for (const auto & [key, value] : in_map) { + if (use_comma) ss << ","; + ss << "{" << key << ":" << value << "}"; + use_comma = true; + } + return ss.str(); + } + /// Take any map type, and run find to determine if a key is present. template inline bool Has( const MAP_T & in_map, const KEY_T & key ) { return in_map.find(key) != in_map.end(); } + // Check to see if any of the elements in a map satisfy a function. + template + bool AnyOf(const std::map & c, FUN_T fun) { + // If the provided function takes just the element type, that's all we should give it. + if constexpr (std::is_invocable_r()) { + return std::any_of(c.begin(), c.end(), [fun](auto x){ return fun(x.second); }); + } + + // Otherwise provide both key and element. + else { + return std::any_of(c.begin(), c.end(), [fun](auto x){ return fun(x.first, x.second); }); + } + } + + // Check to see if any of the elements in a map satisfy a function. + template + bool AllOf(const std::map & c, FUN_T fun) { + // If the provided function takes just the element type, that's all we should give it. + if constexpr (std::is_invocable_r()) { + return std::all_of(c.begin(), c.end(), [fun](auto x){ return fun(x.second); }); + } + + // Otherwise provide both key and element. + else { + return std::all_of(c.begin(), c.end(), [fun](auto x){ return fun(x.first, x.second); }); + } + } + + // Check to see if any of the elements in a map satisfy a function. + template + bool NoneOf(const std::map & c, FUN_T fun) { + // If the provided function takes just the element type, that's all we should give it. + if constexpr (std::is_invocable_r()) { + return std::none_of(c.begin(), c.end(), [fun](auto x){ return fun(x.second); }); + } + + // Otherwise provide both key and element. + else { + return std::none_of(c.begin(), c.end(), [fun](auto x){ return fun(x.first, x.second); }); + } + } template - inline auto Keys( const MAP_T & in_map) -> emp::vectorfirst)>::type> { - using KEY_T = typename std::remove_constfirst)>::type; - emp::vector keys; + inline auto Keys( const MAP_T & in_map) { + emp::vector keys; for (auto it : in_map) { keys.push_back(it.first); } - return keys; - } @@ -58,6 +112,15 @@ namespace emp { return val_it->second; } + /// Take any map and element, run find() member function, and return a reference to + /// the result found; trip assert if the result is not present. + template + inline const auto & GetConstRef( const MAP_T & in_map, const KEY_T & key) { + auto val_it = in_map.find(key); + emp_assert(val_it != in_map.end()); + return val_it->second; + } + // The following two functions are from: // http://stackoverflow.com/questions/5056645/sorting-stdmap-using-value diff --git a/include/emp/datastructs/ra_map.hpp b/include/emp/datastructs/ra_map.hpp new file mode 100644 index 0000000000..4702e6f662 --- /dev/null +++ b/include/emp/datastructs/ra_map.hpp @@ -0,0 +1,174 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2022 +*/ +/** + * @file + * @brief This file defines a Random Access Map template. + * @note Status: ALPHA + * + * A random access map allows for simple traversal by index and a guarantee that a value at a + * given index will always be at that index unless any map element is deleted. This allows + * storage of indices for maps with a fixed layout, resulting in easy access. + */ + +#ifndef EMP_DATASTRUCTS_RA_MAP_HPP_INCLUDE +#define EMP_DATASTRUCTS_RA_MAP_HPP_INCLUDE + +#include + +#include "../base/unordered_map.hpp" +#include "../base/vector.hpp" +#include "../math/constants.hpp" + +namespace emp { + + /// This class uses a combination of a hashtable (std::unordered_map) and emp::vector to + /// lookup insert, lookup, and delete values in constant time, while still being able to + /// step through all values (albeit in an arbitrary order). + /// + /// @note The arbitrary order of values may change if any values are deleted. + + template , + typename KeyEqual = std::equal_to, + typename Allocator = std::allocator< std::pair > + > + class ra_map { + public: + using key_type = KEY_T; + using mapped_type = T; + using value_type = std::pair; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using hasher = Hash; + using key_equal = KeyEqual; + using allocator_type = Allocator; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + + using layout_t = emp::unordered_map; + + private: + layout_t id_map; ///< Map to find keys in vector. + emp::vector vals; ///< Vector of all values. + + using this_t = ra_map; + public: + ra_map() = default; + ra_map(const ra_map &) = default; + ra_map(ra_map &&) = default; + this_t & operator=(const ra_map &) = default; + this_t & operator=(ra_map &&) = default; + + // -- Iterators -- + auto begin() { return vals.begin(); } + auto cbegin() const { return vals.cbegin(); } + auto end() { return vals.end(); } + auto cend() const { return vals.cend(); } + + // -- Capacity -- + size_t size() const { return vals.size(); } ///< Number of entries in map. + bool empty() const { return size() == 0; } ///< Are there NO values in map? + size_t max_size() const { return id_map.max_size(); } ///< Max system limit on size. + + // -- Modifiers -- + void clear() { id_map.clear(); vals.resize(0); } ///< Remove all values from container. + + /// Insert a new value into container by copy; return position. + size_t insert(const value_type & v) { + auto pos_it = id_map.find(v.first); + if (pos_it != id_map.end()) return pos_it->second; // Already in map. + const size_t pos = vals.size(); + id_map[v.first] = pos; + vals.emplace_back(v); + return pos; + } + + /// Insert a new value into container by move; return position. + size_t insert(value_type && v) { + auto pos_it = id_map.find(v.first); + if (pos_it != id_map.end()) return pos_it->second; // Already in map. + const size_t pos = vals.size(); + id_map[v.first] = pos; + vals.emplace_back(std::move(v)); + return pos; + } + + /// Construct a new value in place in a container container; return position. + template + size_t emplace(Ts &&... args) { + const size_t new_pos = vals.size(); + vals.emplace_back(std::forward(args)...); + auto old_pos_it = id_map.find(vals.back().first); + if (old_pos_it != id_map.end()) { + vals.resize(vals.size()-1); // Destroy newly created instance. + return old_pos_it->second; // Return old position in map. + } + id_map[vals.back().first] = new_pos; // Save new position for later lookup. + return new_pos; // And return it. + } + + /// Erase a specific value from the container. + bool erase(const KEY_T & key) { + if (!count(key)) return false; // Not in map. + + // Find out where key is in id_map and clear it. + const size_t pos = id_map[key]; + id_map.erase(key); + + // Move the former last value to the now-empty spot. + const size_t last_pos = vals.size() - 1; + if (pos != last_pos) { + const_cast(vals[pos].first) = vals[last_pos].first; + vals[pos].second = vals[last_pos].second; + id_map[vals[pos].first] = pos; + } + vals.resize(last_pos); + return true; + } + + + size_t count(const KEY_T & key) const { return id_map.count(key); } /// Is value included? (0 or 1). + + /// Index into the ra_map by key. + T & operator[](key_type key) { + auto key_it = id_map.find(key); + if (key_it == id_map.end()) { + return NewEntry(key); + } + return vals[key_it->second].second; + } + + // --- Empirical only commands --- + + const layout_t & GetLayout() const { return id_map; } + + T & NewEntry(key_type key) { + emp_assert(id_map.find(key) == id_map.end(), "ra_map::NewEntry must be an unused key!", key); + const size_t pos = vals.size(); + id_map[key] = pos; + vals.emplace_back(); + return vals.back().second; + } + + bool Has(key_type key) const { return id_map.find(key) != id_map.end(); } + + size_t GetID(key_type key) const { + auto key_it = id_map.find(key); + return (key_it == id_map.end()) ? emp::MAX_SIZE_T : key_it->second; + } + + key_type & KeyAtID(size_t id) { return vals[id]->first; } + + T & AtID(size_t id) { return vals[id]->second; } + const T & AtID(size_t id) const { return vals[id].second; } + }; + +} + +#endif // #ifndef EMP_DATASTRUCTS_RA_MAP_HPP_INCLUDE diff --git a/include/emp/datastructs/ra_set.hpp b/include/emp/datastructs/ra_set.hpp index 62aa079b17..2bf9d60297 100644 --- a/include/emp/datastructs/ra_set.hpp +++ b/include/emp/datastructs/ra_set.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2019 - * - * @file ra_set.hpp + * @file * @brief This file defines a Random Access Set template. * @note Status: ALPHA */ @@ -12,6 +13,7 @@ #define EMP_DATASTRUCTS_RA_SET_HPP_INCLUDE #include +#include #include "../base/map.hpp" #include "../base/vector.hpp" diff --git a/include/emp/datastructs/reference_vector.hpp b/include/emp/datastructs/reference_vector.hpp index 931b272380..72958d96ee 100644 --- a/include/emp/datastructs/reference_vector.hpp +++ b/include/emp/datastructs/reference_vector.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file reference_vector.hpp + * @file * @brief A version of vector that holds only references to objects. Be careful! * @note Status: ALPHA */ @@ -11,6 +12,8 @@ #ifndef EMP_DATASTRUCTS_REFERENCE_VECTOR_HPP_INCLUDE #define EMP_DATASTRUCTS_REFERENCE_VECTOR_HPP_INCLUDE +#include + #include "../base/Ptr.hpp" #include "../base/vector.hpp" diff --git a/include/emp/datastructs/set_utils.hpp b/include/emp/datastructs/set_utils.hpp index 40e82089b9..e95a22430e 100644 --- a/include/emp/datastructs/set_utils.hpp +++ b/include/emp/datastructs/set_utils.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file set_utils.hpp + * @file * @brief Tools to save and load data from classes. * @note Status: ALPHA */ @@ -38,7 +39,7 @@ namespace emp { template bool Has(const std::unordered_set & s, const V & val) { return s.count(val); } - /// Test if an std::unordere_multiset has a particular element without modifying the set in any way. + /// Test if an std::unordered_multiset has a particular element without modifying the set in any way. template bool Has(const std::unordered_multiset & s, const V & val) { return s.count(val); } @@ -46,7 +47,7 @@ namespace emp { // by reference and vectors are passed by value, because these functions only work // on sorted data. Therefore, vectors must be sorted first, which happens in place. - /// Compute the set difference of @param s1 and @param s2 (elements that are in S1 but no S2) + /// Compute the set difference of s1 and s2 (elements that are in S1 but no S2) template std::set difference(std::set & s1, std::set & s2) { // Based on PierreBdR's answer to https://stackoverflow.com/questions/283977/c-stl-set-difference @@ -56,7 +57,7 @@ namespace emp { return result; } - /// Compute the set difference of @param s1 and @param s2 (elements that are in S1 but no S2) + /// Compute the set difference of s1 and s2 (elements that are in S1 but no S2) template std::set difference(emp::vector s1, emp::vector s2) { // Based on PierreBdR's answer to https://stackoverflow.com/questions/283977/c-stl-set-difference @@ -69,7 +70,7 @@ namespace emp { return result; } - /// Compute the set difference of @param s1 and @param s2 (elements that are in S1 but not S2) + /// Compute the set difference of s1 and s2 (elements that are in S1 but not S2) template std::set difference(std::set & s1, emp::vector s2) { // Based on PierreBdR's answer to https://stackoverflow.com/questions/283977/c-stl-set-difference @@ -80,7 +81,7 @@ namespace emp { return result; } - /// Compute the set difference of @param s1 and @param s2 (elements that are in S1 but no S2) + /// Compute the set difference of s1 and s2 (elements that are in S1 but no S2) template std::set difference(emp::vector s1, std::set & s2) { // Based on PierreBdR's answer to https://stackoverflow.com/questions/283977/c-stl-set-difference @@ -91,7 +92,7 @@ namespace emp { return result; } - /// Compute the set intersection of @param s1 and @param s2 (elements that are in both S1 and S2) + /// Compute the set intersection of s1 and s2 (elements that are in both S1 and S2) template std::set intersection(std::set & s1, std::set & s2) { std::set result; @@ -100,7 +101,7 @@ namespace emp { return result; } - /// Compute the set intersection of @param s1 and @param s2 (elements that are in both S1 and S2) + /// Compute the set intersection of s1 and s2 (elements that are in both S1 and S2) template std::set intersection(emp::vector s1, emp::vector s2) { std::sort(s1.begin(), s1.end()); // set_intersection expects sorted things @@ -112,7 +113,7 @@ namespace emp { return result; } - /// Compute the set intersection of @param s1 and @param s2 (elements that are in both S1 and S2) + /// Compute the set intersection of s1 and s2 (elements that are in both S1 and S2) template std::set intersection(std::set & s1, emp::vector s2) { std::sort(s2.begin(), s2.end()); // set_intersection expects sorted things @@ -123,7 +124,7 @@ namespace emp { return result; } - /// Compute the set intersection of @param s1 and @param s2 (elements that are in both S1 and S2) + /// Compute the set intersection of s1 and s2 (elements that are in both S1 and S2) template std::set intersection(emp::vector s1, std::set & s2) { std::sort(s1.begin(), s1.end()); // set_intersection expects sorted things @@ -134,7 +135,7 @@ namespace emp { return result; } - /// Compute the set union of @param s1 and @param s2 (elements that are in either S1 or S2) + /// Compute the set union of s1 and s2 (elements that are in either S1 or S2) template std::set set_union(std::set & s1, std::set & s2) { std::set result; @@ -143,7 +144,7 @@ namespace emp { return result; } - /// Compute the set union of @param s1 and @param s2 (elements that are in either S1 or S2) + /// Compute the set union of s1 and s2 (elements that are in either S1 or S2) template std::set set_union(emp::vector s1, emp::vector s2) { std::sort(s1.begin(), s1.end()); // set_union expects sorted things @@ -155,7 +156,7 @@ namespace emp { return result; } - /// Compute the set union of @param s1 and @param s2 (elements that are in either S1 or S2) + /// Compute the set union of s1 and s2 (elements that are in either S1 or S2) template std::set set_union(std::set & s1, emp::vector s2) { std::sort(s2.begin(), s2.end()); // set_union expects sorted things @@ -166,7 +167,7 @@ namespace emp { return result; } - /// Compute the set union of @param s1 and @param s2 (elements that are in either S1 or S2) + /// Compute the set union of s1 and s2 (elements that are in either S1 or S2) template std::set set_union(emp::vector s1, std::set & s2) { std::sort(s1.begin(), s1.end()); // set_union expects sorted things @@ -177,7 +178,7 @@ namespace emp { return result; } - /// Compute the set symmetric_difference of @param s1 and @param s2 (elements that are in either S1 or S2 but not both) + /// Compute the set symmetric_difference of s1 and s2 (elements that are in either S1 or S2 but not both) template std::set symmetric_difference(std::set & s1, std::set & s2) { std::set result; @@ -186,7 +187,7 @@ namespace emp { return result; } - /// Compute the set symmetric_difference of @param s1 and @param s2 (elements that are in either S1 or S2 but not both) + /// Compute the set symmetric_difference of s1 and s2 (elements that are in either S1 or S2 but not both) template std::set symmetric_difference(emp::vector s1, emp::vector s2) { std::sort(s1.begin(), s1.end()); // set_symmetric_difference expects sorted things @@ -198,7 +199,7 @@ namespace emp { return result; } - /// Compute the set symmetric_difference of @param s1 and @param s2 (elements that are in either S1 or S2 but not both) + /// Compute the set symmetric_difference of s1 and s2 (elements that are in either S1 or S2 but not both) template std::set symmetric_difference(std::set & s1, emp::vector s2) { std::sort(s2.begin(), s2.end()); // set_symmetric_difference expects sorted things @@ -209,7 +210,7 @@ namespace emp { return result; } - /// Compute the set symmetric_difference of @param s1 and @param s2 (elements that are in either S1 or S2 but not both) + /// Compute the set symmetric_difference of s1 and s2 (elements that are in either S1 or S2 but not both) template std::set symmetric_difference(emp::vector s1, std::set & s2) { std::sort(s1.begin(), s1.end()); // set_symmetric_difference expects sorted things diff --git a/include/emp/datastructs/span_utils.hpp b/include/emp/datastructs/span_utils.hpp new file mode 100644 index 0000000000..642a85aee5 --- /dev/null +++ b/include/emp/datastructs/span_utils.hpp @@ -0,0 +1,58 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2022 +*/ +/** + * @file + * @brief A set of simple functions to manipulate std::span + * @note Status: BETA + * + */ + +#ifndef EMP_DATASTRUCTS_SPAN_UTILS_HPP_INCLUDE +#define EMP_DATASTRUCTS_SPAN_UTILS_HPP_INCLUDE + +#include +#include + +#include "../base/array.hpp" +#include "../base/vector.hpp" + +namespace emp { + + /// Print the contents of a span. + template + void Print(const std::span & v, std::ostream & os=std::cout, const std::string & spacer=" ") { + for (size_t id = 0; id < v.size(); id++) { + if (id) os << spacer; // Put a space before second element and beyond. + os << emp::to_string(v[id]); + } + } + + /// Convert an emp::array to an equivalent span + template + auto to_span(emp::array a) { return std::span(a); } + + /// Convert an emp::vector to an equivalent span + template + auto to_span(emp::vector v) { return std::span(v); } +} + +namespace std { + // A generic streaming function for spans. + template + std::ostream & operator<<(std::ostream & out, std::span s) { + emp::Print(s, out); + return out; + } + + template + std::istream & operator>>(std::istream & is, std::span s) { + for (T & x : s) is >> x; + return is; + } + +} + +#endif // #ifndef EMP_DATASTRUCTS_SPAN_UTILS_HPP_INCLUDE diff --git a/include/emp/datastructs/tuple_struct.hpp b/include/emp/datastructs/tuple_struct.hpp index 6906f30b79..793f5397b6 100644 --- a/include/emp/datastructs/tuple_struct.hpp +++ b/include/emp/datastructs/tuple_struct.hpp @@ -1,13 +1,14 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file tuple_struct.hpp + * @file * @brief These macros will build a tuple and accessors to that tuple's members inside of a * class definintion. * - * Status: ALPHA + * @note Status: ALPHA * * "But WHY???" you ask. Let me explain: Keeping a tuple allows us to easily track the * members in the stuct or class, and makes possible powerful types of reflection diff --git a/include/emp/datastructs/tuple_utils.hpp b/include/emp/datastructs/tuple_utils.hpp index 3f1e028009..7fe5f1a39b 100644 --- a/include/emp/datastructs/tuple_utils.hpp +++ b/include/emp/datastructs/tuple_utils.hpp @@ -1,17 +1,19 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021. - * - * @file tuple_utils.hpp + * @file * @brief Functions to simplify the use of std::tuple - * @note Status: RELEASE + * Status: RELEASE */ #ifndef EMP_DATASTRUCTS_TUPLE_UTILS_HPP_INCLUDE #define EMP_DATASTRUCTS_TUPLE_UTILS_HPP_INCLUDE #include +#include #include #include "../meta/ValPack.hpp" diff --git a/include/emp/datastructs/valsort_map.hpp b/include/emp/datastructs/valsort_map.hpp index 3faf29b382..d5f705db4b 100644 --- a/include/emp/datastructs/valsort_map.hpp +++ b/include/emp/datastructs/valsort_map.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file valsort_map.hpp + * @file * @brief This file defines a map that is sorted by value, not key. * @note Status: ALPHA * @@ -20,6 +21,7 @@ #include #include +#include #include "../base/vector.hpp" diff --git a/include/emp/datastructs/vector_utils.hpp b/include/emp/datastructs/vector_utils.hpp index d30f02de66..5b2cf7175a 100644 --- a/include/emp/datastructs/vector_utils.hpp +++ b/include/emp/datastructs/vector_utils.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2021. - * - * @file vector_utils.hpp + * @file * @brief A set of simple functions to manipulate emp::vector * @note Status: BETA * @@ -21,17 +22,35 @@ #include #include #include +#include #include "../base/vector.hpp" #include "../tools/string_utils.hpp" namespace emp { + /// Remove and return the first element of a vector. + template + T PopFront(emp::vector & v) { + emp_assert(v.size()); + T out = v[0]; + v.erase(v.begin()); + return out; + } + + /// Insert a value at a specified position in a vector. + template + void InsertAt(emp::vector & v, size_t id, T value) { + v.insert(v.begin()+id, value); + } + + #ifndef DOXYGEN_SHOULD_SKIP_THIS /// Base case for Append; we just have a single vector with nothing to append. template emp::vector & Append(emp::vector & base) { return base; } + #endif // DOXYGEN_SHOULD_SKIP_THIS /// Append one or more vectors on to the end of an existing vector. template @@ -50,8 +69,7 @@ namespace emp { return Append(base, vs...); } - - /// Concatonate two or more vectors together, creating a new vector. + /// Concatenate two or more vectors together, creating a new vector. template emp::vector Concat(const emp::vector & v1, const Vs &... vs) { emp::vector out_v(v1); @@ -61,13 +79,19 @@ namespace emp { /// Convert a map to a vector. template - emp::vector ToVector(const std::map & in_map, T default_val=T()) { + emp::vector ToVector( + const std::map & in_map, + T default_val=T(), + INDEX_T index_cap=32768 + ) { INDEX_T max_index = in_map.back().second; if (max_index < 0) max_index = 0; // In case all entries are negative... + if (max_index >= index_cap) max_index=index_cap-1; emp::vector out_vec; out_vec.resize(max_index+1, default_val); for (auto [index, val] : in_map) { - if (index < 0) continue; // Skip entries that can't go into a vector... + if (index < 0) continue; // Skip entries that can't go into a vector... + if (index >= index_cap) break; // Stop when we've hit the upper limit on vector size. out_vec[index] = val; } return out_vec; @@ -75,10 +99,14 @@ namespace emp { /// Convert an unordered map to a vector. template - emp::vector ToVector(const std::unordered_map & in_map, T default_val=T()) { + emp::vector ToVector( + const std::unordered_map & in_map, + T default_val=T(), + INDEX_T index_cap=32768 + ) { emp::vector out_vec; for (auto [index, val] : in_map) { - if (index < 0) continue; // Skip entries that can't go into a vector... + if (index < 0 || index >= index_cap) continue; // Skip entries that can't go into a vector... if (((size_t) index) >= out_vec.size()) out_vec.resize(index+1, default_val); out_vec[index] = val; } @@ -123,6 +151,29 @@ namespace emp { return true; } + /// Remove value at an index. + template + void RemoveAt(emp::vector & v, size_t id) { + v.erase(v.begin() + id); + } + + /// Remove values starting at an index. + template + void RemoveAt(emp::vector & v, size_t id, size_t count) { + if (!count) return; + v.erase(v.begin() + id, v.begin() + id + count); + } + + /// Return a new vector containing the same elements as @param v + /// with any duplicate elements removed. + /// Not guaranteed to preserve order + template + emp::vector RemoveDuplicates(const emp::vector & v) { + std::set temp_set(v.begin(), v.end()); + emp::vector new_vec(temp_set.begin(), temp_set.end()); + return new_vec; + } + /// Return whether a value exists in a vector template bool Has(const emp::vector & v, const T & val) { @@ -294,16 +345,6 @@ namespace emp { return numbers; } - /// Return a new vector containing the same elements as @param v - /// with any duplicate elements removed. - /// Not guaranteed to preserve order - template - emp::vector RemoveDuplicates(const emp::vector & v) { - std::set temp_set(v.begin(), v.end()); - emp::vector new_vec(temp_set.begin(), temp_set.end()); - return new_vec; - } - /// Build a vector with a range of values from min to max at the provided step size. template static inline emp::vector BuildRange(T min, T max, T step=1) { diff --git a/include/emp/debug/alert.hpp b/include/emp/debug/alert.hpp index 70be769cd2..3dc5e5c4a1 100644 --- a/include/emp/debug/alert.hpp +++ b/include/emp/debug/alert.hpp @@ -1,16 +1,19 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2017 - * - * @file alert.hpp + * @file * @brief Define an Alert function that goes to std::cerr in c++ or to Alert() in Javascript. - * @note Status: RELEASE + * Status: RELEASE */ #ifndef EMP_DEBUG_ALERT_HPP_INCLUDE #define EMP_DEBUG_ALERT_HPP_INCLUDE +#include + #include "../tools/string_utils.hpp" // If we are in emscripten, make sure to include the header. diff --git a/include/emp/debug/debug.hpp b/include/emp/debug/debug.hpp index 8fae099a1b..362c8692eb 100644 --- a/include/emp/debug/debug.hpp +++ b/include/emp/debug/debug.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2022 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2017 - * - * @file debug.hpp + * @file * @brief Basic tools for use in developing high-assurance code. * @note Status: BETA */ @@ -20,7 +21,7 @@ namespace emp { - /// BlockRelease() will halt compilation if NDEBUG is on and EMP_NO_BLOCK is off. + /// BlockRelease(true) will halt compilation if NDEBUG is on and EMP_NO_BLOCK is off. /// It is useful to include alongside debug code that you want to remember to remove when you /// are done debugging; it is automatically included with the emp_debug() function below. /// If you want to intentionally compile in release mode, make sure to define EMP_NO_BLOCK. @@ -28,17 +29,22 @@ namespace emp { #ifdef EMP_NO_BLOCK #define BlockRelease(BLOCK) #else - #define BlockRelease(BLOCK) static_assert(!BLOCK, "Release blocked due to debug material.") + #define BlockRelease(BLOCK) \\ + std::cerr << "Release block at " << __FILE___ << ", line " << __LINE__ << std::endl;\\ + static_assert(!BLOCK, "Release blocked due to debug material.") #endif #else #define BlockRelease(BLOCK) #endif /// The EMP_DEBUG macro executes its contents in debug mode, but otherwise ignores them. + /// test_debug() can be used inside of an if-constexpr for code you want only in debug mode. #ifdef NDEBUG -#define EMP_DEBUG(...) + #define EMP_DEBUG(...) + constexpr bool test_debug() { return false; } #else -#define EMP_DEBUG(...) __VA_ARGS__ + #define EMP_DEBUG(...) __VA_ARGS__ + constexpr bool test_debug() { return true; } #endif template @@ -47,19 +53,10 @@ namespace emp { std::cerr << std::endl; } - /// emp_debug() will print its contents as a message in debug mode and BLOCK release mode until it's removed. - #define emp_debug(...) BlockRelease(true); emp::emp_debug_print(__VA_ARGS__); - - /// Depricated() prints its contents exactly once to notify a user of a depricated function. - static void Depricated(const std::string & name, const std::string & desc="") { - static std::set name_set; - if (name_set.count(name) == 0) { - std::cerr << "Deprication WARNING: " << name << std::endl; - if (desc != "") std::cerr << desc << std::endl; - name_set.insert(name); - } - } - + /// emp_debug() will print its contents as a message in debug mode and BLOCK release mode until + /// it is removed. It's a useful too for printing "Ping1", "Ping2", etc, but no forgetting to + /// remove them. + #define emp_debug(...) { BlockRelease(true); emp::emp_debug_print(__VA_ARGS__); } } #endif // #ifndef EMP_DEBUG_DEBUG_HPP_INCLUDE diff --git a/include/emp/debug/mem_track.hpp b/include/emp/debug/mem_track.hpp index e2a96da6bf..802a1ec545 100644 --- a/include/emp/debug/mem_track.hpp +++ b/include/emp/debug/mem_track.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2019 - * - * @file mem_track.hpp + * @file * @brief A set of macros to track how many instances of specific classes are made. * @note Status: BETA * diff --git a/include/emp/functional/AnyFunction.hpp b/include/emp/functional/AnyFunction.hpp index 63ac2d5ba6..52b82a451f 100644 --- a/include/emp/functional/AnyFunction.hpp +++ b/include/emp/functional/AnyFunction.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file AnyFunction.hpp + * @file * @brief Based on std::function, but with a generic base class. * @note Status: ALPHA * @@ -14,6 +15,7 @@ #define EMP_FUNCTIONAL_ANYFUNCTION_HPP_INCLUDE #include +#include #include "../base/assert.hpp" #include "../base/Ptr.hpp" @@ -50,6 +52,8 @@ namespace emp { /// Determine if this BaseFunction can be converted into a derived emp::Function template bool ConvertOK(); + + virtual emp::Ptr Clone() = 0; }; @@ -80,6 +84,10 @@ namespace emp { /// Get the std::function to be called. const fun_t & GetFunction() const { return fun; } + + emp::Ptr Clone() override{ + return emp::NewPtr>(fun); + } }; @@ -88,6 +96,7 @@ namespace emp { private: emp::Ptr fun = nullptr; + private: /// Helper to build a proper derived function. template auto MakePtr( T in_fun ) { @@ -101,6 +110,29 @@ namespace emp { // By default, build an empty function. AnyFunction() { ; } + AnyFunction(const AnyFunction& other){ // copy constructor + fun = other.CloneFunc(); + } + + AnyFunction(AnyFunction&& other) noexcept{ // move constructor + fun = other.CloneFunc(); + other.fun.Delete(); + other.fun = nullptr; + } + + AnyFunction& operator=(const AnyFunction& other){ // copy assignment + Clear(); + fun = other.CloneFunc(); + return *this; + } + + AnyFunction& operator=(AnyFunction&& other) noexcept{ // move assignment + Clear(); + fun = other.CloneFunc(); + other.Clear(); + return *this; + } + /// If an argument is provided, set the function. template AnyFunction(T in_fun) { @@ -111,6 +143,10 @@ namespace emp { void Clear() { if (fun) fun.Delete(); fun = nullptr; } size_t NumArgs() const { return fun ? fun->NumArgs() : 0; } + emp::Ptr CloneFunc() const{ + if(fun == nullptr) return nullptr; + return fun->Clone(); + } operator bool() { return (bool) fun; } @@ -163,7 +199,7 @@ namespace emp { } }; - + #ifndef DOXYGEN_SHOULD_SKIP_THIS ///////////////////////////////////// // Member function implementaions. @@ -197,6 +233,7 @@ namespace emp { template bool BaseFunction::ConvertOK() { return dynamic_cast *>(this); } + #endif // DOXYGEN_SHOULD_SKIP_THIS } diff --git a/include/emp/functional/FunctionSet.hpp b/include/emp/functional/FunctionSet.hpp index 46d5c1fce8..c2af5ba8fc 100644 --- a/include/emp/functional/FunctionSet.hpp +++ b/include/emp/functional/FunctionSet.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file FunctionSet.hpp + * @file * @brief Setup a collection of functions, all with the same signature, that can be run as a group. * @note Status: BETA */ @@ -12,6 +13,7 @@ #define EMP_FUNCTIONAL_FUNCTIONSET_HPP_INCLUDE #include +#include #include "../base/vector.hpp" diff --git a/include/emp/functional/GenericFunction.hpp b/include/emp/functional/GenericFunction.hpp index e8e3240aad..d0d62ecefa 100644 --- a/include/emp/functional/GenericFunction.hpp +++ b/include/emp/functional/GenericFunction.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2019 - * - * @file GenericFunction.hpp + * @file * @brief Based on std::function, but with a common base class. * @note Status: ALPHA * @@ -15,6 +16,7 @@ #define EMP_FUNCTIONAL_GENERICFUNCTION_HPP_INCLUDE #include +#include #include "../base/assert.hpp" @@ -83,6 +85,7 @@ namespace emp { const fun_t & GetFunction() const { return fun; } }; + #ifndef DOXYGEN_SHOULD_SKIP_THIS ///////////////////////////////////// // Member function implementaions. @@ -116,7 +119,7 @@ namespace emp { template bool GenericFunction::ConvertOK() { return dynamic_cast *>(this); } - + #endif // DOXYGEN_SHOULD_SKIP_THIS } #endif // #ifndef EMP_FUNCTIONAL_GENERICFUNCTION_HPP_INCLUDE diff --git a/include/emp/functional/flex_function.hpp b/include/emp/functional/flex_function.hpp index 191d24273a..25a17aec60 100644 --- a/include/emp/functional/flex_function.hpp +++ b/include/emp/functional/flex_function.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file flex_function.hpp + * @file * @brief Based on std::function, but holds default parameter values for calls with fewer args. * @note Status: ALPHA */ @@ -13,6 +14,7 @@ #include +#include #include #include "../base/assert.hpp" diff --git a/include/emp/functional/memo_function.hpp b/include/emp/functional/memo_function.hpp index e802cafe54..f096c9f27c 100644 --- a/include/emp/functional/memo_function.hpp +++ b/include/emp/functional/memo_function.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2019 - * - * @file memo_function.hpp + * @file * @brief A function that memorizes previous results to speed up any repeated calls. * @note Status: BETA */ @@ -11,6 +12,7 @@ #ifndef EMP_FUNCTIONAL_MEMO_FUNCTION_HPP_INCLUDE #define EMP_FUNCTIONAL_MEMO_FUNCTION_HPP_INCLUDE +#include #include #include "../base/assert.hpp" diff --git a/include/emp/games/Mancala.hpp b/include/emp/games/Mancala.hpp index b3518374af..241596cb4b 100644 --- a/include/emp/games/Mancala.hpp +++ b/include/emp/games/Mancala.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021 - * - * @file Mancala.hpp + * @file * @brief A simple Malcala game state handler. */ @@ -13,6 +14,7 @@ #include #include #include +#include #include #include "../base/array.hpp" @@ -28,8 +30,9 @@ namespace emp { side_t boardA; // Current board state for side A. side_t boardB; // Current board state for side B. + size_t turn_count; // How many turns has this game been played? bool over = false; // Has the game ended? - size_t is_A_turn; // Which player goes next? + bool is_A_turn; // Which player goes next? void TestOver() { bool side_A_empty = true; @@ -46,7 +49,7 @@ namespace emp { public: using move_t = size_t; - Mancala(bool A_first=true) : boardA(), boardB(), over(false), is_A_turn(true) { + Mancala(bool A_first=true) : boardA(), boardB(), turn_count(0), over(false), is_A_turn(true) { Reset(A_first); } ~Mancala() { ; } @@ -54,6 +57,7 @@ namespace emp { void Reset(bool A_first=true) { for (size_t i = 0; i < 6; i++) { boardA[i] = 4; boardB[i] = 4; } boardA[6] = boardB[6] = 0; + turn_count = 0; over = false; is_A_turn = A_first; } @@ -94,12 +98,14 @@ namespace emp { // Returns bool indicating whether player can go again bool DoMove(move_t cell) { - emp_assert(cell < 6); // You cannot choose a cell out of bounds. + emp_assert(cell < 6); // Make sure move is not out of bounds. - side_t & cur_board = GetCurSide(); + turn_count++; // Maintain count of moves. + + side_t & cur_board = GetCurSide(); // Load in board view based on current player. side_t & other_board = GetOtherSide(); - emp_assert(cur_board[cell] != 0); // You cannot choose an empty cell. + emp_assert(cur_board[cell] != 0); // Make sure move is not an empty pit. size_t stone_count = cur_board[cell]; size_t cur_cell = cell; diff --git a/include/emp/games/Othello.hpp b/include/emp/games/Othello.hpp index a3d17530a3..c803836622 100644 --- a/include/emp/games/Othello.hpp +++ b/include/emp/games/Othello.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Othello.hpp + * @file * @brief A simple Othello game state handler. * * @todo Add Hash for boards to be able to cachce moves. @@ -16,6 +17,7 @@ #include #include #include +#include #include #include "../base/array.hpp" diff --git a/include/emp/games/Othello8.hpp b/include/emp/games/Othello8.hpp index 93d5d22574..d9b641e5de 100644 --- a/include/emp/games/Othello8.hpp +++ b/include/emp/games/Othello8.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Othello8.hpp + * @file * @brief A simple Othello game state handler limited to an 8x8 board. * * @todo Add Hash for boards to be able to cachce moves. @@ -13,9 +14,11 @@ #ifndef EMP_GAMES_OTHELLO8_HPP_INCLUDE #define EMP_GAMES_OTHELLO8_HPP_INCLUDE +#include #include #include #include +#include #include #include "../base/array.hpp" @@ -49,11 +52,11 @@ namespace emp { constexpr Index(size_t x, size_t y) : pos() { Set(x,y); } constexpr Index(const Index & _in) : pos(_in.pos) { emp_assert(pos <= NUM_CELLS); } - operator size_t() const { return pos; } - size_t x() const { return pos & 7; } - size_t y() const { return pos >> 3; } - void Set(size_t x, size_t y) { pos = (x> 3; } + constexpr void Set(size_t x, size_t y) { pos = (x #include #include "../base/array.hpp" diff --git a/include/emp/geometry/Angle2D.hpp b/include/emp/geometry/Angle2D.hpp index b0b62914ff..83c878611b 100644 --- a/include/emp/geometry/Angle2D.hpp +++ b/include/emp/geometry/Angle2D.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file Angle2D.hpp + * @file * @brief emp::Angle maintains an angle on a 2D surface. * * The internal representation uses an int to represent angles. @@ -15,6 +16,7 @@ #define EMP_GEOMETRY_ANGLE2D_HPP_INCLUDE #include +#include #include "../math/constants.hpp" diff --git a/include/emp/geometry/Body2D.hpp b/include/emp/geometry/Body2D.hpp index 553228aefa..6538b07671 100644 --- a/include/emp/geometry/Body2D.hpp +++ b/include/emp/geometry/Body2D.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file Body2D.hpp + * @file * @brief This file defines classes to represent bodies that exist on a 2D surface. * * Each class should be able to: @@ -23,6 +24,9 @@ #ifndef EMP_GEOMETRY_BODY2D_HPP_INCLUDE #define EMP_GEOMETRY_BODY2D_HPP_INCLUDE +#include +#include + #include "../base/assert.hpp" #include "../base/Ptr.hpp" #include "../base/vector.hpp" diff --git a/include/emp/geometry/Circle2D.hpp b/include/emp/geometry/Circle2D.hpp index 84801902e5..786c8f43be 100644 --- a/include/emp/geometry/Circle2D.hpp +++ b/include/emp/geometry/Circle2D.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file Circle2D.hpp + * @file * @brief A class to manage circles in a 2D plane. */ diff --git a/include/emp/geometry/Physics2D.hpp b/include/emp/geometry/Physics2D.hpp index 9fe311607d..63e6eb762d 100644 --- a/include/emp/geometry/Physics2D.hpp +++ b/include/emp/geometry/Physics2D.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file Physics2D.hpp + * @file * @brief Physics2D - handles movement and collissions in a simple 2D world. * */ @@ -12,6 +13,7 @@ #define EMP_GEOMETRY_PHYSICS2D_HPP_INCLUDE #include +#include #include #include "../base/Ptr.hpp" diff --git a/include/emp/geometry/Point2D.hpp b/include/emp/geometry/Point2D.hpp index cc09e0222b..bc30faf6b9 100644 --- a/include/emp/geometry/Point2D.hpp +++ b/include/emp/geometry/Point2D.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file Point2D.hpp + * @file * @brief A simple class to track value pairs of any kind, optimized for points in 2D Space. * * @note For maximal efficiency, prefer SquareMagnitude() and SquareDistance() @@ -108,6 +109,7 @@ namespace emp { } +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace std { // Overload ostream to work with points. template std::ostream & operator<<(std::ostream & os, @@ -115,5 +117,6 @@ namespace std { return os << "(" << point.GetX() << "," << point.GetY() << ")"; } } +#endif // DOXYGEN_SHOULD_SKIP_THIS #endif // #ifndef EMP_GEOMETRY_POINT2D_HPP_INCLUDE diff --git a/include/emp/geometry/Surface.hpp b/include/emp/geometry/Surface.hpp index decf6d596e..be056e5cd0 100644 --- a/include/emp/geometry/Surface.hpp +++ b/include/emp/geometry/Surface.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file Surface.hpp + * @file * @brief This file defines a templated class to represent a 2D suface capable of maintaining data * about which 2D bodies are currently on that surface and rapidly identifying if they are * overlapping. @@ -20,6 +21,7 @@ #include #include +#include #include "../base/Ptr.hpp" #include "../datastructs/vector_utils.hpp" diff --git a/include/emp/geometry/Surface2D.hpp b/include/emp/geometry/Surface2D.hpp index 293ae88902..5e4d978cf0 100644 --- a/include/emp/geometry/Surface2D.hpp +++ b/include/emp/geometry/Surface2D.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file Surface2D.hpp + * @file * @brief This file defines a templated class to represent a 2D suface capable of maintaining data * about which 2D bodies are currently on that surface and rapidly identifying if they are * overlapping. diff --git a/include/emp/hardware/AvidaCPU_InstLib.hpp b/include/emp/hardware/AvidaCPU_InstLib.hpp index ca00e09969..8c5e26754d 100644 --- a/include/emp/hardware/AvidaCPU_InstLib.hpp +++ b/include/emp/hardware/AvidaCPU_InstLib.hpp @@ -1,24 +1,25 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file AvidaCPU_InstLib.hpp + * @file * @brief A specialized version of InstLib to handle AvidaCPU Instructions. */ #ifndef EMP_HARDWARE_AVIDACPU_INSTLIB_HPP_INCLUDE #define EMP_HARDWARE_AVIDACPU_INSTLIB_HPP_INCLUDE +#include + #include "../math/math.hpp" #include "InstLib.hpp" namespace emp { - /// AvidaCPU_InstLib is a pure-virtual class that defines a series of instructions that /// can be used with AvidaCPU_Base or any of its derived classes. - template struct AvidaCPU_InstLib : public InstLib { using hardware_t = HARDWARE_T; diff --git a/include/emp/hardware/AvidaGP.hpp b/include/emp/hardware/AvidaGP.hpp index 2ced1ebf7b..549d15085b 100644 --- a/include/emp/hardware/AvidaGP.hpp +++ b/include/emp/hardware/AvidaGP.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2021. - * - * @file AvidaGP.hpp + * @file * @brief This is a simple, efficient CPU for and applied version of Avida. * * @todo Should we save a copy of the original genome? (or create a new "memory" member) @@ -22,6 +23,7 @@ #include #include #include +#include #include "../base/array.hpp" #include "../base/Ptr.hpp" @@ -56,7 +58,7 @@ namespace emp { using stack_t = emp::vector; using arg_set_t = emp::array; - struct Instruction { + struct Instruction : public inst_lib_t::InstructionBase { size_t id; arg_set_t args; @@ -68,7 +70,7 @@ namespace emp { Instruction & operator=(const Instruction &) = default; Instruction & operator=(Instruction &&) = default; bool operator<(const Instruction & in) const { - return std::tie(id, args) < std::tie(in.id, in.args); + return (id == in.id) ? (args < in.args) : (id < in.id); } bool operator==(const Instruction & in) const { return id == in.id && args == in.args; } bool operator!=(const Instruction & in) const { return !(*this == in); } @@ -78,6 +80,10 @@ namespace emp { void Set(size_t _id, size_t _a0=0, size_t _a1=0, size_t _a2=0) { id = _id; args[0] = _a0; args[1] = _a1; args[2] = _a2; } + + size_t GetIndex() const override{ + return id; + } }; struct ScopeInfo { @@ -483,6 +489,7 @@ namespace emp { of.close(); } + #ifndef DOXYGEN_SHOULD_SKIP_THIS template void AvidaCPU_Base::PrintSymbols(std::ostream & os) const { // Example output: t(12)u()b(A5C)m(8) @@ -499,6 +506,7 @@ namespace emp { } os << '\n'; } + #endif // DOXYGEN_SHOULD_SKIP_THIS template size_t AvidaCPU_Base::PredictNextInst() const { @@ -532,6 +540,7 @@ namespace emp { return inst_ptr; } + #ifndef DOXYGEN_SHOULD_SKIP_THIS template void AvidaCPU_Base::PrintState(std::ostream & os) const { size_t next_inst = PredictNextInst(); @@ -563,6 +572,7 @@ namespace emp { // emp::vector reg_stack; // emp::vector call_stack; } + #endif // DOXYGEN_SHOULD_SKIP_THIS class AvidaGP : public AvidaCPU_Base { public: @@ -582,6 +592,7 @@ namespace emp { }; } +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace std { /// operator<< to work with ostream (must be in std to work) @@ -590,5 +601,6 @@ namespace std { return out; } } +#endif // DOXYGEN_SHOULD_SKIP_THIS #endif // #ifndef EMP_HARDWARE_AVIDAGP_HPP_INCLUDE diff --git a/include/emp/hardware/BitSorter.hpp b/include/emp/hardware/BitSorter.hpp index 746a4eab48..6d58c0e9f8 100644 --- a/include/emp/hardware/BitSorter.hpp +++ b/include/emp/hardware/BitSorter.hpp @@ -1,15 +1,18 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file BitSorter.hpp + * @file * @brief A quick series of comparisons intended for sorting bits. */ #ifndef EMP_HARDWARE_BITSORTER_HPP_INCLUDE #define EMP_HARDWARE_BITSORTER_HPP_INCLUDE +#include +#include #include #include "../base/vector.hpp" @@ -20,7 +23,7 @@ namespace emp { class BitSorter { public: - using bits_t = uint32_t; ///< Type used to represent pairs if posisions as bit masks. + using bits_t = uint32_t; ///< Type used to represent pairs if positions as bit masks. protected: emp::vector compare_set; ///< Comparators, in order (pairs of 1's in bitstring) @@ -183,6 +186,7 @@ namespace emp { } +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace std { /// operator<< to work with ostream (must be in std to work) @@ -191,5 +195,6 @@ namespace std { return out; } } +#endif // DOXYGEN_SHOULD_SKIP_THIS #endif // #ifndef EMP_HARDWARE_BITSORTER_HPP_INCLUDE diff --git a/include/emp/hardware/EventDrivenGP.hpp b/include/emp/hardware/EventDrivenGP.hpp index 1bb670e1d0..1931bf5904 100644 --- a/include/emp/hardware/EventDrivenGP.hpp +++ b/include/emp/hardware/EventDrivenGP.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file EventDrivenGP.hpp + * @file * @brief TODO. */ @@ -14,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -363,6 +365,7 @@ namespace emp { CEREAL_NVP(id) ); } + size_t GetIndex() const{ return id; } }; @@ -391,7 +394,10 @@ namespace emp { Function( const affinity_t & _aff=affinity_t(), const inst_seq_t & _seq=inst_seq_t(), - std::function _fun_matchbin_refresh=[](){} + std::function _fun_matchbin_refresh + #ifndef DOXYGEN_SHOULD_SKIP_THIS + =[](){} + #endif // DOXYGEN_SHOULD_SKIP_THIS ) : affinity(_aff) , inst_seq(_seq) , fun_matchbin_refresh(_fun_matchbin_refresh) @@ -818,7 +824,9 @@ namespace emp { size_t exec_core_id; //< core ID of the currently executing core. bool is_executing; //< True when mid-execution of all cores. (On every CPU cycle: execute all cores). MATCHBIN_T matchBin; + #ifndef DOXYGEN_SHOULD_SKIP_THIS trait_printer_t fun_trait_print = [](std::ostream& os, TRAIT_T){os << "UNCONFIGURED TRAIT PRINT FUNCTION\n";}; + #endif // DOXYGEN_SHOULD_SKIP_THIS // TODO: disallow configuration of hardware while executing. (and any other functions that could sent things into a bad state) diff --git a/include/emp/hardware/EventLib.hpp b/include/emp/hardware/EventLib.hpp index 4fd9db33e1..47a36e2e56 100644 --- a/include/emp/hardware/EventLib.hpp +++ b/include/emp/hardware/EventLib.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file EventLib.hpp + * @file * @brief This file maintains information about events available in virtual hardware. * * This file is largely based on InstLib.h. @@ -15,6 +16,7 @@ #include #include #include +#include #include #include diff --git a/include/emp/hardware/Genome.hpp b/include/emp/hardware/Genome.hpp index ad35313162..bc2fcd3414 100644 --- a/include/emp/hardware/Genome.hpp +++ b/include/emp/hardware/Genome.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021. - * - * @file Genome.hpp + * @file * @brief This is a simple, container for a series of instructions. * */ @@ -11,10 +12,10 @@ #ifndef EMP_HARDWARE_GENOME_HPP_INCLUDE #define EMP_HARDWARE_GENOME_HPP_INCLUDE -#include "../base/Ptr.hpp" - +#include #include "../base/array.hpp" +#include "../base/Ptr.hpp" #include "../base/vector.hpp" #include "../datastructs/map_utils.hpp" #include "../io/File.hpp" diff --git a/include/emp/hardware/InstLib.hpp b/include/emp/hardware/InstLib.hpp index fb4f40cedf..c0e8116f2e 100644 --- a/include/emp/hardware/InstLib.hpp +++ b/include/emp/hardware/InstLib.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2021. - * - * @file InstLib.hpp + * @file * @brief This file maintains information about instructions availabel in virtual hardware. */ @@ -11,6 +12,7 @@ #define EMP_HARDWARE_INSTLIB_HPP_INCLUDE #include +#include #include #include @@ -22,14 +24,15 @@ namespace emp { + /// ScopeType is used for scopes that we need to do something special at the end. /// Eg: LOOP needs to go back to beginning of loop; FUNCTION needs to return to call. enum class ScopeType { NONE=0, ROOT, BASIC, LOOP, FUNCTION }; /// @brief InstLib maintains a set of instructions for use in virtual hardware. - /// @param HARDWARE_T Type of the virtual hardware class to track instructions. - /// @param ARG_T What types of arguments are associated with instructions. - /// @param ARG_COUNT Max number of arguments on an instruction. + /// @tparam HARDWARE_T Type of the virtual hardware class to track instructions. + /// @tparam ARG_T What types of arguments are associated with instructions. + /// @tparam ARG_COUNT Max number of arguments on an instruction. template class InstLib { public: @@ -40,7 +43,14 @@ namespace emp { using fun_t = std::function; using inst_properties_t = std::unordered_set; + struct InstructionBase{ + virtual ~InstructionBase() {;} + virtual size_t GetIndex() const = 0; + }; + struct InstDef { + size_t index; + size_t id; std::string name; ///< Name of this instruction. fun_t fun_call; ///< Function to call when executing. size_t num_args; ///< Number of args needed by function. @@ -50,11 +60,11 @@ namespace emp { inst_properties_t properties; ///< Are there any generic properties associated with this inst def? char symbol; ///< Unique symbol for this instruction. - InstDef(const std::string & _n, fun_t _fun, size_t _args, const std::string & _d, - ScopeType _s_type, size_t _s_arg, + InstDef(size_t _idx, size_t _id, const std::string & _n, fun_t _fun, size_t _args, + const std::string & _d, ScopeType _s_type, size_t _s_arg, const inst_properties_t & _properties = inst_properties_t(), char _sym='?') - : name(_n), fun_call(_fun), num_args(_args), desc(_d) + : index(_idx), id(_id), name(_n), fun_call(_fun), num_args(_args), desc(_d) , scope_type(_s_type), scope_arg(_s_arg), properties(_properties), symbol(_sym) { ; } InstDef(const InstDef &) = default; }; @@ -63,6 +73,7 @@ namespace emp { emp::vector inst_lib; ///< Full definitions for instructions. emp::vector inst_funs; ///< Map of instruction IDs to their functions. std::map name_map; ///< How do names link to instructions? + std::map id_map; ///< How do identifiers link to instructions? std::map arg_map; ///< How are different arguments named? /// Symbols to use when representing individual instructions (80). @@ -71,59 +82,84 @@ namespace emp { emp::array symbol_map; ///< Map of symbols back to instruction IDs. public: - InstLib() : inst_lib(), inst_funs(), name_map(), arg_map() { ; } ///< Default Constructor + InstLib() : inst_lib(), inst_funs(), name_map(), id_map(), arg_map() { ; } ///< Default Constructor InstLib(const InstLib &) = delete; ///< Copy Constructor InstLib(InstLib &&) = delete; ///< Move Constructor - ~InstLib() { ; } ///< Destructor + virtual ~InstLib() { ; } ///< Destructor - InstLib & operator=(const InstLib &) = default; ///< Copy Operator - InstLib & operator=(InstLib &&) = default; ///< Move Operator + InstLib & operator=(const InstLib &) = default; ///< Copy Operator + InstLib & operator=(InstLib &&) = default; ///< Move Operator /// Return the name associated with the specified instruction ID. - const std::string & GetName(size_t id) const { return inst_lib[id].name; } + const std::string & GetName(size_t idx) const { return inst_lib[idx].name; } /// Return the function associated with the specified instruction ID. - const fun_t & GetFunction(size_t id) const { return inst_lib[id].fun_call; } + const fun_t & GetFunction(size_t idx) const { return inst_lib[idx].fun_call; } /// Return the number of arguments expected for the specified instruction ID. - size_t GetNumArgs(size_t id) const { return inst_lib[id].num_args; } + size_t GetNumArgs(size_t idx) const { return inst_lib[idx].num_args; } - /// Return the provided description for the provided instruction ID. - const std::string & GetDesc(size_t id) const { return inst_lib[id].desc; } + /// Return the provided description for the providxed instruction ID. + const std::string & GetDesc(size_t idx) const { return inst_lib[idx].desc; } /// What type of scope does this instruction state? ScopeType::NONE is default. - ScopeType GetScopeType(size_t id) const { return inst_lib[id].scope_type; } + ScopeType GetScopeType(size_t idx) const { return inst_lib[idx].scope_type; } - /// If this instruction alters scope, identify which argument does so. - size_t GetScopeArg(size_t id) const { return inst_lib[id].scope_arg; } + /// If this instruction alters scope, idxentify which argument does so. + size_t GetScopeArg(size_t idx) const { return inst_lib[idx].scope_arg; } - /// Return the set of properties for the provided instruction ID. - const inst_properties_t & GetProperties(size_t id) const { return inst_lib[id].properties; } + /// Return the set of properties for the providxed instruction ID. + const inst_properties_t & GetProperties(size_t idx) const { + return inst_lib[idx].properties; + } - char GetSymbol(size_t id) const { return inst_lib[id].symbol; } + char GetSymbol(size_t idx) const { return inst_lib[idx].symbol; } /// Does the given instruction ID have the given property value? - bool HasProperty(size_t id, std::string property) const { return inst_lib[id].properties.count(property); } + bool HasProperty(size_t idx, std::string property) const { + return inst_lib[idx].properties.count(property); + } /// Get the number of instructions in this set. size_t GetSize() const { return inst_lib.size(); } + /// Returns boolean indicating whether the given + /// string is a valid instruction. bool IsInst(const std::string name) const { return Has(name_map, name); } + size_t GetID(const size_t idx) const { + return inst_lib[idx].id; + } /// Return the ID of the instruction that has the specified name. size_t GetID(const std::string & name) const { emp_assert(Has(name_map, name), name); - return Find(name_map, name, (size_t) -1); + return inst_lib[Find(name_map, name, (size_t) -1)].id; } /// Return the ID of the instruction associated with the specified symbol. - size_t GetID(char symbol) { + size_t GetIDFromSymbol(char symbol) const { emp_assert(symbol > 0); return symbol_map[(size_t) symbol]; } + /// Return the ID of the instruction that has the specified name. + size_t GetIndex(const std::string & name) const { + emp_assert(Has(name_map, name), name); + return Find(name_map, name, (size_t) -1); + } + /// Return the ID of the instruction that has the specified id. + size_t GetIndex(const size_t id) const { + emp_assert(Has(id_map, id), id); + return Find(id_map, id, (size_t) -1); + } + + size_t GetIndexFromSymbol(char symbol) const { + size_t id = GetIDFromSymbol(symbol); + return GetIndex(id); + } + /// Return the argument value associated with the provided keyword. arg_t GetArg(const std::string & name) { emp_assert(Has(arg_map, name)); @@ -144,14 +180,20 @@ namespace emp { const std::string & desc="", ScopeType scope_type=ScopeType::NONE, size_t scope_arg=(size_t) -1, - const inst_properties_t & inst_properties=inst_properties_t()) + const inst_properties_t & inst_properties=inst_properties_t(), + int _id = -1) { - const size_t id = inst_lib.size(); + const size_t idx = inst_lib.size(); + const size_t id = (_id >= 0) ? _id : inst_lib.size(); + emp_assert(!Has(id_map, id), "ID is already in use!", id); const char symbol = (id < symbol_defaults.size()) ? symbol_defaults[id] : '+'; - inst_lib.emplace_back(name, fun_call, num_args, desc, scope_type, scope_arg, inst_properties, symbol); + inst_lib.emplace_back(idx, id, name, fun_call, num_args, desc, scope_type, scope_arg, + inst_properties, symbol); inst_funs.emplace_back(fun_call); - name_map[name] = id; + name_map[name] = idx; + id_map[id] = idx; symbol_map[(size_t) symbol] = id; + std::cout << "Registered instruction: " << name << " index: " << idx << "; id: " << id << "; symbol: " << symbol << std::endl; } /// Specify a keyword and arg value. @@ -161,18 +203,17 @@ namespace emp { } /// Process a specified instruction in the provided hardware. - void ProcessInst(hardware_t & hw, const inst_t & inst) const { - inst_funs[inst.id](hw, inst); + virtual void ProcessInst(hardware_t & hw, const inst_t & inst) const { + inst_funs[inst.GetIndex()](hw, inst); } /// Process a specified instruction on hardware that can be converted to the correct type. template void ProcessInst(emp::Ptr hw, const inst_t & inst) const { emp_assert( dynamic_cast(hw.Raw()) ); - inst_funs[inst.id](*(hw.template Cast()), inst); + inst_funs[inst.GetIndex()](*(hw.template Cast()), inst); } - /// Write out a full genome to the provided ostream. void WriteGenome(const genome_t & genome, std::ostream & os=std::cout) const { for (const inst_t & inst : genome) { @@ -188,9 +229,10 @@ namespace emp { /// Read the instruction in the provided info and append it to the provided genome. void ReadInst(genome_t & genome, std::string info) const { std::string name = emp::string_pop_word(info); - size_t id = GetID(name); - genome.emplace_back(id); - size_t num_args = GetNumArgs(id); + size_t idx = GetIndex(name); + size_t id = GetID(idx); + genome.emplace_back(idx, id); + size_t num_args = GetNumArgs(idx); for (size_t i = 0; i < num_args; i++) { std::string arg_name = emp::string_pop_word(info); // @CAO: Should check to make sure arg name is real. diff --git a/include/emp/hardware/LinearCode.hpp b/include/emp/hardware/LinearCode.hpp index 17d0f2d9bd..d41bef963c 100644 --- a/include/emp/hardware/LinearCode.hpp +++ b/include/emp/hardware/LinearCode.hpp @@ -1,15 +1,18 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file LinearCode.hpp + * @file * @brief A linear sequence of instructions. */ #ifndef EMP_HARDWARE_LINEARCODE_HPP_INCLUDE #define EMP_HARDWARE_LINEARCODE_HPP_INCLUDE +#include + #include "../base/array.hpp" #include "../base/vector.hpp" diff --git a/include/emp/hardware/VirtualCPU.hpp b/include/emp/hardware/VirtualCPU.hpp new file mode 100644 index 0000000000..1f13889f21 --- /dev/null +++ b/include/emp/hardware/VirtualCPU.hpp @@ -0,0 +1,847 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021-2022 +*/ +/** + * @file + * @brief A simple virtual CPU styled after the original and extended Avidian architectures. + * @TODO + * - Expanded heads? + * - expanded_nop_args useful? + * - Consider changing default return value for search functions + * - Consider switching to (or adding an optional mode) where nops are only curated + * as-needed instead of all at once + * + */ + +#ifndef EMP_HARDWARE_VIRTUALCPU_HPP_INCLUDE +#define EMP_HARDWARE_VIRTUALCPU_HPP_INCLUDE + +#include +#include +#include + +#include "../base/array.hpp" +#include "../base/Ptr.hpp" +#include "../base/unordered_map.hpp" +#include "../base/vector.hpp" +#include "../datastructs/map_utils.hpp" +#include "../datastructs/vector_utils.hpp" +#include "../io/File.hpp" +#include "../math/Random.hpp" +#include "../tools/string_utils.hpp" + +#include "Genome.hpp" +#include "VirtualCPU_InstLib.hpp" + +namespace emp{ + /// \brief A simple virtual CPU styled after those seen in Avida + /// + /// This class represents a single virtual CPU following a genome of assembly-level + /// instructions. + /// By default, each CPU features four heads, two stacks, multiple registers, and + /// a circular genome. + /// Both the original and extended architectures are supported. + template + class VirtualCPU{ + public: + static constexpr size_t NUM_STACKS = 2; ///< Number of stacks in this CPU (currently 2) + static constexpr size_t MAX_NOPS = 23; ///< Maximum number of nop instructions supported + struct Instruction; + + using derived_t = DERIVED; + using data_t = int32_t; + using inst_t = Instruction; + using inst_lib_t = VirtualCPU_InstLib; + using genome_t = Genome; + using nop_vec_t = emp::vector; + using stack_t = emp::vector; + + /// \brief Representation of a single instruction in the CPU's genome + /// + /// Only contains the necessary information for which instruction is being represented + /// as well as any data it needs in the genome. + /// Does NOT contain the actual logic of the instruction, nor the name. + /// These are handled by the instruction library itself. + struct Instruction : public inst_lib_t::InstructionBase { + size_t idx; /// Index of the instruction in the instruction library + size_t id; /// Identifier for the instruction that gives the user + /// flexibility over the instruction (e.g., what symbol + /// it should use in a string representation) + emp::vector nop_vec; /// Representation of the contiguous sequence of NOP + /// instructions following this instruction in the genome + bool has_been_executed = false; /// Has this instruction been executed? + bool has_been_copied = false; // Has this instruction been copied to an offspring? + + Instruction() = delete; + Instruction(size_t _idx, size_t _id=0, emp::vector _nop_vec = {}) + : idx(_idx), id(_id), nop_vec(_nop_vec) { ; } + Instruction(const Instruction &) = default; + Instruction(Instruction &&) = default; + + Instruction & operator=(const Instruction &) = default; + Instruction & operator=(Instruction &&) = default; + bool operator<(const Instruction & in) const { + return id < in.id; + } + bool operator==(const Instruction & in) const { return id == in.id; } + bool operator!=(const Instruction & in) const { return !(*this == in); } + bool operator>(const Instruction & in) const { return in < *this; } + bool operator>=(const Instruction & in) const { return !(*this < in); } + bool operator<=(const Instruction & in) const { return !(in < *this); } + + void Set(size_t _idx, size_t _id, emp::vector _nop_vec = {}) + { idx = _idx; id = _id; nop_vec=_nop_vec;} + + size_t GetIndex() const override { return idx; } + }; + + + protected: + size_t num_regs = 0; ///< Number of registers found in this CPU + size_t num_nops = 0; ///< Number of NOP instructions found in this CPU's library + + public: + //////// FLAGS + bool are_nops_counted = false; ///< Flag detailing if the number of NOP instructions + ///< in the CPU's library have been counted + bool are_regs_expanded = false; ///< Flag signaling if the number of registers have + ///< been expanded to accommodate the number of NOP + ///< instructions in the library + bool nops_need_curated = true; ///< Flag signaling that NOP instructions need curated + bool expanded_nop_args = false; ///< Flag signaling that CPU is used the expanded + + //////// CPU COMPONENTS + emp::vector regs; ///< Vector of registers + std::unordered_map inputs; ///< Map of all available inputs + ///< (position -> value) + std::unordered_map outputs; ///< Map of all outputs (position -> value) + emp::array stacks; ///< Array of stacks for this CPU + size_t inst_ptr; ///< Instruction pointer, signifies next + ///< instruction to be executed + size_t flow_head; ///< Flow head, used for moving heads and + ///< values + size_t read_head; ///< Read head, signals what instruction to + ///< copy next + size_t write_head; ///< Write head, signals where to copy next + ///< instruction + size_t cooldown_timer = 0; ///< Do not process inst if value > 0. + ///< Decrease this value instead + //////// HELPER CONSTRUCTS + emp::unordered_map nop_id_map;/**< NOP inst id -> Nop index + (e.g., NopA -> 0, NopB -> 1, + NopE -> 5) */ + emp::vector label_idx_vec; ///< Vector of LABEL instructions indices in genome + //////// GENOME + genome_t genome; ///< Preserved copy of genome from organism creation/birth + ///< that should not change in any way + genome_t genome_working; ///< Working copy of genome that can mutate, resize, and change + //////// BOOKKEEPING + size_t active_stack_idx = 0; ///< Index of CPU's active stack + emp::vector copied_inst_id_vec; /**< Vector of instructions that have been + copied */ + size_t num_insts_executed = 0; ///< Number of instructions that have been executed + + + //////// CONSTRUCTORS / DESTRUCTOR + /// Create a new VirtualCPU with the same genome (and thus instruction library) + VirtualCPU(const genome_t & in_genome) + : regs(), inputs(), outputs(), + inst_ptr(0), flow_head(0), read_head(0), write_head(0), + genome(in_genome), genome_working(in_genome) { + Initialize(); + ResetHardware(); + } + /// Create a default VirtualCPU (no genome sequence, default instruction set) + VirtualCPU() : + VirtualCPU(genome_t(inst_lib_t::DefaultInstLib())) { + Initialize(); + ResetHardware(); + } + /// Create a perfect copy of passed VirtualCPU + VirtualCPU(const VirtualCPU &) = default; + /// Default move constructor + VirtualCPU(VirtualCPU &&) = default; + /// Default destructor + virtual ~VirtualCPU() { ; } + + + //////// GETTERS + /// Return size of original genome + size_t GetGenomeSize() const { return genome.GetSize(); } + /// Return size of working genome + size_t GetWorkingGenomeSize() const { return genome_working.GetSize(); } + /// Return the number of registers in the CPU + size_t GetNumRegs() const { return num_regs; } + /// Return the number of NOP instructions found in the CPU's instruction library + size_t GetNumNops() const { return num_nops; } + /// Return the outputs of the CPU + const std::unordered_map & GetOutputs() const { return outputs; } + /// Return a pointer to the CPU's instruction library + Ptr GetInstLib() const { return genome.GetInstLib(); } + /// Return the number of instructions that have been executed + size_t GetNumInstsExecuted() const{ + size_t count = 0; + for (auto inst : genome_working) { + if (inst.has_been_executed) count++; + } + return count; + } + /// Return the number of instructions that have been copied + size_t GetNumInstsCopied() const{ + size_t count = 0; + for (auto inst : genome_working) { + if (inst.has_been_copied) count++; + } + return count; + } + + + + //////// SETTERS + /// Copies passed vector into input map + void SetInputs(const emp::vector & vals) { + inputs = emp::ToUMap(vals); + } + + + //////// GENOME & INSTRUCTION MANIPULATION + /// Load instructions from input stream + bool Load(std::istream & input) { + ClearGenome(); + File file(input); + file.RemoveComments("//"); // Remove all C++ style comments + file.RemoveComments("#"); // Remove all bash/Python/R style comments + file.CompressWhitespace(); // Trim down remaining whitespace. + file.RemoveEmpty(); + if (file.GetNumLines() == 0) { + emp::notify::Error("VirtualCPU trying to load a genome from an empty stream!"); + } + file.Apply( [this](std::string & info) { PushInst(info); } ); + nops_need_curated = true; + return true; + } + + /// Load instructions from file + bool Load(const std::string & filename) { + std::ifstream is(filename); + if (is.is_open()) return Load(is); + emp::notify::Error("VirtualCPU genome file is either empty or missing: ", filename); + return false; + } + + /// Load genome from a string of characters + bool LoadFromChars(const std::string & new_genome){ + ClearGenome(); + for(size_t idx = 0; idx < new_genome.size(); ++idx){ + PushInst(new_genome[idx]); + } + nops_need_curated = true; + return true; + } + + /// Add a new instruction to the end of the genome, by index in the instruction library + void PushInst(size_t idx) { + const size_t id = GetInstLib()->GetID(idx); + genome.emplace_back(idx, id); + genome_working.emplace_back(idx, id); + nops_need_curated = true; + } + + /// Redirect literal ints to PushInst(size_t) overload. + void PushInst(int idx) { PushInst(static_cast(idx)); } + + /// Add a new instruction to the end of the genome, by the instruction's symbol/char + void PushInst(char c) { PushInst( GetInstLib()->GetIndexFromSymbol(c) ); } + + /// Add a new instruction to the end of the genome, by name + void PushInst(const std::string & name) { + PushInst(GetInstLib()->GetIndex(name)); + nops_need_curated = true; + } + + /// Add a specified new instruction to the end of the genome + void PushInst(const inst_t & inst) { + genome.emplace_back(inst); + genome_working.emplace_back(inst); + nops_need_curated = true; + } + + /// Add multiple copies of a specified instruction to the end of the genome + void PushInst(const inst_t & inst, size_t count) { + genome.reserve(genome.size() + count); + for (size_t i = 0; i < count; i++) genome.emplace_back(inst); + genome_working.reserve(genome.size() + count); + for (size_t i = 0; i < count; i++) genome_working.emplace_back(inst); + nops_need_curated = true; + } + + /// Return the first instruction in the instruction library + inst_t GetDefaultInst() const{ + return inst_t(GetInstLib()->GetIndex(0), 0); + } + + /// Add one or more default instructions to the end of the genome + void PushDefaultInst(size_t count=1) { + PushInst( inst_t(GetInstLib()->GetIndex(0), 0), count ); + nops_need_curated = true; + } + + /// Return a random instruction from the instruction library + inst_t GetRandomInst(Random & rand) { + size_t id = rand.GetUInt(GetInstLib()->GetSize()); + size_t idx = GetInstLib()->GetIndex(id); + //size_t idx = rand.GetUInt(GetInstLib()->GetSize()); + //size_t id = GetInstLib()->GetID(idx); + return inst_t(idx, id); + } + + /// Overwrite the instruction at the given genome index with passed instruction + void SetInst(size_t pos, const inst_t & inst) { + genome[pos] = inst; + genome_working[pos] = inst; + nops_need_curated = true; + } + + /// Overwrite the instruction at the given genome index with a random instruction + void RandomizeInst(size_t pos, Random & rand) { + SetInst(pos, GetRandomInst(rand) ); + nops_need_curated = true; + } + + /// Add a random instruction from the instruction library to the end of the genome + void PushRandomInst(Random & random, const size_t count=1) { + for (size_t i = 0; i < count; i++) { + PushInst(GetRandomInst(random)); + } + nops_need_curated = true; + } + + /// Insert the given instruction at the specified genome position + void InsertInst(const inst_t& inst, const size_t idx) { + genome.emplace(genome.begin() + idx, inst); + genome_working.emplace(genome_working.begin() + idx, inst); + nops_need_curated = true; + } + + /// Inserts a random instruction at the given genome position + void InsertRandomInst(const size_t idx, emp::Random& random) { + InsertInst(GetRandomInst(random), idx); + } + + /// Remove the instruction at the specified genome position + void RemoveInst(const size_t idx) { + genome.erase(genome.begin() + idx); + genome_working.erase(genome_working.begin() + idx); + nops_need_curated = true; + } + /// Increase the cooldown by some value, so instructions cannot be processed for longer + void IncreaseCooldown(size_t val){ + cooldown_timer += val; + } + /// Decrease the cooldown by some value, so instructions can be processed sooner + void DecreaseCooldown(size_t val){ + if(cooldown_timer >= val) cooldown_timer -= val; + else cooldown_timer = 0; + } + /// Reset the cooldown timer + void ResetCooldown(){ + cooldown_timer = 0; + } + + + + //////// HEAD MANIPULATION + + void ResetIP() { inst_ptr = 0; } ///< Move instruction pointer to beginning of the genome. + void ResetRH() { read_head = 0; } ///< Move read head to beginning of the genome. + void ResetWH() { write_head = 0; } ///< Move write head to beginning of the genome. + void ResetFH() { flow_head = 0; } ///< Move flow head to beginning of the genome. + + /// Advance the instruction pointer so many steps and wrap around the end of the genome + void AdvanceIP(size_t steps=1) { + inst_ptr += steps; + inst_ptr = (genome_working.size() > 0 ? inst_ptr % genome_working.size() : 0); + } + /// Advance the read head so many steps and wrap around the end of the genome + void AdvanceRH(size_t steps=1) { + read_head += steps; + read_head = (genome_working.size() > 0 ? read_head % genome_working.size() : 0); + } + /// Advance the write head so many steps and wrap around the end of the genome + void AdvanceWH(size_t steps=1) { + write_head += steps; + write_head = (genome_working.size() > 0 ? write_head % genome_working.size() : 0); + } + /// Advance the flow head so many steps and wrap around the end of the genome + void AdvanceFH(size_t steps=1) { + flow_head += steps; + flow_head = (genome_working.size() > 0 ? flow_head % genome_working.size() : 0); + } + /// Set the instruction pointer to the genome index, wrap around the end of the genome + void SetIP(size_t pos) { + inst_ptr = pos; + inst_ptr %= genome_working.size(); + } + /// Set the read head to the genome index, wrap around the end of the genome + void SetRH(size_t pos) { + read_head = pos; + read_head %= genome_working.size(); + } + /// Set the write head to the genome index, wrap around the end of the genome + void SetWH(size_t pos) { + write_head = pos; + write_head %= genome_working.size(); + } + /// Set the flow head to the genome index, wrap around the end of the genome + void SetFH(size_t pos) { + flow_head = pos; + flow_head %= genome_working.size(); + } + /// Set the specified head (which can wrap) to the beginning of the genome, + void ResetModdedHead(size_t head_idx) { + size_t modded_idx = head_idx % 4; + if (modded_idx == 0) SetIP(0); + else if (modded_idx == 1) SetRH(0); + else if (modded_idx == 2) SetWH(0); + else if (modded_idx == 3) SetFH(0); + } + /// Set the specified head (which can wrap) to the given genome position, + /// wrap around the end of the genome + void SetModdedHead(size_t head_idx, size_t pos) { + size_t modded_idx = head_idx % 4; + if (modded_idx == 0) SetIP(pos); + else if (modded_idx == 1) SetRH(pos); + else if (modded_idx == 2) SetWH(pos); + else if (modded_idx == 3) SetFH(pos); + } + /// Advance the specified head (which can wrap) the given number of instructions, + /// wrap around the end of the genome + void AdvanceModdedHead(size_t head_idx, size_t steps=1) { + size_t modded_idx = head_idx % 4; + if (modded_idx == 0) AdvanceIP(steps); + else if (modded_idx == 1) AdvanceRH(steps); + else if (modded_idx == 2) AdvanceWH(steps); + else if (modded_idx == 3) AdvanceFH(steps); + } + /// Return the head POSITION of the specified head (can wrap) + size_t GetModdedHead(size_t head_idx) { + size_t modded_idx = head_idx % 4; + if (modded_idx == 0) return inst_ptr; + else if (modded_idx == 1) return read_head; + else if (modded_idx == 2) return write_head; + else if (modded_idx == 3) return flow_head; + return inst_ptr; + } + + + //////// HARDWARE MANIPULATION + /// Initializes the CPU by counting the number of NOP instructions in the instruction + /// library and expanding the number of registers to match + void Initialize() { + CountNops(); + ExpandRegisters(); + ResetHardware(); + } + /// Reset all heads + void ResetHeads() { + ResetIP(); + ResetRH(); + ResetWH(); + ResetFH(); + ResetCooldown(); + } + /// Reset all inputs and outputs + void ResetIO() { + inputs.clear(); + outputs.clear(); + } + /// Reset all memory/data + void ResetMemory() { + // Initialize registers to their position. So Reg0 = 0 and Reg11 = 11. + for (size_t i = 0; i < num_regs; i++) { + regs[i] = (data_t) i; + } + for (size_t i = 0; i < NUM_STACKS; ++i) { + stacks[i].resize(0); + } + active_stack_idx = 0; + } + /// Reset all bookkeeping variables + void ResetBookkeeping() { + copied_inst_id_vec.clear(); + num_insts_executed = 0; + } + /// Reset the working genome back to the original genome + void ResetWorkingGenome() { + genome_working = genome; + label_idx_vec.clear(); + nops_need_curated = true; + } + /// Reset just the CPU hardware, but keep the original genome + void ResetHardware() { + ResetHeads(); + ResetMemory(); + ResetIO(); + ResetBookkeeping(); + } + /// Clear the main genome of the organism and reset all hardware + void ClearGenome() { + genome.resize(0,0); // Clear out genome + genome_working.resize(0,0); // Clear out working genome + label_idx_vec.clear(); // No labels if genome is empty + nops_need_curated = true; + ResetHardware(); // Reset the full hardware + } + /// Compile NOP instructions in genome into useful nop vectors for each instruction, + /// and records the position of all LABEL instructions + void CurateNops() { + if (genome_working.size() == 0) { + nops_need_curated = false; + return; + } + bool label_inst_present = GetInstLib()->IsInst("Label"); + size_t label_inst_id = label_inst_present ? GetInstLib()->GetID("Label") : 0; + + if (!are_nops_counted) CountNops(); + label_idx_vec.clear(); + // Start by filling the nop vector of the last instruction + for (size_t inst_idx = 0; inst_idx < genome_working.GetSize() - 1; ++inst_idx) { + if (emp::Has(nop_id_map, genome_working[inst_idx].id)) { + genome_working[genome_working.size() - 1].nop_vec.push_back( + nop_id_map[genome_working[inst_idx].id]); + } + else break; + } + // If the last index is a label, record it! + if (label_inst_present && + (genome_working[genome_working.size() - 1].id == label_inst_id)) + label_idx_vec.push_back(genome_working.size() - 1); + // Now iterate backward over the genome, filling in each instruction's nop vector + // Example, our genome looks like xyzabc where only a, b, and c are nops + // If we are on index 2 (z), we see it is followed by a nop. + // Thus, we copy the next instruction into the nop vector [a] + // Then we copy THAT instruction's nop vector, too: [a,b,c] + // By going in reverse order, all following instructions already have a nop vec + for (auto it = genome_working.rbegin() + 1; it != genome_working.rend(); ++it) { + if (emp::Has(nop_id_map, (it - 1)->id)) { + it->nop_vec.resize( (it - 1)->nop_vec.size() + 1 ); + it->nop_vec[0] = nop_id_map[(it - 1)->id]; + std::copy( + (it - 1)->nop_vec.begin(), + (it - 1)->nop_vec.end(), + it->nop_vec.begin() + 1); + } + } + for (size_t inst_idx = 0; inst_idx < genome_working.size(); ++inst_idx) { + if (genome_working[inst_idx].id == label_inst_id) // Record pos if inst is label + label_idx_vec.push_back(inst_idx); + } + nops_need_curated = false; + } + /// Determine the number of sequential NOP instructions in the instruction library + /// + /// Starts at NopA and continues from there. Any missing instructions force count to + /// stop. Last possible NOP instruction is NopW, as NopX is a special case in Avida. + void CountNops() { + num_nops = 0; + nop_id_map.clear(); + are_nops_counted = true; + for (size_t idx = 0; idx < MAX_NOPS ; ++idx) { // Stop before X! + std::string nop_name = (std::string)"Nop" + (char)('A' + idx); + if (GetInstLib()->IsInst(nop_name)) { + num_nops++; + size_t id = GetInstLib()->GetID(nop_name); + nop_id_map[id] = idx; + } + else return; + } + } + /// Expand the CPU's registers to match the number of NOP instructions in the + /// instruction library + void ExpandRegisters() { + if (!are_nops_counted) CountNops(); + are_regs_expanded = true; + num_regs = num_nops; + regs.resize(num_regs); + } + + //////// NOP SEQUENCE METHODS + /// For a given NOP instruction (as an index), return its complement index + size_t GetComplementNop(size_t idx) { + if (idx >= num_nops - 1) return 0; + else return idx + 1; + } + /// For a vector of NOP instructions (as indices), return a vector of complement indices + /// in the same order + nop_vec_t GetComplementNopSequence(const nop_vec_t& nop_vec) { + nop_vec_t res_vec; + for (size_t nop : nop_vec) { + res_vec.push_back(GetComplementNop(nop)); + } + return res_vec; + } + /// Check if a vector of NOP instructions is the same as the START of another vector + bool CompareNopSequences(const nop_vec_t& search_vec, const nop_vec_t& compare_vec) { + if (search_vec.size() > compare_vec.size()) return false; + if (search_vec.size() == 0 || compare_vec.size() == 0) return false; + for (size_t idx = 0; idx < search_vec.size(); ++idx) { + if (search_vec[idx] != compare_vec[idx]) return false; + } + return true; + } + /// Check if the given vector of NOP instructions (as indices) were the last + /// instructions to be copied by the CPU + bool CheckIfLastCopied(const nop_vec_t& label) { + if (label.size() > copied_inst_id_vec.size()) return false; + if (label.size() == 0) return false; + int idx = label.size() - 1; + for (auto copied_it = copied_inst_id_vec.rbegin(); copied_it != copied_inst_id_vec.rend(); copied_it++) { + if (*copied_it != label[idx]) + return false; + idx--; + if (idx < 0) break; + + } + return true; + } + /// Search up the genome (backward) for a sequence of NOP instructions following a LABEL + /// instruction that match the NOP sequence following the current instruction + /// + /// @param start_local If true, search from instruction pointer. If false, search from + /// start of the genome + size_t FindLabel_Reverse(bool start_local) { + const nop_vec_t search_vec = genome_working[inst_ptr].nop_vec; + size_t start_label_vec_idx = label_idx_vec.size() - 1; + if (start_local) { + bool start_found = false; + for (size_t offset = 0; offset < label_idx_vec.size(); ++offset) { + if (label_idx_vec[label_idx_vec.size() - offset - 1] < inst_ptr) { + start_label_vec_idx = label_idx_vec.size() - offset - 1; + start_found = true; + break; + } + } + if (!start_found) start_label_vec_idx = label_idx_vec.size() - 1; + } + for (size_t offset = 0; offset < label_idx_vec.size(); ++offset) { + const size_t idx = + label_idx_vec[ + (start_label_vec_idx - offset + label_idx_vec.size()) % label_idx_vec.size() + ]; + if (CompareNopSequences(search_vec, genome_working[idx].nop_vec)) return idx; + } + return inst_ptr; + } + /// Search the genome for a sequence of NOP instructions following a LABEL + /// instruction that match the NOP sequence following the current instruction + /// + /// @param start_local If true, search from instruction pointer. If false, search from + /// start of the genome + /// @param reverse If true, traverse the genome backward. If false, traverse forward + size_t FindLabel(bool start_local, bool reverse = false) { + if (reverse) return FindLabel_Reverse(start_local); + const nop_vec_t search_vec = genome_working[inst_ptr].nop_vec; + size_t start_label_vec_idx = 0; + if (start_local) { + bool start_found = false; + for (; start_label_vec_idx < label_idx_vec.size(); ++start_label_vec_idx) { + if (label_idx_vec[start_label_vec_idx] > inst_ptr) { + start_found = true; + break; + } + } + if (!start_found) start_label_vec_idx = 0; + } + for (size_t offset = 0; offset < label_idx_vec.size(); ++offset) { + const size_t idx = label_idx_vec[(start_label_vec_idx + offset) % label_idx_vec.size()]; + if (CompareNopSequences(search_vec, genome_working[idx].nop_vec)) return idx; + } + return inst_ptr; + } + /// Search up the genome (backward) for a sequence of NOP instructions + /// that match the given NOP sequence + /// + /// @param search_vec The sequence of NOP instructions to search for + /// @param start_idx Position in the genome to start the search + size_t FindNopSequence_Reverse(const nop_vec_t& search_vec, size_t start_idx) { + for (size_t offset = 1; offset < genome_working.size() + 1; ++offset) { + const size_t idx = (start_idx - offset + genome_working.size()) % genome_working.size(); + if (CompareNopSequences(search_vec, genome_working[idx].nop_vec)) return idx; + } + return inst_ptr; + } + /// Search up the genome (backward) for a sequence of NOP instructions + /// that match the given NOP sequence + /// + /// @param search_vec The sequence of NOP instructions to search for + /// @param start_local If true, search from instruction pointer. If false, search from + /// start of the genome + size_t FindNopSequence_Reverse(const nop_vec_t& search_vec, bool start_local) { + size_t start_idx = 0; + if (start_local && inst_ptr != 0) start_idx = inst_ptr; + return FindNopSequence_Reverse(search_vec, start_idx); + } + /// Search up the genome (backward) for a sequence of NOP instructions + /// that match the NOP sequence following the current instruction + /// + /// @param start_local If true, search from instruction pointer. If false, search from + /// start of the genome + size_t FindNopSequence_Reverse(bool start_local) { + const nop_vec_t search_vec = genome_working[inst_ptr].nop_vec; + return FindNopSequence_Reverse(search_vec, start_local); + } + /// Search the genome for a sequence of NOP instructions that match the given + /// NOP sequence + /// + /// @param search_vec The sequence of NOP instructions to search for + /// @param start_idx Position in the genome to start the search + size_t FindNopSequence(const nop_vec_t& search_vec, size_t start_idx, + bool reverse = false) { + if (reverse) return FindNopSequence_Reverse(search_vec, start_idx); + for (size_t offset = 1; offset < genome_working.size() + 1; ++offset) { + const size_t idx = (start_idx + offset) % genome_working.size(); + if (CompareNopSequences(search_vec, genome_working[idx].nop_vec)) return idx; + } + return inst_ptr; + } + /// Search the genome for a sequence of NOP instructions that match the given + /// NOP sequence + /// + /// @param search_vec The sequence of NOP instructions to search for + /// @param start_local If true, search from instruction pointer. If false, search from + /// start of the genome + /// @param reverse If true, traverse the genome backward. If false, traverse forward + size_t FindNopSequence(const nop_vec_t& search_vec, bool start_local, + bool reverse = false) { + size_t start_idx = genome_working.size() - 1; + if (start_local) start_idx = inst_ptr; + return FindNopSequence(search_vec, start_idx, reverse); + } + /// Search up the genome (backward) for a sequence of NOP instructions + /// that match the NOP sequence following the current instruction + /// + /// @param start_local If true, search from instruction pointer. If false, search from + /// start of the genome + /// @param reverse If true, traverse the genome backward. If false, traverse forward + size_t FindNopSequence(bool start_local, bool reverse = false) { + const nop_vec_t search_vec = genome_working[inst_ptr].nop_vec; + return FindNopSequence(search_vec, start_local, reverse); + } + + + //////// STACK MANIPULATION + /// Push the value in the specified register on top of the active stack + void StackPush(size_t reg_idx) { + stacks[active_stack_idx].push_back(regs[reg_idx]); + } + /// Remove the value from the top of the active stack and store it in the + /// specified register + void StackPop(size_t reg_idx) { + if (stacks[active_stack_idx].size()) { + regs[reg_idx] = *stacks[active_stack_idx].rbegin(); + stacks[active_stack_idx].pop_back(); + } + } + /// Swap which stack is active + void StackSwap() { + active_stack_idx++; + if (active_stack_idx >= NUM_STACKS) active_stack_idx = 0; + } + /// Fetch the nth value of the specified stack + data_t GetStackVal(size_t stack_idx, size_t val_idx) { + emp_assert(stack_idx < NUM_STACKS); + emp_assert(val_idx < stacks[stack_idx].size()); + size_t reverse_idx = stacks[stack_idx].size() - val_idx - 1; + return stacks[stack_idx][reverse_idx]; + } + + + //////// PROCESSING + /// Process the next instruction pointed to be the instruction pointer + void SingleProcess(bool verbose = true) { + if(cooldown_timer > 0){ + DecreaseCooldown(1); + num_insts_executed++; + } + else{ + emp_assert(genome_working.GetSize() > 0); // A genome must exist to be processed. + if(!are_regs_expanded) ExpandRegisters(); + if(nops_need_curated) CurateNops(); + if(verbose){ + GetInstLib()->GetName(genome_working[inst_ptr].idx); + PrintDetails(); + } + genome_working[inst_ptr].has_been_executed = true; + GetInstLib()->ProcessInst(ToPtr(this), genome_working[inst_ptr]); + AdvanceIP(); + num_insts_executed++; + } + } + /// Process the next SERIES of instructions, directed by the instruction pointer. + void Process(size_t num_inst = 1, bool verbose = true) { + for (size_t i = 0; i < num_inst; i++) SingleProcess(verbose); + } + + + //////// STATE -> STRING FUNCTIONS + /// Return the working genome in string form. + /// + /// Each instruction is represented by a single character, dictated by the + /// instruction's ID. + std::string GetWorkingGenomeString() const{ + std::stringstream sstr; + sstr << "[" << genome_working.size() << "]"; + for (size_t idx = 0; idx < genome_working.size(); idx++) { + unsigned char c = 'a' + genome_working[idx].id; + if (genome_working[idx].id > 25) c = 'A' + genome_working[idx].id - 26; + sstr << c; + } + return sstr.str(); + } + /// Return the original genome in string form. + /// + /// Each instruction is represented by a single character, dictated by the + /// instruction's ID. + std::string GetGenomeString() const{ + std::stringstream sstr; + sstr << "[" << genome.size() << "]"; + for (size_t idx = 0; idx < genome.size(); idx++) { + unsigned char c = 'a' + genome[idx].id; + if (genome[idx].id > 25) c = 'A' + genome[idx].id - 26; + sstr << c; + } + return sstr.str(); + } + /// Return the original genome in string form, without the genome length. + /// + /// Each instruction is represented by a single character, dictated by the + /// instruction's ID. + std::string GetRawGenomeString() const{ + std::stringstream sstr; + for(size_t idx = 0; idx < genome.size(); idx++){ + unsigned char c = 'a' + genome[idx].id; + if(genome[idx].id > 25) c = 'A' + genome[idx].id - 26; + sstr << c; + } + return sstr.str(); + } + /// Output the state of the CPU's heads and registers to the specified output stream + void PrintDetails(std::ostream& os = std::cout) { + os << "IP: " << inst_ptr; + os << " RH: " << read_head; + os << " WH: " << write_head; + os << " FH: " << flow_head; + os << "(nops: " << num_nops << "; regs: " << num_regs << ")" << std::endl; + for (size_t reg_idx = 0; reg_idx < regs.size(); ++reg_idx) { + os << "[" << reg_idx << "] " << regs[reg_idx] << std::endl; + } + } + + }; // End VirtualCPU class +} // End namespace + + + +#endif // #ifndef EMP_HARDWARE_VIRTUALCPU_HPP_INCLUDE diff --git a/include/emp/hardware/VirtualCPU_InstLib.hpp b/include/emp/hardware/VirtualCPU_InstLib.hpp new file mode 100644 index 0000000000..626543ac11 --- /dev/null +++ b/include/emp/hardware/VirtualCPU_InstLib.hpp @@ -0,0 +1,297 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021-2022 +*/ +/** + * @file + * @brief A specialized version of InstLib to handle VirtualCPU instructions. + * + */ + +#ifndef EMP_HARDWARE_VIRTUALCPU_INSTLIB_HPP_INCLUDE +#define EMP_HARDWARE_VIRTUALCPU_INSTLIB_HPP_INCLUDE + +#include "../base/error.hpp" +#include "../math/math.hpp" + +#include "InstLib.hpp" + +namespace emp { + + /// \brief A pure-virtual class that defines a series of instructions for VirtualCPU_Base or any of its derived classes. + template + struct VirtualCPU_InstLib : public InstLib { + using hardware_t = HARDWARE_T; + using inst_lib_t = InstLib; + using arg_t = ARG_T; + using this_t = VirtualCPU_InstLib; + using inst_t = typename hardware_t::inst_t; + using nop_vec_t = typename hardware_t::nop_vec_t; + + // Instructions + static void Inst_NopA(hardware_t & /*hw*/, const inst_t & /*inst*/) { ; } + static void Inst_NopB(hardware_t & /*hw*/, const inst_t & /*inst*/) { ; } + static void Inst_NopC(hardware_t & /*hw*/, const inst_t & /*inst*/) { ; } + static void Inst_Inc(hardware_t & hw, const inst_t & inst) { + size_t idx = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + ++hw.regs[idx]; + } + static void Inst_Dec(hardware_t & hw, const inst_t & inst) { + size_t idx = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + --hw.regs[idx]; + } + static void Inst_If_Not_Equal(hardware_t & hw, const inst_t & inst) { + if(hw.expanded_nop_args){ + size_t idx_op_1 = inst.nop_vec.size() < 1 ? 1 : inst.nop_vec[0]; + size_t idx_op_2 = inst.nop_vec.size() < 2 ? hw.GetComplementNop(idx_op_1) : inst.nop_vec[1]; + if(hw.regs[idx_op_1] == hw.regs[idx_op_2]) + hw.AdvanceIP(1); + hw.AdvanceIP(inst.nop_vec.size()); + } + else{ + size_t idx_1 = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + size_t idx_2 = hw.GetComplementNop(idx_1); + if(hw.regs[idx_1] == hw.regs[idx_2]) + hw.AdvanceIP(1); + if(inst.nop_vec.size()) hw.AdvanceIP(1); + } + } + static void Inst_If_Less(hardware_t & hw, const inst_t & inst) { + if(hw.expanded_nop_args){ + size_t idx_op_1 = inst.nop_vec.size() < 1 ? 1 : inst.nop_vec[0]; + size_t idx_op_2 = inst.nop_vec.size() < 2 ? hw.GetComplementNop(idx_op_1) : inst.nop_vec[1]; + if(hw.regs[idx_op_1] >= hw.regs[idx_op_2]) + hw.AdvanceIP(1); + hw.AdvanceIP(inst.nop_vec.size()); + } + else{ + size_t idx_1 = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + size_t idx_2 = hw.GetComplementNop(idx_1); + if(hw.regs[idx_1] >= hw.regs[idx_2]) + hw.AdvanceIP(1); + if(inst.nop_vec.size()) hw.AdvanceIP(1); + } + } + static void Inst_Pop(hardware_t & hw, const inst_t & inst) { + size_t idx = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + hw.StackPop(idx); + } + static void Inst_Push(hardware_t & hw, const inst_t & inst) { + size_t idx = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + hw.StackPush(idx); + } + static void Inst_Swap_Stack(hardware_t & hw, const inst_t & /*inst*/) { + hw.StackSwap(); + } + static void Inst_Shift_Right(hardware_t & hw, const inst_t & inst) { + size_t idx = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + hw.regs[idx] >>= 1; + } + static void Inst_Shift_Left(hardware_t & hw, const inst_t & inst) { + size_t idx = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + hw.regs[idx] <<= 1; + } + static void Inst_Add(hardware_t & hw, const inst_t & inst) { + if(hw.expanded_nop_args){ + size_t idx_res = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + size_t idx_op_1 = inst.nop_vec.size() < 2 ? idx_res : inst.nop_vec[1]; + size_t idx_op_2 = inst.nop_vec.size() < 3 ? hw.GetComplementNop(idx_op_1) : inst.nop_vec[2]; + hw.regs[idx_res] = hw.regs[idx_op_1] + hw.regs[idx_op_2]; + } + else{ + size_t idx = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + size_t idx_2 = hw.GetComplementNop(idx); + hw.regs[idx] = hw.regs[idx] + hw.regs[idx_2]; + } + } + static void Inst_Sub(hardware_t & hw, const inst_t & inst) { + if(hw.expanded_nop_args){ + size_t idx_res = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + size_t idx_op_1 = inst.nop_vec.size() < 2 ? idx_res : inst.nop_vec[1]; + size_t idx_op_2 = inst.nop_vec.size() < 3 ? hw.GetComplementNop(idx_op_1) : inst.nop_vec[2]; + hw.regs[idx_res] = hw.regs[idx_op_1] - hw.regs[idx_op_2]; + } + else{ + size_t idx = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + size_t idx_2 = hw.GetComplementNop(idx); + hw.regs[idx] = hw.regs[idx] - hw.regs[idx_2]; + } + } + static void Inst_Nand(hardware_t & hw, const inst_t & inst) { + if(hw.expanded_nop_args){ + size_t idx_res = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + size_t idx_op_1 = inst.nop_vec.size() < 2 ? idx_res : inst.nop_vec[1]; + size_t idx_op_2 = inst.nop_vec.size() < 3 ? hw.GetComplementNop(idx_op_1) : inst.nop_vec[2]; + hw.regs[idx_res] = ~(hw.regs[idx_op_1] & hw.regs[idx_op_2]); + } + else{ + size_t idx = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + size_t idx_2 = hw.GetComplementNop(idx); + hw.regs[idx] = hw.regs[idx] + hw.regs[idx_2]; + hw.regs[idx] = ~(hw.regs[idx] & hw.regs[idx_2]); + } + } + static void Inst_IO(hardware_t & hw, const inst_t & inst) { + size_t idx = inst.nop_vec.empty() ? 1 : inst.nop_vec[0]; + std::cout << "Output: " << hw.regs[idx] << std::endl; + // TODO: Handle input + } + static void Inst_H_Alloc(hardware_t & hw, const inst_t & /*inst*/) { + hw.genome_working.resize(hw.genome.size() * 2, hw.GetDefaultInst()); + hw.regs[0] = hw.genome.size(); + } + static void Inst_H_Divide(hardware_t & hw, const inst_t & /*inst*/) { + if(hw.read_head >= hw.genome.size()){ + hw.genome_working.resize(hw.read_head, 0); + hw.ResetHardware(); + hw.inst_ptr = hw.genome.size() - 1; + std::cout << "Divide!" << std::endl; + } + } + static void Inst_H_Copy(hardware_t & hw, const inst_t & /*inst*/) { + hw.genome_working[hw.write_head] = hw.genome_working[hw.read_head]; + hw.copied_inst_id_vec.push_back(hw.genome_working[hw.write_head].id); + hw.read_head++; + while(hw.read_head >= hw.genome_working.size()) hw.read_head -= hw.genome_working.size(); + hw.write_head++; + while(hw.write_head >= hw.genome_working.size()) hw.write_head -= hw.genome_working.size(); + // TODO: Mutation + } + static void Inst_H_Search(hardware_t & hw, const inst_t & inst) { + size_t res = hw.FindNopSequence(hw.GetComplementNopSequence(inst.nop_vec), hw.inst_ptr); + if(inst.nop_vec.size() == 0 || res == hw.inst_ptr){ + hw.regs[1] = 0; + hw.regs[2] = 0; + hw.SetFH(hw.inst_ptr + 1); + } + else{ + hw.regs[1] = (res - hw.inst_ptr) > 0 ? res - hw.inst_ptr : res + hw.genome_working.size() - res + hw.inst_ptr; + hw.regs[2] = inst.nop_vec.size(); + hw.SetFH(res + inst.nop_vec.size() + 1); + } + } + static void Inst_Mov_Head(hardware_t & hw, const inst_t & inst) { + if(hw.expanded_nop_args){ + size_t dest_idx = hw.flow_head; + if(inst.nop_vec.size() >= 2) dest_idx = hw.GetModdedHead(inst.nop_vec[1]); + if(!inst.nop_vec.empty()) hw.SetModdedHead(inst.nop_vec[0], dest_idx); + else hw.SetIP(dest_idx); + } + else{ + if(!inst.nop_vec.empty()){ + // IP is a special case because it auto advances! + if(inst.nop_vec[0] % 4 == 0) hw.SetIP(hw.flow_head - 1); + else hw.SetModdedHead(inst.nop_vec[0], hw.flow_head); + } + else hw.SetIP(hw.flow_head - 1); + } + } + static void Inst_Jmp_Head(hardware_t & hw, const inst_t & inst) { + if(hw.expanded_nop_args){ + size_t jump_dist = hw.regs[1]; + if(inst.nop_vec.size() >= 2) jump_dist = hw.regs[inst.nop_vec[1]]; + if(!inst.nop_vec.empty()) hw.AdvanceModdedHead(inst.nop_vec[0], jump_dist); + else hw.AdvanceIP(jump_dist); + } + else{ + if(!inst.nop_vec.empty()) hw.AdvanceModdedHead(inst.nop_vec[0], hw.regs[2]); + else hw.AdvanceIP(hw.regs[2]); + } + } + static void Inst_Get_Head(hardware_t & hw, const inst_t & inst) { + if(hw.expanded_nop_args){ + size_t head_val = inst.nop_vec.empty() ? hw.inst_ptr : hw.GetModdedHead(inst.nop_vec[0]); + if(inst.nop_vec.size() < 2) hw.regs[2] = head_val; + else hw.regs[inst.nop_vec[1]] = head_val; + } + else{ + if(inst.nop_vec.empty()) hw.regs[2] = hw.inst_ptr; + else hw.regs[2] = hw.GetModdedHead(inst.nop_vec[0]); + } + } + static void Inst_If_Label(hardware_t & hw, const inst_t & inst) { + hw.AdvanceIP(inst.nop_vec.size()); + if(!hw.CheckIfLastCopied(hw.GetComplementNopSequence(inst.nop_vec))) hw.AdvanceIP(); + } + static void Inst_Set_Flow(hardware_t & hw, const inst_t & inst) { + size_t idx = inst.nop_vec.empty() ? 2 : inst.nop_vec[0]; + hw.SetFH(hw.regs[idx]); + } + + /// Maintain and return a singleton of default instructions + static const this_t & DefaultInstLib() { + static this_t inst_lib; + if (inst_lib.GetSize() == 0) { + inst_lib.AddInst("NopA", Inst_NopA, 0, "No-operation A"); + inst_lib.AddInst("NopB", Inst_NopB, 0, "No-operation B"); + inst_lib.AddInst("NopC", Inst_NopC, 0, "No-operation C"); + inst_lib.AddInst("IfNEq", Inst_If_Not_Equal, 1, + "Skip next inst unless register values match"); + inst_lib.AddInst("IfLess", Inst_If_Less, 1, + "Skip next inst unless focal register is less than its complement"); + inst_lib.AddInst("Inc", Inst_Inc, 1, "Increment value in reg Arg1"); + inst_lib.AddInst("Dec", Inst_Dec, 1, "Decrement value in reg Arg1"); + inst_lib.AddInst("Pop", Inst_Pop, 1, "Pop value from active stack into register"); + inst_lib.AddInst("Push", Inst_Push, 1, "Add register's value to active stack"); + inst_lib.AddInst("Swap-Stk", Inst_Swap_Stack, 1, "Swap which stack is active"); + inst_lib.AddInst("ShiftR", Inst_Shift_Right, 1, "Shift register value right by one bit"); + inst_lib.AddInst("ShiftL", Inst_Shift_Left, 1, "Shift register value left by one bit"); + inst_lib.AddInst("Add", Inst_Add, 1, + "Add values in registers B and C, then store result in given register"); + inst_lib.AddInst("Sub", Inst_Sub, 1, + "Sub values in registers B and C, then store result in given register"); + inst_lib.AddInst("Nand", Inst_Nand, 1, + "NAND values in registers B and C, then store result in given register"); + inst_lib.AddInst("IO", Inst_IO, 1, + "Output value in given register and then place new input in that register"); + inst_lib.AddInst("HAlloc", Inst_H_Alloc, 1, "Allocate memory for offspring"); + inst_lib.AddInst("HDivide", Inst_H_Divide, 1, "Attempt to split offspring"); + inst_lib.AddInst("HCopy", Inst_H_Copy, 1, "Copy instruction from read head to write head"); + inst_lib.AddInst("HSearch", Inst_H_Search, 1, "Search for label complement"); + inst_lib.AddInst("MovHead", Inst_Mov_Head, 1, "Move a given head to a postiion"); + inst_lib.AddInst("JmpHead", Inst_Jmp_Head, 1, "Move a given head by a relative amount"); + inst_lib.AddInst("GetHead", Inst_Get_Head, 1, "Get location of head"); + inst_lib.AddInst("IfLabel", Inst_If_Label, 1, + "Execute next instruction if label was the last thing copied"); + inst_lib.AddInst("SetFlow", Inst_Set_Flow, 1, "Set flow head to register value"); + /* + inst_lib.AddInst("Dec", Inst_Dec, 1, "Decrement value in reg Arg1"); + inst_lib.AddInst("Not", Inst_Not, 1, "Logically toggle value in reg Arg1"); + inst_lib.AddInst("SetReg", Inst_SetReg, 2, "Set reg Arg1 to numerical value Arg2"); + inst_lib.AddInst("Add", Inst_Add, 3, "regs: Arg3 = Arg1 + Arg2"); + inst_lib.AddInst("Sub", Inst_Sub, 3, "regs: Arg3 = Arg1 - Arg2"); + inst_lib.AddInst("Mult", Inst_Mult, 3, "regs: Arg3 = Arg1 * Arg2"); + inst_lib.AddInst("Div", Inst_Div, 3, "regs: Arg3 = Arg1 / Arg2"); + inst_lib.AddInst("Mod", Inst_Mod, 3, "regs: Arg3 = Arg1 % Arg2"); + inst_lib.AddInst("TestEqu", Inst_TestEqu, 3, "regs: Arg3 = (Arg1 == Arg2)"); + inst_lib.AddInst("TestNEqu", Inst_TestNEqu, 3, "regs: Arg3 = (Arg1 != Arg2)"); + inst_lib.AddInst("TestLess", Inst_TestLess, 3, "regs: Arg3 = (Arg1 < Arg2)"); + inst_lib.AddInst("If", Inst_If, 2, "If reg Arg1 != 0, scope -> Arg2; else skip scope", ScopeType::BASIC, 1); + inst_lib.AddInst("While", Inst_While, 2, "Until reg Arg1 != 0, repeat scope Arg2; else skip", ScopeType::LOOP, 1); + inst_lib.AddInst("Countdown", Inst_Countdown, 2, "Countdown reg Arg1 to zero; scope to Arg2", ScopeType::LOOP, 1); + inst_lib.AddInst("Break", Inst_Break, 1, "Break out of scope Arg1"); + inst_lib.AddInst("Scope", Inst_Scope, 1, "Enter scope Arg1", ScopeType::BASIC, 0); + inst_lib.AddInst("Define", Inst_Define, 2, "Build function Arg1 in scope Arg2", ScopeType::FUNCTION, 1); + inst_lib.AddInst("Call", Inst_Call, 1, "Call previously defined function Arg1"); + inst_lib.AddInst("Push", Inst_Push, 2, "Push reg Arg1 onto stack Arg2"); + inst_lib.AddInst("Pop", Inst_Pop, 2, "Pop stack Arg1 into reg Arg2"); + inst_lib.AddInst("Input", Inst_Input, 2, "Pull next value from input Arg1 into reg Arg2"); + inst_lib.AddInst("Output", Inst_Output, 2, "Push reg Arg1 into output Arg2"); + inst_lib.AddInst("CopyVal", Inst_CopyVal, 2, "Copy reg Arg1 into reg Arg2"); + inst_lib.AddInst("ScopeReg", Inst_ScopeReg, 1, "Backup reg Arg1; restore at end of scope"); + */ + + //for (size_t i = 0; i < hardware_t::NUM_REGS; i++) { + // inst_lib.AddArg(to_string((int)i), i); // Args can be called by value + // inst_lib.AddArg(to_string("Reg", 'A'+(char)i), i); // ...or as a register. + //} + } + + return inst_lib; + } + }; + +} + +#endif // #ifndef EMP_HARDWARE_VIRTUALCPU_INSTLIB_HPP_INCLUDE diff --git a/include/emp/hardware/signalgp_utils.hpp b/include/emp/hardware/signalgp_utils.hpp index 475f7eb9d7..5a2a39eb89 100644 --- a/include/emp/hardware/signalgp_utils.hpp +++ b/include/emp/hardware/signalgp_utils.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018-2022 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file signalgp_utils.hpp + * @file * @brief Helper functions for working with SignalGP virtual hardware/programs. * @todo Mutator class * @todo tests @@ -13,7 +14,9 @@ #define EMP_HARDWARE_SIGNALGP_UTILS_HPP_INCLUDE #include +#include #include +#include #include #include @@ -33,6 +36,7 @@ namespace emp { /// @param unique_from - Other tags that the tag being generated should be unique with respect to. template BitSet GenRandSignalGPTag(emp::Random & rnd, const emp::vector> & unique_from=emp::vector>()) { + #ifndef DOXYGEN_SHOULD_SKIP_THIS using tag_t = BitSet; emp_assert(unique_from.size() < emp::Pow2(TAG_WIDTH), "Tag width is not large enough to be able to guarantee requested number of unique tags"); tag_t new_tag(rnd, 0.5); // Make a random tag. @@ -48,6 +52,7 @@ namespace emp { } } return new_tag; + #endif // DOXYGEN_SHOULD_SKIP_THIS } @@ -217,9 +222,8 @@ namespace emp { /// - Single-instruction substitution (applied per-instruction) /// - Single-instruction insertions and deletions (each applied per-instruction) /// - Instruction-tag bit-flips (applied per-bit) - /// - Instruction-argument substitutions (applied per-argument) - /// NOTE: could use some feedback on this! - /// - Not loving the inconsistency between rates and constraints at the moment. + /// - Instruction-argument substitutions (applied per-argument) NOTE: could use some feedback on this! + /// - Not loving the inconsistency between rates and constraints at the moment. template< size_t TAG_WIDTH, typename TRAIT_T=double, @@ -664,7 +668,7 @@ namespace emp { fun_t new_fun(program[fID].GetAffinity()); size_t expected_func_len = program[fID].GetSize(); // Compute number and location of insertions. - const uint32_t num_ins = rnd.GetRandBinomial(program[fID].GetSize(), INST_INS__PER_INST()); + const uint32_t num_ins = rnd.GetBinomial(program[fID].GetSize(), INST_INS__PER_INST()); emp::vector ins_locs; if (num_ins > 0) { ins_locs = emp::RandomUIntVector(rnd, num_ins, 0, program[fID].GetSize()); diff --git a/include/emp/in_progress/AST.hpp b/include/emp/in_progress/AST.hpp new file mode 100644 index 0000000000..f55ae94f46 --- /dev/null +++ b/include/emp/in_progress/AST.hpp @@ -0,0 +1,60 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ +/** + * @file + * @brief Tools to build an Abstract Syntax Tree. + * @note Status: ALPHA + */ + +#ifndef EMP_IN_PROGRESS_AST_HPP_INCLUDE +#define EMP_IN_PROGRESS_AST_HPP_INCLUDE + +#include + +#include "../base/assert.hpp" +#include "../base/error.hpp" +#include "../meta/TypeID.hpp" + +namespace emp { + + template + struct AST { + + // Base class for all AST nodes. + class Node { + protected: + emp::Ptr parent; + public: + Node(emp::Ptr in_parent=nullptr) : parent(in_parent) { } + virtual ~Node(); + + emp::Ptr GetParent() { return parent; } + void SetParent(emp::Ptr in_parent) { parent = in_parent; } + + virtual std::string GetName() const = 0; + virtual emp::TypeID GetType() const = 0; + + virtual bool IsLeaf() const { return false; } + virtual bool IsInternal() const { return false; } + + virtual size_t GetNumChildren() const { return 0; } + virtual emp::Ptr GetChild(size_t /* id */) { + emp_error("Calling GetChild on AST::Node with no children.")); + return nullptr; + } + + // virtual emp::Ptr GetScope() { return parent ? parent->GetScope() : nullptr; } + // virtual entry_ptr_t Process() = 0; + virtual std::function AsMathFunction() = 0; + + virtual void Write(std::ostream & /* os */=std::cout, + const std::string & /* offset */="") const { } + } + + }; +} + +#endif // #ifndef EMP_IN_PROGRESS_AST_HPP_INCLUDE diff --git a/include/emp/in_progress/BatchConfig.hpp b/include/emp/in_progress/BatchConfig.hpp index 756b897bc2..c344e4f411 100644 --- a/include/emp/in_progress/BatchConfig.hpp +++ b/include/emp/in_progress/BatchConfig.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file BatchConfig.hpp + * @file * @brief A tool to control a series of runs and keep them updated. * * Development notes: currently doesn't compile because of last line. @@ -13,6 +14,7 @@ #define EMP_IN_PROGRESS_BATCHCONFIG_HPP_INCLUDE #include +#include #include "../base/vector.hpp" diff --git a/include/emp/in_progress/ConfigLexer.hpp b/include/emp/in_progress/ConfigLexer.hpp index d7f7078e94..2af0db3542 100644 --- a/include/emp/in_progress/ConfigLexer.hpp +++ b/include/emp/in_progress/ConfigLexer.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file ConfigLexer.hpp + * @file * @brief A simple lexer for the Empirical configuration language. * * Development notes: Initially building the lexer to be language specific, but a diff --git a/include/emp/in_progress/ConfigParser.hpp b/include/emp/in_progress/ConfigParser.hpp index b81b46088f..f5b4c1db7b 100644 --- a/include/emp/in_progress/ConfigParser.hpp +++ b/include/emp/in_progress/ConfigParser.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file ConfigParser.hpp + * @file * @brief A simple parser for the Empirical configuration language. * * This parser is being implemented as a pushdown automata. diff --git a/include/emp/in_progress/Empower/Empower.hpp b/include/emp/in_progress/Empower/Empower.hpp index 5e9f8ba202..4d1b763ad0 100644 --- a/include/emp/in_progress/Empower/Empower.hpp +++ b/include/emp/in_progress/Empower/Empower.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Empower.hpp + * @file * @brief A scripting language built inside of C++ * * Empower is a scripting language built inside of Empirical to simplify and the use of fast @@ -34,6 +35,7 @@ #include #include +#include #include #include "../base/Ptr.hpp" diff --git a/include/emp/in_progress/Empower/MemoryImage.hpp b/include/emp/in_progress/Empower/MemoryImage.hpp index adf3a8e80e..21c9f7a959 100644 --- a/include/emp/in_progress/Empower/MemoryImage.hpp +++ b/include/emp/in_progress/Empower/MemoryImage.hpp @@ -1,15 +1,18 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file MemoryImage.hpp + * @file * @brief A collection of arbitrary objects stored in a chunk of memory. */ #ifndef EMP_IN_PROGRESS_EMPOWER_MEMORYIMAGE_HPP_INCLUDE #define EMP_IN_PROGRESS_EMPOWER_MEMORYIMAGE_HPP_INCLUDE +#include + #include "../base/assert.hpp" #include "../base/Ptr.hpp" #include "../base/vector.hpp" diff --git a/include/emp/in_progress/Empower/Struct.hpp b/include/emp/in_progress/Empower/Struct.hpp index efacb088cc..12e31e50db 100644 --- a/include/emp/in_progress/Empower/Struct.hpp +++ b/include/emp/in_progress/Empower/Struct.hpp @@ -1,15 +1,18 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Struct.hpp + * @file * @brief Struct is a set of active variables, grouped by name (organized by a specific StructType) */ #ifndef EMP_IN_PROGRESS_EMPOWER_STRUCT_HPP_INCLUDE #define EMP_IN_PROGRESS_EMPOWER_STRUCT_HPP_INCLUDE +#include + #include "../base/assert.hpp" #include "../base/Ptr.hpp" #include "../base/vector.hpp" diff --git a/include/emp/in_progress/Empower/StructType.hpp b/include/emp/in_progress/Empower/StructType.hpp index fb3e31974d..959b215ff5 100644 --- a/include/emp/in_progress/Empower/StructType.hpp +++ b/include/emp/in_progress/Empower/StructType.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file StructType.hpp + * @file * @brief StructType maps variables to a MemoryImage; Struct is an instance of StructType * * @todo Immediately before setting a StructType to active, we can optimize variable ordering. @@ -12,6 +13,7 @@ #ifndef EMP_IN_PROGRESS_EMPOWER_STRUCTTYPE_HPP_INCLUDE #define EMP_IN_PROGRESS_EMPOWER_STRUCTTYPE_HPP_INCLUDE +#include #include #include "../base/assert.hpp" diff --git a/include/emp/in_progress/Empower/Type.hpp b/include/emp/in_progress/Empower/Type.hpp index dfaa0fafb8..26125b8a73 100644 --- a/include/emp/in_progress/Empower/Type.hpp +++ b/include/emp/in_progress/Empower/Type.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Type.hpp + * @file * @brief A collection of information about how to manage variables of a specified type. */ @@ -11,6 +12,7 @@ #define EMP_IN_PROGRESS_EMPOWER_TYPE_HPP_INCLUDE #include +#include #include #include "../base/assert.hpp" diff --git a/include/emp/in_progress/Empower/TypeManager.hpp b/include/emp/in_progress/Empower/TypeManager.hpp index 2e2b138b70..bf4e58d2dc 100644 --- a/include/emp/in_progress/Empower/TypeManager.hpp +++ b/include/emp/in_progress/Empower/TypeManager.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file TypeManager.hpp + * @file * @brief Handles creation or retrieval of type objects. */ @@ -11,6 +12,7 @@ #define EMP_IN_PROGRESS_EMPOWER_TYPEMANAGER_HPP_INCLUDE #include +#include #include #include diff --git a/include/emp/in_progress/Empower/Var.hpp b/include/emp/in_progress/Empower/Var.hpp index 84061f3526..2df7bb0611 100644 --- a/include/emp/in_progress/Empower/Var.hpp +++ b/include/emp/in_progress/Empower/Var.hpp @@ -1,15 +1,18 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Var.hpp + * @file * @brief A collection of information about a single, instantiated variable in Empower */ #ifndef EMP_IN_PROGRESS_EMPOWER_VAR_HPP_INCLUDE #define EMP_IN_PROGRESS_EMPOWER_VAR_HPP_INCLUDE +#include + #include "../base/assert.hpp" #include "../base/Ptr.hpp" #include "../meta/TypeID.hpp" diff --git a/include/emp/in_progress/Empower/VarInfo.hpp b/include/emp/in_progress/Empower/VarInfo.hpp index 7d0e30477d..49169e78be 100644 --- a/include/emp/in_progress/Empower/VarInfo.hpp +++ b/include/emp/in_progress/Empower/VarInfo.hpp @@ -1,15 +1,18 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file VarInfo.hpp + * @file * @brief Generic info about a single variable (across MemoryImages) */ #ifndef EMP_IN_PROGRESS_EMPOWER_VARINFO_HPP_INCLUDE #define EMP_IN_PROGRESS_EMPOWER_VARINFO_HPP_INCLUDE +#include + #include "../base/assert.hpp" #include "../base/Ptr.hpp" diff --git a/include/emp/in_progress/Empower2/Var.hpp b/include/emp/in_progress/Empower2/Var.hpp index fd9604f255..4aa176850d 100644 --- a/include/emp/in_progress/Empower2/Var.hpp +++ b/include/emp/in_progress/Empower2/Var.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file Var.hpp + * @file * @brief A collection of information about a single, instantiated variable in Empower * * @@ -18,6 +19,7 @@ #ifndef EMP_IN_PROGRESS_EMPOWER2_VAR_HPP_INCLUDE #define EMP_IN_PROGRESS_EMPOWER2_VAR_HPP_INCLUDE +#include #include #include "../base/assert.hpp" diff --git a/include/emp/in_progress/Parser.hpp b/include/emp/in_progress/Parser.hpp index d681d64afa..6d3568b228 100644 --- a/include/emp/in_progress/Parser.hpp +++ b/include/emp/in_progress/Parser.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2019. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2019. - * - * @file Parser.hpp + * @file * @brief A general-purpose, fast parser. * @note Status: DEVELOPMENT * @@ -27,6 +28,7 @@ #define EMP_IN_PROGRESS_PARSER_HPP_INCLUDE #include +#include #include "../base/vector.hpp" #include "../bits/BitVector.hpp" diff --git a/include/emp/in_progress/Ptr-overload-fix.hpp b/include/emp/in_progress/Ptr-overload-fix.hpp new file mode 100644 index 0000000000..d7078ad0b9 --- /dev/null +++ b/include/emp/in_progress/Ptr-overload-fix.hpp @@ -0,0 +1,1068 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2022 +*/ +/** + * @file + * @brief A wrapper for pointers that does careful memory tracking (but only in debug mode). + * @note Status: BETA + * + * Ptr objects behave as normal pointers under most conditions. However, if a program is + * compiled with EMP_TRACK_MEM set, then these pointers perform extra tests to ensure that + * they point to valid memory and that memory is freed before pointers are released. + * + * If you want to prevent pointers to pointers (a common source of errors, but MAY be done + * intentionally) you can define EMP_NO_PTR_TO_PTR + * + * If you trip an assert, you can re-do the run a track a specific pointer by defining + * EMP_ABORT_PTR_NEW or EMP_ABORT_PTR_DELETE to the ID of the pointer in question. + * + * For example: -DEMP_ABORT_PTR_NEW=1691 + * + * This will allow you to track the pointer more easily in a debugger. + * + * @todo Track information about emp::vector and emp::array objects to make sure we don't + * point directly into them? (A resize() could make such pointers invalid!) Or better, warn + * it vector memory could have moved. + * @todo Get working with threads + */ + +#ifndef EMP_IN_PROGRESS_PTR_OVERLOAD_FIX_HPP_INCLUDE +#define EMP_IN_PROGRESS_PTR_OVERLOAD_FIX_HPP_INCLUDE + +#include +#include + +#include "assert.hpp" +#include "vector.hpp" + +namespace emp { + + // ------------ Pre-declare some helper types and functions -------------- + + template class Ptr; + + + template + inline void FillMemory(emp::Ptr mem_ptr, const size_t num_bytes, T fill_value); + + /// Fill an array by repeatedly calling the provided fill functions. + template + inline void FillMemoryFunction(emp::Ptr mem_ptr, const size_t num_bytes, T fill_fun); + + #ifndef DOXYGEN_SHOULD_SKIP_THIS + namespace internal { + /// An anonymous log2 calculator for hashing below. + static constexpr size_t Log2(size_t x) { return x <= 1 ? 0 : (Log2(x/2) + 1); } + + static bool ptr_debug = false; + } + #endif // DOXYGEN_SHOULD_SKIP_THIS + inline void SetPtrDebug(bool _d = true) { internal::ptr_debug = _d; } + inline bool GetPtrDebug() { return internal::ptr_debug; } + + enum class PtrStatus { DELETED=0, ACTIVE, ARRAY }; + + class PtrInfo { + private: + const void * ptr; ///< Which pointer are we keeping data on? + int count; ///< How many of this pointer do we have? + PtrStatus status; ///< Has this pointer been deleted? (i.e., if so, don't access it!) + size_t array_bytes; ///< How big is the array pointed to (in bytes)? + + public: + PtrInfo(const void * _ptr) : ptr(_ptr), count(1), status(PtrStatus::ACTIVE), array_bytes(0) { + if (internal::ptr_debug) std::cout << "Created info for pointer: " << ptr << std::endl; + } + PtrInfo(const void * _ptr, size_t _array_bytes) + : ptr(_ptr), count(1), status(PtrStatus::ARRAY), array_bytes(_array_bytes) + { + emp_assert(_array_bytes >= 1); + if (internal::ptr_debug) { + std::cout << "Created info for array pointer (bytes=" << array_bytes << "): " + << ptr << std::endl; + } + } + PtrInfo(const PtrInfo &) = default; + PtrInfo(PtrInfo &&) = default; + PtrInfo & operator=(const PtrInfo &) & = default; + PtrInfo & operator=(PtrInfo &&) & = default; + + ~PtrInfo() { + if (internal::ptr_debug) std::cout << "Deleted info for pointer " << ptr << std::endl; + } + + /// What pointer does this one hold information about? + const void * GetPtr() const noexcept { return ptr; } + + /// How many Ptr objects point to the associated position? + int GetCount() const noexcept { return count; } + + /// If this ptr is to an array, how many bytes large is the array (may be different from size!) + size_t GetArrayBytes() const noexcept { return array_bytes; } + + /// Is this pointer currently valid to access? + bool IsActive() const noexcept { return (bool) status; } + + /// Is this pointer pointing to an array? + bool IsArray() const noexcept { return status == PtrStatus::ARRAY; } + + /// Denote that this pointer is an array. + void SetArray(size_t bytes) noexcept { array_bytes = bytes; status = PtrStatus::ARRAY; } + + /// Add one more pointer. + void Inc([[maybe_unused]] const size_t id) { + if (internal::ptr_debug) std::cout << "Inc info for pointer " << ptr << std::endl; + emp_assert(status != PtrStatus::DELETED, "Incrementing deleted pointer!", id); + count++; + } + + /// Remove a pointer. + void Dec([[maybe_unused]] const size_t id) { + if (internal::ptr_debug) std::cout << "Dec info for pointer " << ptr << std::endl; + + // Make sure that we have more than one copy, -or- we've already deleted this pointer + emp_assert(count > 1 || status == PtrStatus::DELETED, "Removing last reference to owned Ptr!", id); + count--; + } + + /// Indicate that the associated position has been deleted. + void MarkDeleted() { + if (internal::ptr_debug) std::cout << "Marked deleted for pointer " << ptr << std::endl; + status = PtrStatus::DELETED; + } + + /// Debug utility to determine if everything looks okay with this pointer's information. + bool OK() const noexcept { + if (ptr == nullptr) return false; // Should not have info for a null pointer. + if (status == PtrStatus::ARRAY) { + if (array_bytes == 0) return false; // Arrays cannot be size 0. + if (count == 0) return false; // Active arrays must have pointers to them. + } + if (status == PtrStatus::ACTIVE) { + if (array_bytes > 0) return false; // non-arrays must be array size 0. + if (count == 0) return false; // Active pointers must have references to them. + } + return true; + } + }; + + + /// Facilitate tracking of all Ptr objects in this run. + class PtrTracker { + private: + std::unordered_map ptr_id; ///< Associate raw pointers with unique IDs + emp::vector id_info; ///< Associate IDs with pointer information. + static constexpr size_t UNTRACKED_ID = (size_t) -1; + + // Make PtrTracker a singleton. + PtrTracker() : ptr_id(), id_info() { + std::cout << "EMP_TRACK_MEM: Pointer tracking is active!\n"; + } + PtrTracker(const PtrTracker &) = delete; + PtrTracker(PtrTracker &&) = delete; + PtrTracker & operator=(const PtrTracker &) = delete; + PtrTracker & operator=(PtrTracker &&) = delete; + + public: + ~PtrTracker() { + // Track stats about pointer record. + size_t total = 0; + size_t remain = 0; + emp::vector undeleted_info; + + // Scan through live pointers and make sure all have been deleted. + for (const auto & info : id_info) { + total++; + if (info.GetCount()) remain++; + + if (info.IsActive()) { + undeleted_info.push_back(info); + } + } + + if (undeleted_info.size()) { + std::cerr << undeleted_info.size() << " undeleted pointers at end of execution.\n"; + for (size_t i = 0; i < undeleted_info.size() && i < 10; ++i) { + const auto & info = undeleted_info[i]; + std::cerr << " PTR=" << info.GetPtr() + << " count=" << info.GetCount() + << " active=" << info.IsActive() + << " id=" << ptr_id[info.GetPtr()] + << std::endl; + } + if (undeleted_info.size() > 10) { + std::cerr << " ..." << std::endl; + } + abort(); + } + + std::cout << "EMP_TRACK_MEM: No memory leaks found!\n " + << total << " pointers found; "; + if (remain) { + std::cout << remain << " still exist with a non-null value (but have been properly deleted)"; + } else std::cout << "all have been cleaned up fully."; + std::cout << std::endl; + } + + /// Treat this class as a singleton with a single Get() method to retrieve it. + static PtrTracker & Get() { static PtrTracker tracker; return tracker; } + + /// Get the info associated with an existing pointer. + PtrInfo & GetInfo(const void * ptr) { return id_info[ptr_id[ptr]]; } + PtrInfo & GetInfo(size_t id) { return id_info[id]; } + + /// Determine if a pointer is being tracked. + bool HasPtr(const void * ptr) const { + if (internal::ptr_debug) std::cout << "HasPtr: " << ptr << std::endl; + return ptr_id.find(ptr) != ptr_id.end(); + } + + /// Retrieve the ID associated with a pointer. + size_t GetCurID(const void * ptr) { emp_assert(HasPtr(ptr)); return ptr_id[ptr]; } + + /// Lookup how many pointers are being tracked. + size_t GetNumIDs() const { return id_info.size(); } + + /// How big is an array associated with an ID? + size_t GetArrayBytes(size_t id) const { return id_info[id].GetArrayBytes(); } + + /// Check if an ID is for a pointer that has been deleted. + bool IsDeleted(size_t id) const { + if (id == UNTRACKED_ID) return false; // Not tracked, so not deleted. + if (internal::ptr_debug) std::cout << "IsDeleted: " << id << std::endl; + return !id_info[id].IsActive(); + } + + /// Is a pointer active and ready to be used? + bool IsActive(const void * ptr) { + if (internal::ptr_debug) std::cout << "IsActive: " << ptr << std::endl; + if (ptr_id.find(ptr) == ptr_id.end()) return false; // Not in database. + return GetInfo(ptr).IsActive(); + } + + /// Is a pointer id associated with a pointer that's active and ready to be used? + bool IsActiveID(size_t id) { + if (id == UNTRACKED_ID) return false; + if (id >= id_info.size()) return false; + return id_info[id].IsActive(); + } + + /// Is an ID associated with an array? + bool IsArrayID(size_t id) { + if (internal::ptr_debug) std::cout << "IsArrayID: " << id << std::endl; + if (id == UNTRACKED_ID) return false; + if (id >= id_info.size()) return false; + return id_info[id].IsArray(); + } + + /// How many Ptr objects are associated with an ID? + int GetIDCount(size_t id) const { + if (internal::ptr_debug) std::cout << "Count: " << id << std::endl; + return id_info[id].GetCount(); + } + + /// This pointer was just created as a Ptr! + size_t New(const void * ptr) { + emp_assert(ptr); // Cannot track a null pointer. + size_t id = id_info.size(); +#ifdef EMP_ABORT_PTR_NEW + if (id == EMP_ABORT_PTR_NEW) { + std::cerr << "Aborting at creation of Ptr id " << id << std::endl; + abort(); + } +#endif + if (internal::ptr_debug) std::cout << "New: " << id << " (" << ptr << ")" << std::endl; + // Make sure pointer is not already stored -OR- has been deleted (since re-use is possible). + emp_assert(!HasPtr(ptr) || IsDeleted(GetCurID(ptr)), id); + id_info.emplace_back(ptr); + ptr_id[ptr] = id; + return id; + } + + /// This pointer was just created as a Ptr ARRAY! + size_t NewArray(const void * ptr, size_t array_bytes) { + size_t id = New(ptr); // Build the new pointer. + if (internal::ptr_debug) std::cout << " ...Array of size " << array_bytes << std::endl; + id_info[id].SetArray(array_bytes); + return id; + } + + /// Increment the number of Pointers associated with an ID + void IncID(size_t id) { + if (id == UNTRACKED_ID) return; // Not tracked! + if (internal::ptr_debug) std::cout << "Inc: " << id << std::endl; + id_info[id].Inc(id); + } + + /// Decrement the number of Pointers associated with an ID + void DecID(size_t id) { + if (id == UNTRACKED_ID) return; // Not tracked! + auto & info = id_info[id]; + if (internal::ptr_debug) std::cout << "Dec: " << id << "(" << info.GetPtr() << ")" << std::endl; + emp_assert(info.GetCount() > 0, "Decrementing Ptr, but already zero!", + id, info.GetPtr(), info.IsActive()); + info.Dec(id); + } + + /// Mark the pointers associated with this ID as deleted. + void MarkDeleted(size_t id) { +#ifdef EMP_ABORT_PTR_DELETE + if (id == EMP_ABORT_PTR_DELETE) { + std::cerr << "Aborting at deletion of Ptr id " << id << std::endl; + abort(); + } +#endif + if (internal::ptr_debug) std::cout << "Delete: " << id << std::endl; + emp_assert(id_info[id].IsActive(), "Deleting same emp::Ptr a second time!", id); + id_info[id].MarkDeleted(); + } + }; + + +////////////////////////////////// +// +// --- Ptr implementation --- +// +////////////////////////////////// + +#ifdef EMP_TRACK_MEM + + namespace { + // Debug information provided for each pointer type. + struct PtrDebug { + size_t current; + size_t total; + PtrDebug() : current(0), total(0) { ; } + void AddPtr() { current++; total++; } + void RemovePtr() { current--; } + }; + } + + /// Base class with common functionality (that should not exist in void pointers) + template + class BasePtr { + public: + TYPE * ptr; ///< The raw pointer associated with this Ptr object. + size_t id; ///< A unique ID for this pointer type. + + static constexpr size_t UNTRACKED_ID = (size_t) -1; + + BasePtr(TYPE * in_ptr, size_t in_id) : ptr(in_ptr), id(in_id) { + #ifdef EMP_NO_PTR_TO_PTR + emp_assert(!std::is_pointer_v, "Pointers to pointers are disallowed!"); + #endif + } + + static PtrTracker & Tracker() { return PtrTracker::Get(); } // Single tracker for al Ptr types + + /// Dereference a pointer. + [[nodiscard]] TYPE & operator*() const { + // Make sure a pointer is active and non-null before we dereference it. + emp_assert(Tracker().IsDeleted(id) == false /*, typeid(TYPE).name() */, id); + emp_assert(ptr != nullptr, "Do not dereference a null pointer!"); + return *ptr; + } + + /// Follow a pointer. + TYPE * operator->() const { + // Make sure a pointer is active before we follow it. + emp_assert(Tracker().IsDeleted(id) == false /*, typeid(TYPE).name() */, id); + emp_assert(ptr != nullptr, "Do not follow a null pointer!"); + return ptr; + } + + /// Indexing into array + TYPE & operator[](size_t pos) const { + emp_assert(Tracker().IsDeleted(id) == false /*, typeid(TYPE).name() */, id); + emp_assert(id == UNTRACKED_ID || Tracker().IsArrayID(id), "Only arrays can be indexed into.", id); + emp_assert(id == UNTRACKED_ID || Tracker().GetArrayBytes(id) > (pos*sizeof(TYPE)), + "Indexing out of range.", id, ptr, pos, sizeof(TYPE), Tracker().GetArrayBytes(id)); + emp_assert(ptr != nullptr, "Do not follow a null pointer!"); + return ptr[pos]; + } + + }; + + + /// Base class with functionality only needed in void pointers. + template <> + class BasePtr { + public: + void * ptr; ///< The raw pointer associated with this Ptr object. + size_t id; ///< A unique ID for this pointer type. + + BasePtr(void * in_ptr, size_t in_id) : ptr(in_ptr), id(in_id) { } + static PtrTracker & Tracker() { return PtrTracker::Get(); } // Single tracker for al Ptr types + }; + + /// Base class with functionality only needed in void pointers. + template <> + class BasePtr { + public: + const void * ptr; ///< The raw pointer associated with this Ptr object. + size_t id; ///< A unique ID for this pointer type. + + BasePtr(const void * in_ptr, size_t in_id) : ptr(in_ptr), id(in_id) { } + static PtrTracker & Tracker() { return PtrTracker::Get(); } // Single tracker for al Ptr types + }; + + /// Main Ptr class DEBUG definition. + template + class Ptr : public BasePtr { + public: + using BasePtr::ptr; + using BasePtr::id; + using BasePtr::Tracker; + + using element_type = TYPE; ///< Type being pointed at. + + static constexpr size_t UNTRACKED_ID = (size_t) -1; + + static PtrDebug & DebugInfo() { static PtrDebug info; return info; } // Debug info for each type + + /// Construct a null Ptr by default. + Ptr() : BasePtr(nullptr, UNTRACKED_ID) { + if (internal::ptr_debug) { + std::cout << "null construct." << std::endl; + } + } + + /// Construct using copy constructor + Ptr(const Ptr & _in) : BasePtr(_in.ptr, _in.id) { + if (internal::ptr_debug) std::cout << "copy construct: " << ptr << std::endl; + Tracker().IncID(id); + } + + /// Construct from a raw pointer of compatable type. + template + Ptr(std::enable_if_t,T2*> in_ptr, + bool track=false) + : BasePtr(in_ptr, UNTRACKED_ID) + { + if (internal::ptr_debug) { + std::cout << "raw construct: " << ((void *) ptr) << ". track=" << track << std::endl; + } + + // If this pointer is already active, link to it. + if (Tracker().IsActive(ptr)) { + id = Tracker().GetCurID(ptr); + Tracker().IncID(id); + } + // If we are not already tracking this pointer, but should be, add it. + else if (track) { + id = Tracker().New(ptr); + DebugInfo().AddPtr(); + } + } + + /// Construct from a raw pointer of compatible ARRAY type. + template + Ptr(std::enable_if_t,T2*> _ptr, + size_t array_size, + bool track) + : BasePtr(_ptr, UNTRACKED_ID) + { + const size_t array_bytes = array_size * sizeof(T2); + if (internal::ptr_debug) std::cout << "raw ARRAY construct: " << ptr + << ". size=" << array_size << "(" << array_bytes + << " bytes); track=" << track << std::endl; + + // If this pointer is already active, link to it. + if (Tracker().IsActive(ptr)) { + id = Tracker().GetCurID(ptr); + Tracker().IncID(id); + emp_assert(Tracker().GetArrayBytes(id) == array_bytes); // Make sure pointer is consistent. + } + // If we are not already tracking this pointer, but should be, add it. + else if (track) { + id = Tracker().NewArray(ptr, array_bytes); + DebugInfo().AddPtr(); + } + } + + /// Construct from another Ptr<> object of compatible type. + template + Ptr(std::enable_if_t, Ptr > _in) + : BasePtr(_in.Raw(), _in.GetID()) + { + if (internal::ptr_debug) std::cout << "inexact copy construct: " << ptr << std::endl; + Tracker().IncID(id); + } + + /// Construct from nullptr. + Ptr(std::nullptr_t) : Ptr() { + if (internal::ptr_debug) std::cout << "null construct 2." << std::endl; + } + + /// Destructor. + ~Ptr() { + if (internal::ptr_debug) { + std::cout << "destructing Ptr instance "; + if (ptr) std::cout << id << " (" << ((void *) ptr) << ")\n"; + else std::cout << "(nullptr)\n"; + } + Tracker().DecID(id); + } + + /// Is this Ptr currently nullptr? + [[nodiscard]] bool IsNull() const { return ptr == nullptr; } + + /// Convert this Ptr to a raw pointer that isn't going to be tracked. + [[nodiscard]] TYPE * Raw() const { + emp_assert(Tracker().IsDeleted(id) == false, "Do not convert deleted Ptr to raw.", id); + return ptr; + } + + /// Convert this Ptr to a raw pointer of a position in an array. + [[nodiscard]] TYPE * Raw(size_t pos) const { + emp_assert(Tracker().IsDeleted(id) == false, "Do not convert deleted Ptr to array raw.", id); + return &(ptr[pos]); + } + + /// Cast this Ptr to a different type. + template + [[nodiscard]] Ptr Cast() const { + emp_assert(Tracker().IsDeleted(id) == false, "Do not cast deleted pointers.", id); + return (T2*) ptr; + } + + /// Change constness of this Ptr's target; throw an assert of the cast fails. + template + [[nodiscard]] Ptr ConstCast() const { + emp_assert(Tracker().IsDeleted(id) == false, "Do not cast deleted pointers.", id); + emp_assert( (std::is_same< std::remove_const_t , std::remove_const_t >()) ); + return const_cast(ptr); + } + + /// Dynamically cast this Ptr to another type; throw an assert of the cast fails. + template + [[nodiscard]] Ptr DynamicCast() const { + emp_assert(Tracker().IsDeleted(id) == false, "Do not cast deleted pointers.", id); + return dynamic_cast(ptr); + } + + /// Reinterpret this Ptr to another type; throw an assert of the cast fails. + template + [[nodiscard]] Ptr ReinterpretCast() const { + emp_assert(Tracker().IsDeleted(id) == false, "Do not cast deleted pointers.", id); + #ifdef EMP_NO_PTR_TO_PTR + emp_assert(!std::is_pointer_v, "Reinterpreting as pointers to pointers is disallowed!"); + #endif + return reinterpret_cast(ptr); + } + + /// Get the unique ID associated with this pointer. + size_t GetID() const { return id; } + + /// Reallocate this Ptr to a newly allocated value using arguments passed in. + template + void New(T &&... args) { + Tracker().DecID(id); // Remove a pointer to any old memory... + + ptr = new TYPE(std::forward(args)...); // Special new that uses allocated space. + // ptr = (TYPE*) malloc (sizeof(TYPE)); // Build a new raw pointer. + // emp_emscripten_assert(ptr); // No exceptions in emscripten; assert alloc! + // ptr = new (ptr) TYPE(std::forward(args)...); // Special new that uses allocated space. + + if (internal::ptr_debug) std::cout << "Ptr::New() : " << ptr << std::endl; + id = Tracker().New(ptr); // And track it! + DebugInfo().AddPtr(); + } + + /// Reallocate this Ptr to a newly allocated array using the size passed in. + // template + // void NewArray(size_t array_size, Ts &&... args) { + void NewArray(size_t array_size) { + Tracker().DecID(id); // Remove a pointer to any old memory... + + // @CAO: This next portion of code is allocating an array of the appropriate type. + // We are currently using "new", but should shift over to malloc since new throws an + // exception when there's a problem, which will trigger an abort in Emscripten mode. + // We'd rather be able to identify a more specific problem. + ptr = new TYPE[array_size]; // Build a new raw pointer to an array. + // ptr = (TYPE*) malloc (array_size * sizeof(TYPE)); // Build a new raw pointer. + // emp_emscripten_assert(ptr, array_size); // No exceptions in emscripten; assert alloc! + // for (size_t i = 0; i < array_size; i++) { + // new (ptr + i*sizeof(TYPE)) TYPE(args...); + // } + + if (internal::ptr_debug) std::cout << "Ptr::NewArray() : " << ptr << std::endl; + id = Tracker().NewArray(ptr, array_size * sizeof(TYPE)); // And track it! + DebugInfo().AddPtr(); + } + + /// Delete this pointer (must NOT be an array). + void Delete() { + emp_assert(ptr, "Trying to delete null Ptr."); + emp_assert(id < Tracker().GetNumIDs(), id, "Trying to delete Ptr that we are not responsible for."); + emp_assert(Tracker().IsArrayID(id) == false, id, "Trying to delete array pointer as non-array."); + emp_assert(Tracker().IsActive(ptr), id, "Trying to delete inactive pointer (already deleted!)"); + if (internal::ptr_debug) std::cout << "Ptr::Delete() : " << ptr << std::endl; + delete ptr; + Tracker().MarkDeleted(id); + DebugInfo().RemovePtr(); + } + + /// Delete this pointer to an array (must be an array). + void DeleteArray() { + emp_assert(id < Tracker().GetNumIDs(), id, "Trying to delete Ptr that we are not responsible for."); + emp_assert(ptr, "Trying to delete null Ptr."); + emp_assert(Tracker().IsArrayID(id), id, "Trying to delete non-array pointer as array."); + emp_assert(Tracker().IsActive(ptr), id, "Trying to delete inactive pointer (already deleted!)"); + if (internal::ptr_debug) std::cout << "Ptr::DeleteArray() : " << ptr << std::endl; + delete [] ptr; + Tracker().MarkDeleted(id); + DebugInfo().RemovePtr(); + } + + /// Convert this pointer to a hash value. + size_t Hash() const noexcept { + // Chop off useless bits of pointer... + static constexpr size_t shift = internal::Log2(1 + sizeof(TYPE)); + return (size_t)(ptr) >> shift; + } + struct hash_t { size_t operator()(const Ptr & t) const noexcept { return t.Hash(); } }; + + /// Copy assignment + Ptr & operator=(const Ptr & _in) & { + if (internal::ptr_debug) { + std::cout << "copy assignment from id " << _in.id << " to id " << id + << std::endl; + } + emp_assert(Tracker().IsDeleted(_in.id) == false, _in.id, "Do not copy deleted pointers."); + if (id != _in.id || ptr != _in.ptr) { // Assignments only need to happen if ptrs are different. + if (internal::ptr_debug) std::cout << "...pointers differ -- copying!" << std::endl; + Tracker().DecID(id); + ptr = _in.ptr; + id = _in.id; + Tracker().IncID(id); + } else { + if (internal::ptr_debug) std::cout << "...pointers same -- no copying!" << std::endl; + } + return *this; + } + + /// Assign to a raw pointer of the correct type; if this is already tracked, hooked in + /// correctly, otherwise don't track. + template + Ptr & operator=(std::enable_if_t,T2*> _in) & { + if (internal::ptr_debug) std::cout << "raw assignment" << std::endl; + + Tracker().DecID(id); // Decrement references to former pointer at this position. + ptr = _in; // Update to new pointer. + + // If this pointer is already active, link to it. + if (Tracker().IsActive(ptr)) { + id = Tracker().GetCurID(ptr); + Tracker().IncID(id); + } + // Otherwise, since this ptr was passed in as a raw pointer, we do not manage it. + else { + id = UNTRACKED_ID; + } + + return *this; + } + + /// Assign to a convertible Ptr + template + Ptr & operator=(std::enable_if_t, Ptr > _in) & { + if (internal::ptr_debug) std::cout << "convert-copy assignment" << std::endl; + emp_assert(Tracker().IsDeleted(_in.id) == false, _in.id, "Do not copy deleted pointers."); + Tracker().DecID(id); + ptr = _in.Raw(); + id = _in.GetID(); + Tracker().IncID(id); + return *this; + } + + /// Auto-cast to raw pointer type. + operator TYPE *() { + // Make sure a pointer is active before we convert it. + emp_assert(Tracker().IsDeleted(id) == false /*, typeid(TYPE).name() */, id); + + // We should not automatically convert managed pointers to raw pointers; use .Raw() + emp_assert(id != UNTRACKED_ID /*, typeid(TYPE).name() */, id, + "Use Raw() to convert to an untracked Ptr"); + return ptr; + } + + /// Does this pointer exist? + operator bool() { return ptr != nullptr; } + + /// Does this const pointer exist? + operator bool() const { return ptr != nullptr; } + + template bool operator==(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr == in_ptr.ptr; } + else { return ptr == in_ptr; } + } + template bool operator!=(const T & in_ptr) const { return !operator==(in_ptr); } + + template bool operator<(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr < in_ptr.ptr; } + else { return ptr < in_ptr; } + } + template bool operator>(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr > in_ptr.ptr; } + else { return ptr > in_ptr; } + } + template bool operator<=(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr <= in_ptr.ptr; } + else { return ptr <= in_ptr; } + } + template bool operator>=(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr >= in_ptr.ptr; } + else { return ptr >= in_ptr; } + } + + [[nodiscard]] Ptr operator+(int value) const { return ptr + value; } + [[nodiscard]] Ptr operator-(int value) const { return ptr - value; } + [[nodiscard]] Ptr operator+(size_t value) const { return ptr + value; } + [[nodiscard]] Ptr operator-(size_t value) const { return ptr - value; } + + /// Fill an array with the provided fill_value. + /// If fill_value is a function, repeatedly call function. + template + void FillMemoryFunction(const size_t num_bytes, T fill_fun) { + // Make sure a pointer is active before we write to it. + emp_assert(Tracker().IsDeleted(id) == false /*, typeid(TYPE).name() */, id); + emp_assert(id == UNTRACKED_ID || Tracker().IsArrayID(id), "Only arrays can fill memory.", id); + emp_assert(id == UNTRACKED_ID || Tracker().GetArrayBytes(id) >= num_bytes, + "Overfilling memory.", id, ptr, sizeof(TYPE), Tracker().GetArrayBytes(id)); + emp_assert(ptr != nullptr, "Do not follow a null pointer!"); + + emp::FillMemoryFunction(*this, num_bytes, fill_fun); + } + + /// Fill an array with the provided fill_value. + /// If fill_value is a function, repeatedly call function. + template + void FillMemory(const size_t num_bytes, T fill_value) { + // Make sure a pointer is active before we write to it. + emp_assert(Tracker().IsDeleted(id) == false /*, typeid(TYPE).name() */, id); + emp_assert(Tracker().IsArrayID(id) || id == UNTRACKED_ID, "Only arrays can fill memory.", id); + emp_assert(Tracker().GetArrayBytes(id) >= num_bytes, + "Overfilling memory.", id, ptr, sizeof(TYPE), Tracker().GetArrayBytes(id)); + emp_assert(ptr != nullptr, "Do not follow a null pointer!"); + + emp::FillMemory(*this, num_bytes, fill_value); + } + + /// Some debug testing functions + int DebugGetCount() const { return Tracker().GetIDCount(id); } + bool DebugIsArray() const { return Tracker().IsArrayID(id); } + size_t DebugGetArrayBytes() const { return Tracker().GetArrayBytes(id); } + bool DebugIsActive() const { return Tracker().IsActiveID(id); } + + bool OK() const { + // Untracked ID's should not have pointers in the Tracker. + if (id == UNTRACKED_ID) return !Tracker().HasPtr(ptr); + + // Make sure this pointer is linked to the correct info. + if (Tracker().GetInfo(id).GetPtr() != ptr) return false; + + // And make sure that info itself is okay. + return Tracker().GetInfo(id).OK(); + } + + // Prevent use of new and delete on Ptr + // static void* operator new(std::size_t) noexcept { + // emp_assert(false, "No Ptr::operator new; use emp::NewPtr for clarity."); + // return nullptr; + // } + // static void* operator new[](std::size_t sz) noexcept { + // emp_assert(false, "No Ptr::operator new[]; use emp::NewPtrArray for clarity."); + // return nullptr; + // } + // + // static void operator delete(void* ptr, std::size_t sz) { + // emp_assert(false, "No Ptr::operator delete; use Delete() member function for clarity."); + // } + // static void operator delete[](void* ptr, std::size_t sz) { + // emp_assert(false, "No Ptr::operator delete[]; use DeleteArray() member function for clarity."); + // } + + }; + +#else // EMP_MEM_TRACK off... + + + template + class BasePtr { + protected: + TYPE * ptr; ///< The raw pointer associated with this Ptr object. + + public: + BasePtr(TYPE * in_ptr) : ptr(in_ptr) { } + + // Dereference a pointer. + [[nodiscard]] TYPE & operator*() const { return *ptr; } + + // Follow a pointer. + TYPE * operator->() const { return ptr; } + + // Should implement operator->* to follow a pointer to a member function. + // For an example, see: + // https://stackoverflow.com/questions/27634036/overloading-operator-in-c + + // Indexing into array + TYPE & operator[](size_t pos) const { return ptr[pos]; } + }; + + /// Base class with functionality only needed in void pointers. + template <> class BasePtr { + protected: void * ptr; ///< The raw pointer associated with this Ptr object. + public: BasePtr(void * in_ptr) : ptr(in_ptr) { } + }; + + template <> class BasePtr { + protected: const void * ptr; ///< The raw pointer associated with this Ptr object. + public: BasePtr(const void * in_ptr) : ptr(in_ptr) { } + }; + + template + class Ptr : public BasePtr { + private: + using BasePtr::ptr; + + public: + using element_type = TYPE; + + /// Default constructor + Ptr() : BasePtr(nullptr) {} + + /// Copy constructor + Ptr(const Ptr & _in) : BasePtr(_in.ptr) {} + + /// Construct from raw ptr + template + Ptr(std::enable_if_t,T2*> in_ptr, bool=false) + : BasePtr(in_ptr) {} + //Ptr(TYPE * in_ptr, bool=false) : BasePtr(in_ptr) {} + + /// Construct from array + template + Ptr(std::enable_if_t,T2*> _ptr, size_t, bool) + : BasePtr(_ptr) {} + + /// From compatible Ptr + template + Ptr(Ptr _in, std::enable_if_t,bool> = true) + : BasePtr(_in.Raw()) {} + + /// From nullptr + Ptr(std::nullptr_t) : Ptr() {} + + /// Destructor + ~Ptr() { ; } + + [[nodiscard]] bool IsNull() const { return ptr == nullptr; } + [[nodiscard]] TYPE * Raw() const { return ptr; } + [[nodiscard]] TYPE * Raw(size_t pos) const { return &(ptr[pos]); } + template Ptr Cast() const { return (T2*) ptr; } + template Ptr ConstCast() const { return const_cast(ptr); } + template Ptr DynamicCast() const { return dynamic_cast(ptr); } + template Ptr ReinterpretCast() const { return reinterpret_cast(ptr); } + + template + void New(T &&... args) { ptr = new TYPE(std::forward(args)...); } // New raw pointer. + void NewArray(size_t array_size) { ptr = new TYPE[array_size]; } + void Delete() { delete ptr; } + void DeleteArray() { delete [] ptr; } + + size_t Hash() const noexcept { + static constexpr size_t shift = internal::Log2(1 + sizeof(TYPE)); // Chop off useless bits... + return (size_t)(ptr) >> shift; + } + struct hash_t { size_t operator()(const Ptr & t) const noexcept { return t.Hash(); } }; + + // Copy assignments + Ptr & operator=(const Ptr & _in) & { ptr = _in.ptr; return *this; } + + // Assign to compatible Ptr or raw (non-managed) pointer. + template Ptr & operator=(std::enable_if_t,T2*> _in) & { ptr = _in; return *this; } + template Ptr & operator=(std::enable_if_t, Ptr > _in) & { ptr = _in.Raw(); return *this; } + + // Auto-cast to raw pointer type. + operator TYPE *() { return ptr; } + + operator bool() { return ptr != nullptr; } + operator bool() const { return ptr != nullptr; } + + template bool operator==(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr == in_ptr.ptr; } + else { return ptr == in_ptr; } + } + template bool operator!=(const T & in_ptr) const { return !operator==(in_ptr); } + + template bool operator<(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr < in_ptr.ptr; } + else { return ptr < in_ptr; } + } + template bool operator>(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr > in_ptr.ptr; } + else { return ptr > in_ptr; } + } + template bool operator<=(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr <= in_ptr.ptr; } + else { return ptr <= in_ptr; } + } + template bool operator>=(const T & in_ptr) const { + if constexpr (std::is_same>()) { return ptr >= in_ptr.ptr; } + else { return ptr >= in_ptr; } + } + + [[nodiscard]] Ptr operator+(int value) const { return ptr + value; } + [[nodiscard]] Ptr operator-(int value) const { return ptr - value; } + [[nodiscard]] Ptr operator+(size_t value) const { return ptr + value; } + [[nodiscard]] Ptr operator-(size_t value) const { return ptr - value; } + + // Extra functionality (not in raw pointers) + + /// Fill an array with the provided fill_value. + /// If fill_value is a function, repeatedly call function. + template + void FillMemoryFunction(const size_t num_bytes, T fill_fun) { + emp::FillMemoryFunction(*this, num_bytes, fill_fun); + } + + /// Fill an array with the provided fill_value. + /// If fill_value is a function, repeatedly call function. + template + void FillMemory(const size_t num_bytes, T fill_value) { + emp::FillMemory(*this, num_bytes, fill_value); + } + + // Stubs for debug-related functions when outside debug mode. + int DebugGetCount() const { return -1; } + bool DebugIsArray() const { emp_assert(false); return false; } + size_t DebugGetArrayBytes() const { return 0; } + bool DebugIsActive() const { return true; } + bool OK() const { return true; } + }; + +#endif // #ifdef EMP_TRACK_MEM + + // IO + template + std::ostream & operator<<(std::ostream & out, const emp::Ptr & ptr) { + out << ptr.Raw(); + return out; + } + + // @CAO: Reading a pointer from a stream seems like a terrible idea in most situations, but I + // can imagine limited circumstances where it would be needed. + template + std::istream & operator>>(std::istream & is, emp::Ptr & ptr) { + T * val; + is >> val; + ptr = val; + return is; + } + + /// Convert a T* to a Ptr. By default, don't track. + template + [[nodiscard]] Ptr ToPtr(T * _in, bool own=false) { return Ptr(_in, own); } + + /// Convert a T* to a Ptr that we DO track. + template + [[nodiscard]] Ptr TrackPtr(T * _in, bool own=true) { return Ptr(_in, own); } + + /// Create a new Ptr of the target type; use the args in the constructor. + template + [[nodiscard]] Ptr NewPtr(ARGS &&... args) { + auto ptr = new T(std::forward(args)...); + // auto ptr = (T*) malloc (sizeof(T)); // Build a new raw pointer. + // emp_assert(ptr); // No exceptions in emscripten; assert alloc! + // new (ptr) T(std::forward(args)...); // Special new that uses allocated space. + return Ptr(ptr, true); + } + + /// Copy an object pointed to and return a Ptr to the copy. + template + [[nodiscard]] Ptr CopyPtr(Ptr in) { return NewPtr(*in); } + + /// Copy a vector of objects pointed to; return a vector of Ptrs to the new copies. + template + [[nodiscard]] emp::vector> CopyPtrs(const emp::vector> & in) { + emp::vector> out_ptrs(in.size()); + for (size_t i = 0; i < in.size(); i++) out_ptrs[i] = CopyPtr(in[i]); + return out_ptrs; + } + + /// Copy a vector of objects pointed to by using their Clone() member function; return vector. + template + [[nodiscard]] emp::vector> ClonePtrs(const emp::vector> & in) { + emp::vector> out_ptrs(in.size()); + for (size_t i = 0; i < in.size(); i++) out_ptrs[i] = in[i]->Clone(); + return out_ptrs; + } + + /// Create a pointer to an array of objects. + template + [[nodiscard]] Ptr NewArrayPtr(size_t array_size) { + auto ptr = new T[array_size]; // Build a new raw pointer. + // const size_t alloc_size = array_size * sizeof(T); + // auto ptr = (T*) malloc (alloc_size); + emp_assert(ptr, array_size); // No exceptions in emscripten; assert alloc! + // for (size_t i = 0; i < array_size; i++) { // Loop through all array elements. + // new (ptr + i*sizeof(T)) T(args...); // ...and initialize them. + // } + return Ptr(ptr, array_size, true); + } + + /// Fill an array with the provided fill_value. + /// If fill_value is a function, repeatedly call function. + template + void FillMemory(emp::Ptr mem_ptr, const size_t num_bytes, T fill_value) { + // If the fill value is a function, call that function for each memory position. + if constexpr (std::is_invocable_v) { + FillMemoryFunction(mem_ptr, num_bytes, std::forward(fill_value)); + } + + constexpr size_t FILL_SIZE = sizeof(T); + + const size_t leftover = num_bytes % FILL_SIZE; + const size_t limit = num_bytes - leftover; + unsigned char * dest = mem_ptr.Raw(); + + // Fill out random bytes in groups of FILL_SIZE. + for (size_t byte = 0; byte < limit; byte += FILL_SIZE) { + std::memcpy(dest+byte, &fill_value, FILL_SIZE); + } + + // If we don't have a multiple of FILL_SIZE, fill in part of the remaining. + if (leftover) std::memcpy(dest+limit, &fill_value, leftover); + } + + /// Fill an array by repeatedly calling the provided fill functions. + template + void FillMemoryFunction(emp::Ptr mem_ptr, const size_t num_bytes, T fill_fun) { + static_assert(std::is_invocable_v, "FillMemoryFunction requires an invocable fill_fun."); + using return_t = decltype(fill_fun()); + constexpr size_t FILL_SIZE = sizeof(return_t); + + const size_t leftover = num_bytes % FILL_SIZE; + const size_t limit = num_bytes - leftover; + unsigned char * dest = mem_ptr.Raw(); + + // Fill out random bytes in groups of FILL_SIZE. + return_t fill_value; + for (size_t byte = 0; byte < limit; byte += FILL_SIZE) { + fill_value = fill_fun(); + std::memcpy(dest+byte, &fill_value, FILL_SIZE); + } + + // If we don't have a multiple of FILL_SIZE, fill in part of the remaining. + if (leftover) { + fill_value = fill_fun(); + std::memcpy(dest+limit, &fill_value, leftover); + } + } + +} // namespace emp + +#endif // #ifndef EMP_IN_PROGRESS_PTR_OVERLOAD_FIX_HPP_INCLUDE diff --git a/include/emp/in_progress/SimpleLexer.hpp b/include/emp/in_progress/SimpleLexer.hpp new file mode 100644 index 0000000000..875602484b --- /dev/null +++ b/include/emp/in_progress/SimpleLexer.hpp @@ -0,0 +1,88 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ +/** + * @file + * @brief Lexer with common functionality already filled in. + * @note Status: ALPHA + */ + +#ifndef EMP_IN_PROGRESS_SIMPLELEXER_HPP_INCLUDE +#define EMP_IN_PROGRESS_SIMPLELEXER_HPP_INCLUDE + +#include +#include + +#include "../base/error.hpp" +#include "../compiler/Lexer.hpp" + +namespace emp { + + using namespace std::string_literals; + + class SimpleLexer : public emp::Lexer { + private: + int token_identifier; ///< Token id for identifiers + int token_number; ///< Token id for literal numbers + int token_string; ///< Token id for literal strings + int token_char; ///< Token id for literal characters + int token_external; ///< Token id for strings to be evaluated externally. + int token_symbol; ///< Token id for other symbols + + public: + SimpleLexer() { + // Whitespace and comments should always be dismissed (top priority) + IgnoreToken("Whitespace", "[ \t\n\r]+"); + IgnoreToken("//-Comments", "//.*"); + IgnoreToken("/*...*/-Comments", "/[*]([^*]|([*]+[^*/]))*[*]+/"); + + // Meaningful tokens have next priority. + + // An indentifier must begin with a letter, underscore, or dot, and followed by + // more of the same OR numbers or brackets. + token_identifier = AddToken("Identifier", "[a-zA-Z_.][a-zA-Z0-9_.[\\]]*"); + + // A literal number must begin with a digit; it can have any number of digits in it and + // optionally a decimal point. + token_number = AddToken("Literal Number", "[0-9]+(\\.[0-9]+)?"); + + // A string must begin and end with a quote and can have an escaped quote in the middle. + token_string = AddToken("Literal String", "\\\"([^\"\\\\]|\\\\.)*\\\""); + + // A literal char must begin and end with a single quote. It will always be treated as + // its ascii value. + token_char = AddToken("Literal Character", "'([^'\n\\\\]|\\\\.)+'"); + + // Setup a RegEx that can detect up to 4-deep nested parentheses. + const std::string no_parens = "[^()\n\r]*"; + const std::string open = "\"(\""; + const std::string close = "\")\""; + const std::string matched_parens = open + no_parens + close; + const std::string multi_parens = no_parens + "("s + matched_parens + no_parens + ")*"s; + const std::string nested_parens2 = open + multi_parens + close; + const std::string multi_nested2 = no_parens + "("s + nested_parens2 + no_parens + ")*"s; + const std::string nested_parens3 = open + multi_nested2 + close; + const std::string multi_nested3 = no_parens + "("s + nested_parens3 + no_parens + ")*"s; + const std::string nested_parens4 = open + multi_nested3 + close; + const std::string multi_nested4 = no_parens + "("s + nested_parens4 + no_parens + ")*"s; + + // An external value should be evaluated in a provided function. If no such function + // exists, using it will be an error. + token_external = AddToken("External Evaluation", "\"$(\""s + multi_nested4 + "\")\""s); + + // Symbols should have least priority. They include any solitary character not listed + // above, or pre-specified multi-character groups. + token_symbol = AddToken("Symbol", ".|\"==\"|\"!=\"|\"<=\"|\">=\"|\"&&\"|\"||\"|\"**\"|\"%%\""); + } + + bool IsID(const emp::Token token) const noexcept { return token.token_id == token_identifier; } + bool IsNumber(const emp::Token token) const noexcept { return token.token_id == token_number; } + bool IsString(const emp::Token token) const noexcept { return token.token_id == token_string; } + bool IsChar(const emp::Token token) const noexcept { return token.token_id == token_char; } + bool IsSymbol(const emp::Token token) const noexcept { return token.token_id == token_symbol; } + }; +} + +#endif // #ifndef EMP_IN_PROGRESS_SIMPLELEXER_HPP_INCLUDE diff --git a/include/emp/in_progress/SimpleParser.hpp b/include/emp/in_progress/SimpleParser.hpp new file mode 100644 index 0000000000..bb344d573e --- /dev/null +++ b/include/emp/in_progress/SimpleParser.hpp @@ -0,0 +1,353 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ +/** + * @file + * @brief Common praser functionality with custom plugins for variables and functions. + * @note Status: ALPHA + * + * Developer TODO: + * - Make ${ ... } actually work + * - Setup operator RegEx to be built dynamically + * - Allow new operators to be added externally + * - Setup LVALUES as a type, and allow assignment + * - Add in a type system (String, double, vectors, etc.) + */ + +#ifndef EMP_IN_PROGRESS_SIMPLEPARSER_HPP_INCLUDE +#define EMP_IN_PROGRESS_SIMPLEPARSER_HPP_INCLUDE + +#include +#include + +#include "../base/error.hpp" +#include "../compiler/Lexer.hpp" + +#include "SimpleLexer.hpp" + +namespace emp { + class SimpleParser { + + using pos_t = emp::TokenStream::Iterator; + + bool verbose = false; + + using value_fun_t = std::function; + struct ValueType { + enum type_t { ERROR=0, VALUE, FUNCTION }; + + type_t type; + double value; + value_fun_t fun; + + ValueType() : type(ERROR) {} + ValueType(const ValueType &) = default; + ValueType(double in_val) : type(VALUE), value(in_val) { } + ValueType(value_fun_t in_fun) : type(FUNCTION), fun(in_fun) { } + + ValueType & operator=(const ValueType &) = default; + ValueType & operator=(double in_val) { type = VALUE; value = in_val; return *this; } + ValueType & operator=(value_fun_t in_fun) { type = FUNCTION; fun = in_fun; return *this; } + + value_fun_t AsFun() { + if (type==FUNCTION) return fun; else return [v=value](emp::DataMap &){ return v; }; + } + }; + + struct BinaryOperator { + using fun_t = std::function; + size_t prec; + fun_t fun; + void Set(size_t in_prec, fun_t in_fun) { prec = in_prec; fun = in_fun; } + }; + + struct Function { + using fun0_t = std::function; + using fun1_t = std::function; + using fun2_t = std::function; + using fun3_t = std::function; + + size_t num_args = 0; + fun0_t fun0; fun1_t fun1; fun2_t fun2; fun3_t fun3; + + void Set0(fun0_t in_fun) { num_args = 0; fun0 = in_fun; } + void Set1(fun1_t in_fun) { num_args = 1; fun1 = in_fun; } + void Set2(fun2_t in_fun) { num_args = 2; fun2 = in_fun; } + void Set3(fun3_t in_fun) { num_args = 3; fun3 = in_fun; } + }; + + // --------- MEMBER VARIABLES ----------- + SimpleLexer lexer; + std::unordered_map> unary_ops; + std::unordered_map binary_ops; + std::unordered_map functions; + size_t error_count = 0; + + using error_fun_t = std::function; + error_fun_t error_fun = + [](const std::string & msg){ std::cerr << "ERROR: " << msg << std::endl; }; + + template + ValueType AddError(Ts &&... args) { + error_fun( emp::to_string(args...); ); + ++error_count; + return ValueType(); + } + + public: + SimpleParser(bool use_defaults=true) { + if (use_defaults) { + AddDefaultOperators(); + AddDefaultFunctions(); + } + } + + bool HasErrors() const { return error_count; } + size_t NumErrors() const { return error_count; } + + error_fun_t GetErrorFun() const { return error_fun; } + void SetErrorFun(error_fun_t in_fun) { error_fun = in_fun; } + + // Add a unary operator + void AddOp(const std::string & op, std::function fun) { + unary_ops[op] = fun; + } + + void AddDefaultOperators() { + // Setup the unary operators for the parser. + AddOp("+", [](double x) { return x; }); + AddOp("-", [](double x) { return -x; }; + AddOp("!", [](double x) { return (double) (x==0.0); }; + + // Setup the default binary operators for the parser. + size_t prec = 0; // Precedence level of each operator... + binary_ops["||"].Set( ++prec, [](double x, double y){ return (x!=0.0)||(y!=0.0); } ); + binary_ops["&&"].Set( ++prec, [](double x, double y){ return (x!=0.0)&&(y!=0.0); } ); + binary_ops["=="].Set( ++prec, [](double x, double y){ return x == y; } ); + binary_ops["!="].Set( prec, [](double x, double y){ return x != y; } ); + binary_ops["<"] .Set( ++prec, [](double x, double y){ return x < y; } ); + binary_ops["<="].Set( prec, [](double x, double y){ return x <= y; } ); + binary_ops[">"] .Set( prec, [](double x, double y){ return x > y; } ); + binary_ops[">="].Set( prec, [](double x, double y){ return x >= y; } ); + binary_ops["+"] .Set( ++prec, [](double x, double y){ return x + y; } ); + binary_ops["-"] .Set( prec, [](double x, double y){ return x - y; } ); + binary_ops["*"] .Set( ++prec, [](double x, double y){ return x * y; } ); + binary_ops["/"] .Set( prec, [](double x, double y){ return x / y; } ); + binary_ops["%"] .Set( prec, [](double x, double y){ return emp::Mod(x, y); } ); + binary_ops["**"].Set( ++prec, [](double x, double y){ return emp::Pow(x, y); } ); + binary_ops["%%"].Set( prec, [](double x, double y){ return emp::Log(x, y); } ); + } + + void AddDefaultFunctions() { + // Setup the default functions. + functions["ABS"].Set1( [](double x){ return std::abs(x); } ); + functions["EXP"].Set1( [](double x){ return emp::Pow(emp::E, x); } ); + functions["LOG"].Set1( [](double x){ return std::log(x); } ); + functions["LOG2"].Set1( [](double x){ return std::log2(x); } ); + functions["LOG10"].Set1( [](double x){ return std::log10(x); } ); + + functions["SQRT"].Set1( [](double x){ return std::sqrt(x); } ); + functions["CBRT"].Set1( [](double x){ return std::cbrt(x); } ); + + functions["SIN"].Set1( [](double x){ return std::sin(x); } ); + functions["COS"].Set1( [](double x){ return std::cos(x); } ); + functions["TAN"].Set1( [](double x){ return std::tan(x); } ); + functions["ASIN"].Set1( [](double x){ return std::asin(x); } ); + functions["ACOS"].Set1( [](double x){ return std::acos(x); } ); + functions["ATAN"].Set1( [](double x){ return std::atan(x); } ); + functions["SINH"].Set1( [](double x){ return std::sinh(x); } ); + functions["COSH"].Set1( [](double x){ return std::cosh(x); } ); + functions["TANH"].Set1( [](double x){ return std::tanh(x); } ); + functions["ASINH"].Set1( [](double x){ return std::asinh(x); } ); + functions["ACOSH"].Set1( [](double x){ return std::acosh(x); } ); + functions["ATANH"].Set1( [](double x){ return std::atanh(x); } ); + + functions["CEIL"].Set1( [](double x){ return std::ceil(x); } ); + functions["FLOOR"].Set1( [](double x){ return std::floor(x); } ); + functions["ROUND"].Set1( [](double x){ return std::round(x); } ); + + functions["ISINF"].Set1( [](double x){ return std::isinf(x); } ); + functions["ISNAN"].Set1( [](double x){ return std::isnan(x); } ); + + // Default 2-input functions + functions["HYPOT"].Set2( [](double x, double y){ return std::hypot(x,y); } ); + functions["EXP"].Set2( [](double x, double y){ return emp::Pow(x,y); } ); + functions["LOG"].Set2( [](double x, double y){ return emp::Log(x,y); } ); + functions["MIN"].Set2( [](double x, double y){ return (xy) ? x : y; } ); + functions["POW"].Set2( [](double x, double y){ return emp::Pow(x,y); } ); + + // Default 3-input functions. + functions["IF"].Set3( [](double x, double y, double z){ return (x!=0.0) ? y : z; } ); + functions["CLAMP"].Set3( [](double x, double y, double z){ return (xz) ? z : x; } ); + functions["TO_SCALE"].Set3( [](double x, double y, double z){ return (z-y)*x+y; } ); + functions["FROM_SCALE"].Set3( [](double x, double y, double z){ return (x-y) / (z-y); } ); + } + + /// Helpers for parsing. + ValueType ParseValue(const DataMap & dm, pos_t & pos) { + if constexpr (verbose) { + std::cout << "ParseValue at position " << pos.GetIndex() << " : " << pos->lexeme << std::endl; + } + + // Deal with any unary operators... + if (emp::Has(unary_ops, pos->lexeme)) { + if constexpr (verbose) std::cout << "Found UNARY OP: " << pos->lexeme << std::endl; + auto op = unary_ops[pos->lexeme]; + ++pos; + ValueType val = ParseValue(dm, pos); + if (val.type == ValueType::VALUE) { return op(val.value); } + else { return (value_fun_t) [fun=val.fun,op](emp::DataMap & dm){ return op(fun(dm)); }; } + } + + // If we have parentheses, process the contents + if (pos->lexeme == "(") { + if constexpr (verbose) std::cout << "Found: OPEN PAREN" << std::endl; + ++pos; + ValueType val = ParseMath(dm, pos); + if (pos->lexeme != ")") return AddError("Expected ')', but found '", pos->lexeme, "'."); + ++pos; + return val; + } + + // If this is a value, set it and return. + if (lexer.IsNumber(*pos)) { + double result = emp::from_string(pos->lexeme); + ++pos; + return result; + } + + // Otherwise it should be and identifier! + const std::string & name = pos->lexeme; + ++pos; + + // If it is followed by a parenthesis, it should be a function. + const bool is_fun = (pos.IsValid() && pos->lexeme == "("); + + if (is_fun) { + if (!emp::Has(functions, name)) return AddError("Call to unknown function '", name,"'."); + ++pos; + emp::vector args; + while(pos->lexeme != ")") { + args.push_back(ParseMath(dm, pos)); + if (pos->lexeme == ",") ++pos; + } + ++pos; + + // Now build the function based on its argument count. + value_fun_t out_fun; + switch (args.size()) { + case 0: + if (!functions[name].fun0) AddError("Function '", name, "' requires arguments."); + out_fun = [fun=functions[name].fun0](emp::DataMap & dm) { return fun(); }; + break; + case 1: + if (!functions[name].fun1) AddError("Function '", name, "' cannot have 1 arguments."); + out_fun = [fun=functions[name].fun1,arg0=args[0].AsFun()](emp::DataMap & dm) { + return fun(arg0(dm)); + }; + break; + case 2: + if (!functions[name].fun2) AddError("Function '", name, "' cannot have 2 arguments."); + out_fun = [fun=functions[name].fun2,arg0=args[0].AsFun(),arg1=args[1].AsFun()](emp::DataMap & dm) { + return fun(arg0(dm), arg1(dm)); + }; + break; + case 3: + if (!functions[name].fun3) AddError("Function '", name, "' cannot have 3 arguments."); + out_fun = [fun=functions[name].fun3,arg0=args[0].AsFun(),arg1=args[1].AsFun(),arg2=args[2].AsFun()](emp::DataMap & dm) { + return fun(arg0(dm), arg1(dm), arg2(dm)); + }; + break; + default: + AddError("Too many arguments for function '", name, "'."); + } + return out_fun; + } + + // This must be a DataMap entry name. + if (!dm.HasName(name)) AddError("Unknown data map entry '", name, "'."); + size_t id = dm.GetID(name); + return (value_fun_t) [id](emp::DataMap & dm){ return dm.GetAsDouble(id); }; + } + + ValueType ParseMath(const DataMap & dm, pos_t & pos, size_t prec_limit=0) { + ValueType val1 = ParseValue(dm, pos); + + if constexpr (verbose) { + if (pos.IsValid()) { + std::cout << "ParseMath at " << pos.GetIndex() << " : " << pos->lexeme << std::endl; + } else std::cout << "PROCESSED!" << std::endl; + } + + while (pos.IsValid() && pos->lexeme != ")" && pos->lexeme != ",") { + if constexpr (verbose) { std::cout << "...Scanning for op... [" << pos->lexeme << "]" << std::endl; } + + // If we have an operator, act on it! + if (Has(binary_ops, pos->lexeme)) { + const BinaryOperator & op = binary_ops[pos->lexeme]; + if (prec_limit >= op.prec) return val1; // Precedence not allowed; return currnet value. + ++pos; + ValueType val2 = ParseMath(dm, pos, op.prec); + if (val1.type == ValueType::VALUE) { + if (val2.type == ValueType::VALUE) { val1 = op.fun(val1.value, val2.value); } + else { + val1 = (value_fun_t) [val1_num=val1.value,val2_fun=val2.fun,op_fun=op.fun](emp::DataMap & dm){ + return op_fun(val1_num, val2_fun(dm)); + }; + } + } else { + if (val2.type == ValueType::VALUE) { + val1 = (value_fun_t) [val1_fun=val1.fun,val2_num=val2.value,op_fun=op.fun](emp::DataMap & dm){ + return op_fun(val1_fun(dm), val2_num); + }; + } else { + val1 = (value_fun_t) [val1_fun=val1.fun,val2_fun=val2.fun,op_fun=op.fun](emp::DataMap & dm){ + return op_fun(val1_fun(dm), val2_fun(dm)); + }; + } + } + } + + else AddError("Operator '", pos->lexeme, "' NOT found!"); + } + + // @CAO Make sure there's not a illegal lexeme here. + + return val1; + } + + /// Parse a function description that will take a DataMap and return the results. + /// For example, if the string "foo * 2 + bar" is passed in, a function will be returned + /// that takes a datamap (of the example type) loads in the values of "foo" and "bar", and + /// returns the result of the above equation. + + value_fun_t BuildMathFunction(const DataMap & dm, const std::string & expression) { + emp::TokenStream tokens = lexer.Tokenize(expression); + if constexpr (verbose) tokens.Print(); + pos_t pos = tokens.begin(); + ValueType val = ParseMath(dm, pos); + + // If this value is fixed, turn it into a function. + if (val.type == ValueType::VALUE) { + return [out=val.value](emp::DataMap &){ return out; }; + } + + // Otherwise return the function produced. + #ifdef NDEBUG + return val.fun; + #else + // If we are in debug mode, save the original datamap and double-check compatability. + return [fun=val.fun,&orig_layout=dm.GetLayout()](emp::DataMap & dm){ + emp_assert(dm.HasLayout(orig_layout)); + return fun(dm); + }; + #endif + } + + }; +} + +#endif // #ifndef EMP_IN_PROGRESS_SIMPLEPARSER_HPP_INCLUDE diff --git a/include/emp/in_progress/TrackedPtr.hpp b/include/emp/in_progress/TrackedPtr.hpp new file mode 100644 index 0000000000..bd167e2a6e --- /dev/null +++ b/include/emp/in_progress/TrackedPtr.hpp @@ -0,0 +1,57 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2022 +*/ +/** + * @file + * @brief Similar to Ptr, but memory is tracked and managed elsewhere, such as smart pointers. + * @note Status: ALPHA + * + */ + +#ifndef EMP_IN_PROGRESS_TRACKEDPTR_HPP_INCLUDE +#define EMP_IN_PROGRESS_TRACKEDPTR_HPP_INCLUDE + +#include "Ptr.hpp" + +namespace emp { + + class PtrManager { + public: + virtual void IncCount() = 0; + }; + + template + class TrackedPtr : public Ptr { + private: + using BasePtr::ptr; + + public: + using element_type = TYPE; + + /// Default constructor + TrackedPtr() : Ptr(nullptr) {} + + /// Copy constructor + TrackedPtr(const TrackedPtr & _in) : Ptr(_in) {} + + /// Construct from raw ptr + template Ptr(T2 * in_ptr, bool=false) : BasePtr(in_ptr) {} + + /// Construct from array + template Ptr(T2 * _ptr, size_t, bool) : BasePtr(_ptr) {} + + /// From compatible Ptr + template Ptr(Ptr _in) : BasePtr(_in.Raw()) {} + + /// From nullptr + Ptr(std::nullptr_t) : Ptr() {} + + /// Destructor + ~Ptr() { ; } + + +} + +#endif // #ifndef EMP_IN_PROGRESS_TRACKEDPTR_HPP_INCLUDE diff --git a/include/emp/in_progress/Trait.hpp b/include/emp/in_progress/Trait.hpp index 67ae5a61e9..c7b15c2f97 100644 --- a/include/emp/in_progress/Trait.hpp +++ b/include/emp/in_progress/Trait.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file Trait.hpp + * @file * @brief The TraitDef class maintains a category of measuments about another class. * * Each trait is associated with a name, a description, and a type. Instance of that diff --git a/include/emp/in_progress/class.hpp b/include/emp/in_progress/class.hpp index 4bf693c54b..8330d90d8a 100644 --- a/include/emp/in_progress/class.hpp +++ b/include/emp/in_progress/class.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file class.hpp + * @file * @brief A re-engineering of tuple_struct.h, intended to be usable throughout Empirical. * * The EMP_CLASS macro builds an inexpensive class that diff --git a/include/emp/in_progress/constexpr/ce_array.hpp b/include/emp/in_progress/constexpr/ce_array.hpp index 74d50281dd..bf7e88003e 100644 --- a/include/emp/in_progress/constexpr/ce_array.hpp +++ b/include/emp/in_progress/constexpr/ce_array.hpp @@ -1,18 +1,20 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file ce_array.hpp + * @file * @brief ce_array defines a limited array object for use within a constexpr class or function. * - * STATUS: ALPHA + * @note STATUS: ALPHA * */ #ifndef EMP_IN_PROGRESS_CONSTEXPR_CE_ARRAY_HPP_INCLUDE #define EMP_IN_PROGRESS_CONSTEXPR_CE_ARRAY_HPP_INCLUDE +#include #include #include "../base/assert.hpp" diff --git a/include/emp/in_progress/constexpr/ce_random.hpp b/include/emp/in_progress/constexpr/ce_random.hpp index 8cb0e9a673..ae5b95c93e 100644 --- a/include/emp/in_progress/constexpr/ce_random.hpp +++ b/include/emp/in_progress/constexpr/ce_random.hpp @@ -1,12 +1,13 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file ce_random.hpp + * @file * @brief A versatile and non-patterned pseudo-random-number generator. * - * Status: DESIGN + * @note Status: DESIGN * * Constructor: * Random(int _seed=-1) @@ -43,8 +44,10 @@ // #include #include #include +#include #include #include +#include #include #include "../math/math.hpp" diff --git a/include/emp/in_progress/constexpr/ce_string.hpp b/include/emp/in_progress/constexpr/ce_string.hpp index 87ff2d7e5d..3583f98d28 100644 --- a/include/emp/in_progress/constexpr/ce_string.hpp +++ b/include/emp/in_progress/constexpr/ce_string.hpp @@ -1,17 +1,19 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016 - * - * @file ce_string.hpp + * @file * @brief ce_string defines a limited string object for use within a constexpr class or function. * - * Status: DESIGN. + * @note Status: DESIGN. */ #ifndef EMP_IN_PROGRESS_CONSTEXPR_CE_STRING_HPP_INCLUDE #define EMP_IN_PROGRESS_CONSTEXPR_CE_STRING_HPP_INCLUDE +#include #include #include diff --git a/include/emp/in_progress/fixed.hpp b/include/emp/in_progress/fixed.hpp index 797c5b1108..0669123d12 100644 --- a/include/emp/in_progress/fixed.hpp +++ b/include/emp/in_progress/fixed.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015 - * - * @file fixed.hpp + * @file * @brief A comprehensive (ideally) fixed-point number representation. * * Type-name: emp::fixed diff --git a/include/emp/in_progress/struct.hpp b/include/emp/in_progress/struct.hpp index 89d6b7a2be..d9281cb409 100644 --- a/include/emp/in_progress/struct.hpp +++ b/include/emp/in_progress/struct.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021 - * - * @file struct.hpp + * @file * @brief A re-engineering of tuple_struct.h, intended to be usable throughout Empirical. * * The EMP_STRUCT macro builds an inexpensive struct that diff --git a/include/emp/io/ContiguousStream.hpp b/include/emp/io/ContiguousStream.hpp index 224bd45758..ab14fd42b0 100644 --- a/include/emp/io/ContiguousStream.hpp +++ b/include/emp/io/ContiguousStream.hpp @@ -1,17 +1,19 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file ContiguousStream.hpp + * @file * @brief Useful for streaming data to contiguous memory. - * @note Status: RELEASE + * Status: RELEASE */ #ifndef EMP_IO_CONTIGUOUSSTREAM_HPP_INCLUDE #define EMP_IO_CONTIGUOUSSTREAM_HPP_INCLUDE #include +#include #include "../base/vector.hpp" diff --git a/include/emp/io/File.hpp b/include/emp/io/File.hpp index fce046515b..70345f4106 100644 --- a/include/emp/io/File.hpp +++ b/include/emp/io/File.hpp @@ -1,14 +1,14 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018-2022. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018-2020. - * - * @file File.hpp + * @file * @brief The File object maintains a simple, in-memory file. * @note Status: BETA * * @todo We need to modify this code so that File can work with Emscripten. - * Alternatively, we might want to have a more flexible file class that wraps this one. * */ @@ -20,22 +20,26 @@ #include #include #include +#include #include #include "../base/vector.hpp" +#include "../meta/FunInfo.hpp" +#include "../tools/String.hpp" #include "../tools/string_utils.hpp" namespace emp { - /// A class to maintin files for loading, writing, storing, and easy access to components. + /// A class to maintain files for loading, writing, storing, and easy access to components. class File { protected: - emp::vector lines; + emp::vector lines; + String file_error = ""; public: File() : lines() { ; } File(std::istream & input) : lines() { Load(input); } - File(const std::string & filename) : lines() { Load(filename); } + File(const String & filename) : lines() { Load(filename); } File(const File &) = default; File(File &&) = default; ~File() { ; } @@ -62,31 +66,41 @@ namespace emp { size_t size() const { return lines.size(); } /// Return entire text of the file - emp::vector GetAllLines() {return lines;} + emp::vector GetAllLines() {return lines;} /// Index into a specific line in this file. - std::string & operator[](size_t pos) { return lines[pos]; } + String & operator[](size_t pos) { return lines[pos]; } /// Const index into a specific line in this file. - const std::string & operator[](size_t pos) const { return lines[pos]; } + const String & operator[](size_t pos) const { return lines[pos]; } /// Return the first line in the file. - std::string & front() { return lines.front(); } + String & front() { return lines.front(); } /// Return a const reference to to the first line in the file. - const std::string & front() const { return lines.front(); } + const String & front() const { return lines.front(); } /// Return the last line in the file. - std::string & back() { return lines.back(); } + String & back() { return lines.back(); } /// Return a const reference to the last line in the file. - const std::string & back() const { return lines.back(); } + const String & back() const { return lines.back(); } + + // Was there an error working with this file? + bool HasError() const { return file_error.size(); } + + // Text of error. + const String & GetError() const { return file_error; } + + // Remove any errors. + void ClearError() { file_error.resize(0); } /// Append a new line to the end of the file. - File & Append(const std::string & line) { lines.emplace_back(line); return *this; } + File & Append(const String & line) { lines.emplace_back(line); return *this; } /// Append a vector of lines to the end of the file. - File & Append(const emp::vector & in_lines) { + template + File & Append(const emp::vector & in_lines) { size_t start_size = lines.size(); lines.resize(start_size + in_lines.size()); for (size_t pos = 0; pos < in_lines.size(); pos++) { @@ -110,57 +124,62 @@ namespace emp { } /// Extract first line from file - auto operator>>(std::string &out) { - out = size() ? front() : out; - lines.erase(begin()); + auto operator>>(std::string & out) { + if (size()) { + out = front(); + lines.erase(begin()); + } } /// Test if two files are identical. - bool operator==(const File in) { return lines == in.lines; } + bool operator==(const File & in) const { return lines == in.lines; } /// Test if two files are different. - bool operator!=(const File in) { return lines != in.lines; } + bool operator!=(const File & in) const { return lines != in.lines; } - /// Load a line from an input stream into a file. - File & LoadLine(std::istream & input) { + /// Load a line from an input stream into a file; return whether load was successful. + bool LoadLine(std::istream & input) { lines.emplace_back(""); - std::getline(input, lines.back()); + if (!std::getline(input, lines.back())) { + lines.pop_back(); + return false; + } // If the input file is DOS formatted, make sure to remove the \r at the end of each line. if (lines.back().size() && lines.back().back() == '\r') lines.back().pop_back(); - return *this; + return true; } /// Load an entire input stream into a file. File & Load(std::istream & input) { - while (!input.eof()) { - LoadLine(input); - } + while (LoadLine(input)); return *this; } /// Load a file from disk using the provided name. /// If file does not exist, this is a nop - File & Load(const std::string & filename) { + File & Load(const String & filename) { std::ifstream file(filename); if (file.is_open()) { Load(file); file.close(); + } else { + file_error.Set("File '", filename, "' failed to open."); } return *this; } /// Write this file to a provided output stream. File & Write(std::ostream & output) { - for (std::string & cur_line : lines) { + for (String & cur_line : lines) { output << cur_line << '\n'; } return *this; } /// Write this file to a file of the provided name. - File & Write(const std::string & filename) { + File & Write(const String & filename) { std::ofstream file(filename); Write(file); file.close(); @@ -168,16 +187,16 @@ namespace emp { } /// Test if a substring exists on ANY line of a file. - bool Contains(const std::string & pattern) const { - for (const std::string & line : lines) { - if (line.find(pattern) != std::string::npos) return true; + bool Contains(const String & pattern) const { + for (const String & line : lines) { + if (line.find(pattern) != String::npos) return true; } return false; } /// Convert this file into an std::set of lines (loses line ordering). - std::set AsSet() const { - std::set line_set; + std::set AsSet() const { + std::set line_set; for (size_t i = 0; i < lines.size(); i++) { line_set.insert(lines[i]); } @@ -185,17 +204,25 @@ namespace emp { } /// Apply a string manipulation function to all lines in the file. - File & Apply(const std::function & fun) { - for (std::string & cur_line : lines) { - fun(cur_line); + template + File & Apply(FUN_T fun) { + for (String & cur_line : lines) { + // If the function returns a string, assume that's what we're supposed to use. + // Otherwise assume that the string gets modified. + using return_t = typename FunInfo::return_t; + if constexpr ( std::is_same() ) { + cur_line = fun(cur_line); + } else { + fun(cur_line); + } } return *this; } /// Purge all lines that don't the criterion function. - File & KeepIf(const std::function & fun) { - emp::vector new_lines; - for (std::string & cur_line : lines) { + File & KeepIf(const std::function & fun) { + emp::vector new_lines; + for (String & cur_line : lines) { if (fun(cur_line)) new_lines.emplace_back(cur_line); } std::swap(lines, new_lines); @@ -203,39 +230,53 @@ namespace emp { } /// Keep only strings that contain a specific substring. - File & KeepIfContains(const std::string & pattern) { + File & KeepIfContains(const String & pattern) { return KeepIf( - [&pattern](const std::string & line){ return line.find(pattern) != std::string::npos; } + [&pattern](const String & line){ return line.find(pattern) != String::npos; } ); } /// Remove all strings that contain a specific substring. - File & RemoveIfContains(const std::string & pattern) { + File & RemoveIfContains(const String & pattern) { return KeepIf( - [&pattern](const std::string & line){ return line.find(pattern) == std::string::npos; } + [&pattern](const String & line){ return line.find(pattern) == String::npos; } + ); + } + + /// Keep only strings that contain a specific substring. + File & KeepIfBegins(const String & prefix) { + return KeepIf( + [&prefix](const String & line){ return line.find(prefix) == 0; } + ); + } + + /// Remove all strings that contain a specific substring. + File & RemoveIfBegins(const String & prefix) { + return KeepIf( + [&prefix](const String & line){ return line.Find(prefix) != 0; } ); } /// Remove all lines that are empty strings. File & RemoveEmpty() { - return KeepIf( [](const std::string & str){ return (bool) str.size(); } ); + return KeepIf( [](const String & str){ return (bool) str.size(); } ); } /// Any time multiple whitespaces are next to each other, collapse to a single WS char. /// Prefer '\n' if in whitespace collapsed, otherwise use ' '. File & CompressWhitespace() { - Apply(compress_whitespace); + Apply([](String & in){ in.Compress(); }); RemoveEmpty(); return *this; } /// Delete all whitespace; by default keep newlines. File & RemoveWhitespace(bool keep_newlines=true) { - Apply(remove_whitespace); + Apply([](String & in){ in.RemoveWhitespace(); }); RemoveEmpty(); if (!keep_newlines) { - std::string all_lines; - for (const std::string & cur_line : lines){ + String all_lines; + for (const String & cur_line : lines){ all_lines += cur_line; } lines.resize(1); @@ -245,21 +286,23 @@ namespace emp { } /// A technique to remove all comments in a file. - File & RemoveComments(const std::string & marker) { - Apply( [marker](std::string & str) { - size_t pos = str.find(marker); - if (pos !=std::string::npos) str.resize( pos ); + File & RemoveComments(const String & marker, bool skip_quotes=true) { + Apply( [marker,skip_quotes](String & str) { + size_t pos = str.Find(marker, 0, skip_quotes); + if (pos !=String::npos) str.resize( pos ); } ); return *this; } /// Allow remove comments to also be specified with a single character. - File & RemoveComments(char marker) { return RemoveComments(emp::to_string(marker)); } + File & RemoveComments(char marker, bool skip_quotes=true) { + return RemoveComments(emp::MakeString(marker), skip_quotes); + } - /// Run a function on each line of a file and return the restults as a vector. + /// Run a function on each line of a file and return the results as a vector. /// Note: Function is allowed to modify string. template - emp::vector Process(const std::function & fun) { + emp::vector Process(const std::function & fun) { emp::vector results(lines.size()); for (size_t i = 0; i < lines.size(); i++) { results[i] = fun(lines[i]); @@ -267,43 +310,65 @@ namespace emp { return results; } + /// Get a series of lines. + emp::vector Read(size_t start, size_t end) const { + if (end > lines.size()) end = lines.size(); + auto start_it = lines.begin()+static_cast(start); + auto end_it = lines.begin()+static_cast(end); + return emp::vector(start_it, end_it); + } + + /// Get a series of lines until a line meets a certain condition. + emp::vector ReadUntil(size_t start, auto test_fun) const { + size_t end = start; + while (end < lines.size() && !test_fun(lines[end])) ++end; + return Read(start, end); + } + + /// Get a series of lines while lines continue to meet a certain condition. + emp::vector ReadWhile(size_t start, auto test_fun) const { + size_t end = start; + while (end < lines.size() && test_fun(lines)) ++end; + return Read(start, end); + } + /// Remove the first column from the file, returning it as a vector of strings. - emp::vector ExtractCol(char delim=',') { - return Process( [delim](std::string & line){ - return string_pop(line, delim); + emp::vector ExtractCol(char delim=',') { + return Process( [delim](String & line){ + return line.Pop(delim); }); } /// Remove the first column from the file, returning it as a vector of a specified type. template emp::vector ExtractColAs(char delim=',') { - return Process( [delim](std::string & line){ - return emp::from_string(string_pop(line, delim)); + return Process( [delim](String & line){ + return line.Pop(delim).As(); }); } /// Convert a row of a file to a vector of string views. - emp::vector ViewRowSlices(size_t row_id, char delim=',') { - return view_slices(lines[row_id], delim); + emp::vector ViewRowSlices(size_t row_id, String delim=",") { + return lines[row_id].ViewSlices(delim); } /// Remove the first row from the file, returning it as a vector of strings. - emp::vector ExtractRow(char delim=',') { + emp::vector ExtractRow(String delim=",") { // Identify the data as string_views emp::vector sv_row = ViewRowSlices(0, delim); // Build the array to return and copy strings into it. - emp::vector out_row(sv_row.size()); + emp::vector out_row(sv_row.size()); for (size_t i=0; i < sv_row.size(); i++) out_row[i] = sv_row[i]; - // Remove the row to be extrated and return the result. + // Remove the row to be extracted and return the result. lines.erase(begin()); return out_row; } /// Remove the first row from the file, returning it as a vector of a specified type. template - emp::vector ExtractRowAs(char delim=',') { + emp::vector ExtractRowAs(String delim=",") { // Identify the data as string_views emp::vector sv_row = ViewRowSlices(0, delim); @@ -311,18 +376,25 @@ namespace emp { emp::vector out_row(sv_row.size()); for (size_t i=0; i < sv_row.size(); i++) out_row[i] = from_string(sv_row[i]); - // Remove the row to be extrated and return the result. + // Remove the row to be extracted and return the result. lines.erase(begin()); return out_row; } + emp::vector< emp::vector > ToCSV(String delim=",") const { + emp::vector< emp::vector > out_csv(lines.size()); + for (size_t row_id = 0; row_id < lines.size(); row_id++) { + out_csv[row_id] = lines[row_id].Slice(delim); + } + return out_csv; + } + template - emp::vector< emp::vector > ToData(char delim=',') { + emp::vector< emp::vector > ToData(String delim=",") const { emp::vector< emp::vector > out_data(lines.size()); - emp::vector sv_row; for (size_t row_id = 0; row_id < lines.size(); row_id++) { - view_slices(lines[row_id], sv_row, delim); + auto sv_row = lines[row_id].ViewSlices(delim); out_data[row_id].resize(sv_row.size()); for (size_t i=0; i < sv_row.size(); i++) { out_data[row_id][i] = from_string(sv_row[i]); @@ -332,6 +404,59 @@ namespace emp { return out_data; } + + // A File::Scan object allows a user to easily step through a File. + class Scan { + private: + const File & file; + size_t line = 0; + + public: + Scan(const File & in, size_t start=0) : file(in), line(start) { } + Scan(const Scan & in) = default; + + const File & GetFile() const { return file; } + size_t GetLine() const { return line; } + + bool AtStart() const { return line == 0; } + bool AtEnd() const { return line >= file.size(); } + operator bool() const { return !AtEnd(); } + + void Set(size_t in_line) { line = in_line; } + void Reset() { line = 0; } + void SetEnd() { line = file.size(); } + + // Get the very next line. + const String & Read() { + if (line > file.size()) return String::Empty(); + return file[line++]; + } + + // Get a block of lines. + emp::vector ReadTo(size_t end) { + emp_assert(end >= line); + if (end > file.size()) end = file.size(); + size_t start = line; + line = end; + return file.Read(start, end); + } + + // Get a block of lines, ending when a condition is met. + emp::vector ReadUntil(auto test_fun) { + auto out = file.ReadUntil(line, test_fun); + line += out.size(); + return out; + } + + // Get a block of lines for as lone as a condition is met. + emp::vector ReadWhile(auto test_fun) { + auto out = file.ReadWhile(line, test_fun); + line += out.size(); + return out; + } + }; + + Scan StartScan(size_t start=0) const { return Scan(*this, start); } }; } diff --git a/include/emp/io/MemoryIStream.hpp b/include/emp/io/MemoryIStream.hpp index 040b0d3c84..912023a5b5 100644 --- a/include/emp/io/MemoryIStream.hpp +++ b/include/emp/io/MemoryIStream.hpp @@ -1,17 +1,19 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file MemoryIStream.hpp + * @file * @brief Useful for streaming data from contiguous memory. - * @note Status: RELEASE + * Status: RELEASE */ #ifndef EMP_IO_MEMORYISTREAM_HPP_INCLUDE #define EMP_IO_MEMORYISTREAM_HPP_INCLUDE #include +#include #include namespace emp { diff --git a/include/emp/io/NullStream.hpp b/include/emp/io/NullStream.hpp index 43f3d869e1..88762e0022 100644 --- a/include/emp/io/NullStream.hpp +++ b/include/emp/io/NullStream.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018 - * - * @file NullStream.hpp + * @file * @brief A handy no-operation output stream. * @note Status: BETA */ diff --git a/include/emp/io/StreamManager.hpp b/include/emp/io/StreamManager.hpp index a516f2257f..b7b98af5be 100644 --- a/include/emp/io/StreamManager.hpp +++ b/include/emp/io/StreamManager.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020-2021. - * - * @file StreamManager.hpp + * @file * @brief The StreamManager object links names to files or other streams. * @note Status: BETA * @@ -36,7 +37,7 @@ namespace emp { protected: - // Helper under error conditions. + // Helper, especially under error conditions. static std::iostream & GetDefaultStream() { static std::stringstream default_stream; return default_stream; @@ -136,6 +137,7 @@ namespace emp { if constexpr (ACCESS == Access::INPUT) ptr = NewPtr(name); else if constexpr (ACCESS == Access::OUTPUT) ptr = NewPtr(name); else if constexpr (ACCESS == Access::IO) ptr = NewPtr(name); + else emp_error("Unknown access type for file creation in StreamManager."); } // Build string streams. @@ -305,12 +307,18 @@ namespace emp { std::istream & GetInputStream(const std::string & name) { - if (!HasInputStream(name)) return AddInputStream(name); + if (!HasInputStream(name)) { // If we don't have this input stream, add it! + emp_assert(!Has(name)); // Make sure we don't have this stream at all! + return AddInputStream(name); + } return streams[name]->GetInputStream(); } std::ostream & GetOutputStream(const std::string & name) { - if (!HasOutputStream(name)) return AddOutputStream(name); + if (!HasOutputStream(name)) { // If we don't have this output stream, add it! + emp_assert(!Has(name)); // Make sure we don't have this stream at all! + return AddOutputStream(name); + } return streams[name]->GetOutputStream(); } diff --git a/include/emp/io/ascii_utils.hpp b/include/emp/io/ascii_utils.hpp index 5e14132887..4fdad4e340 100644 --- a/include/emp/io/ascii_utils.hpp +++ b/include/emp/io/ascii_utils.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file ascii_utils.hpp + * @file * @brief Tools for working with ascii output. * @note Status: ALPHA * @@ -14,6 +15,7 @@ #include #include +#include #include "../base/assert.hpp" #include "../base/vector.hpp" @@ -23,7 +25,7 @@ namespace emp { /// The following function prints an ascii bar graph on to the screen (or provided stream). template - void AsciiBarGraph( emp::vector data, + void AsciiBarGraph( emp::vector data, ///< Data to graph size_t max_width=80, ///< What's the widest bars allowed? bool show_scale=true, ///< Should we show the scale at bottom. bool max_scale_1=true, ///< Should we limit scaling to 1:1? @@ -47,7 +49,7 @@ namespace emp { /// Take the input data, break it into bins, and print it as a bar graph. template - void AsciiHistogram(emp::vector data, + void AsciiHistogram(emp::vector data, ///< Data to graph size_t num_bins=40, ///< How many bins in histogram? size_t max_width=80, ///< What's the widest bars allowed? bool show_scale=true, ///< Should we show the scale at bottom? diff --git a/include/emp/io/serialize.hpp b/include/emp/io/serialize.hpp index a6b27e339a..f14be1641a 100644 --- a/include/emp/io/serialize.hpp +++ b/include/emp/io/serialize.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2021. - * - * @file serialize.hpp + * @file * @brief Tools to save and load data from classes. * @note Status: ALPHA * @@ -52,6 +53,7 @@ #ifndef EMP_IO_SERIALIZE_HPP_INCLUDE #define EMP_IO_SERIALIZE_HPP_INCLUDE +#include #include #include "../base/Ptr.hpp" diff --git a/include/emp/io/serialize_macros.hpp b/include/emp/io/serialize_macros.hpp index 1574c81289..5cc893f96b 100644 --- a/include/emp/io/serialize_macros.hpp +++ b/include/emp/io/serialize_macros.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2021. - * - * @file serialize_macros.hpp + * @file * @brief Macros for simplifying to serialization of objects. * @note Status: ALPHA */ diff --git a/include/emp/matching/MatchBin.hpp b/include/emp/matching/MatchBin.hpp index 22cb215fce..2acb717f7a 100644 --- a/include/emp/matching/MatchBin.hpp +++ b/include/emp/matching/MatchBin.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file MatchBin.hpp + * @file * @brief A container that supports flexible tag-based lookup. . * */ @@ -26,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -61,6 +63,7 @@ namespace emp::internal { using query_t = Query; using tag_t = Tag; + #ifndef DOXYGEN_SHOULD_SKIP_THIS template < typename Val, typename Metric, @@ -68,7 +71,7 @@ namespace emp::internal { typename Regulator > friend class emp::MatchBin; - + #endif /*DOXYGEN_SHOULD_SKIP_THIS*/ struct LogEntry { query_t query; diff --git a/include/emp/matching/MatchDepository.hpp b/include/emp/matching/MatchDepository.hpp index 67550bae6a..816fb02a19 100644 --- a/include/emp/matching/MatchDepository.hpp +++ b/include/emp/matching/MatchDepository.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file MatchDepository.hpp + * @file * @brief A container for tag-based lookup, optimized for situations where * tags are not removed from the lookup set. * @@ -14,6 +15,7 @@ #include #include +#include #include "../datastructs/SmallFifoMap.hpp" #include "../datastructs/SmallVector.hpp" diff --git a/include/emp/matching/_DepositoryEntry.hpp b/include/emp/matching/_DepositoryEntry.hpp index df19fd3fc6..b8f5c704fa 100644 --- a/include/emp/matching/_DepositoryEntry.hpp +++ b/include/emp/matching/_DepositoryEntry.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file _DepositoryEntry.hpp + * @file * @brief Helper struct for MatchDepository. * */ diff --git a/include/emp/matching/matchbin_metrics.hpp b/include/emp/matching/matchbin_metrics.hpp index 5a2b340c90..70cfde0aeb 100644 --- a/include/emp/matching/matchbin_metrics.hpp +++ b/include/emp/matching/matchbin_metrics.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019-2022. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019-2021. - * - * @file matchbin_metrics.hpp + * @file * @brief Metric structs that can be plugged into MatchBin. * */ @@ -19,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -37,7 +40,6 @@ #include "../datastructs/tuple_utils.hpp" #include "../math/Distribution.hpp" #include "../math/math.hpp" -#include "../polyfill/span.hpp" #include "../tools/string_utils.hpp" namespace emp { @@ -254,7 +256,7 @@ namespace emp { } inline static double calculate(const query_t& a, const tag_t& b) { - return (b - a).GetValue() / emp::BitSet::GetNumStates(); + return (b - a).GetValue() / emp::Pow2(Width); } }; @@ -281,7 +283,7 @@ namespace emp { } inline static double calculate(const query_t& a, const tag_t& b) { - constexpr double max_dist = emp::BitSet::GetNumStates(); + constexpr double max_dist = emp::Pow2(Width); return (b >= a ? (b - a).GetValue() : max_dist) / max_dist; } @@ -312,7 +314,7 @@ namespace emp { } inline static double calculate(const query_t& a, const tag_t& b) { - constexpr double max_dist = emp::BitSet::GetNumStates() / 2.0; + constexpr double max_dist = emp::Pow2(Width) / 2.0; return std::min(a - b, b - a).GetValue() / max_dist; } @@ -342,7 +344,7 @@ namespace emp { } inline static double calculate(const query_t& a, const tag_t& b) { - return (a > b ? a - b : b - a).GetValue() / emp::BitSet::GetNumStates(); + return (a > b ? a - b : b - a).GetValue() / emp::Pow2(Width); } }; @@ -714,7 +716,7 @@ namespace emp { for (size_t i = 0; i < metric_width; ++ i) { best = std::min(Metric::calculate(dup, b), best); - dup.template ROTL_SELF<1>(); + dup.ROTATE_SELF(-1); } return best; @@ -1176,22 +1178,22 @@ namespace emp { : public BaseMetric< emp::BitSet< std::tuple_size::value - * DimMetric::query_t::value_type::GetSize() + * DimMetric::query_t::value_type::GetCTSize() >, emp::BitSet< std::tuple_size::value - * DimMetric::tag_t::value_type::GetSize() + * DimMetric::tag_t::value_type::GetCTSize() > > { using query_t = emp::BitSet< std::tuple_size::value - * DimMetric::query_t::value_type::GetSize() + * DimMetric::query_t::value_type::GetCTSize() >; using tag_t = emp::BitSet< std::tuple_size::value - * DimMetric::tag_t::value_type::GetSize() + * DimMetric::tag_t::value_type::GetCTSize() >; inline static DimMetric metric{}; @@ -1214,8 +1216,8 @@ namespace emp { typename DimMetric::tag_t arr_b; for (size_t d = 0; d < metric.dim(); ++d) { - arr_a[d].Import(a, d * DimMetric::query_t::value_type::GetSize()); - arr_b[d].Import(b, d * DimMetric::tag_t::value_type::GetSize()); + arr_a[d].Import(a, d * DimMetric::query_t::value_type::GetCTSize()); + arr_b[d].Import(b, d * DimMetric::tag_t::value_type::GetCTSize()); } return DimMetric::calculate(arr_a, arr_b); diff --git a/include/emp/matching/matchbin_regulators.hpp b/include/emp/matching/matchbin_regulators.hpp index d6a9bb7398..16937b6d10 100644 --- a/include/emp/matching/matchbin_regulators.hpp +++ b/include/emp/matching/matchbin_regulators.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019-2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019-2020. - * - * @file matchbin_regulators.hpp + * @file * @brief Regulator structs that can be plugged into MatchBin. * */ @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/include/emp/matching/matchbin_selectors.hpp b/include/emp/matching/matchbin_selectors.hpp index 1e7057e5c5..1010bbf3bb 100644 --- a/include/emp/matching/matchbin_selectors.hpp +++ b/include/emp/matching/matchbin_selectors.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019-2021. - * - * @file matchbin_selectors.hpp + * @file * @brief Selector structs that can be plugged into MatchBin. * */ @@ -19,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/include/emp/matching/matchbin_utils.hpp b/include/emp/matching/matchbin_utils.hpp index 893b971fcf..b1e8f03631 100644 --- a/include/emp/matching/matchbin_utils.hpp +++ b/include/emp/matching/matchbin_utils.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2019 - * - * @file matchbin_utils.hpp + * @file * @brief Metric, Selector, and Regulator structs * that can be plugged into MatchBin. * diff --git a/include/emp/matching/regulators/PlusCountdownRegulator.hpp b/include/emp/matching/regulators/PlusCountdownRegulator.hpp index daf34375c7..4434cb6260 100644 --- a/include/emp/matching/regulators/PlusCountdownRegulator.hpp +++ b/include/emp/matching/regulators/PlusCountdownRegulator.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file PlusCountdownRegulator.hpp + * @file * @brief Regulator that modifies match distance through addition * and decays to baseline with a countdown timer. * diff --git a/include/emp/matching/selectors_static/RankedSelector.hpp b/include/emp/matching/selectors_static/RankedSelector.hpp index a3d339e45d..389cddec2d 100644 --- a/include/emp/matching/selectors_static/RankedSelector.hpp +++ b/include/emp/matching/selectors_static/RankedSelector.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2020 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 - * - * @file RankedSelector.hpp + * @file * @brief Selector that picks the N best matches within a threshold. * */ @@ -14,6 +15,7 @@ #include #include #include +#include #include "../../base/vector.hpp" #include "../../datastructs/SmallVector.hpp" diff --git a/include/emp/math/CombinedBinomialDistribution.hpp b/include/emp/math/CombinedBinomialDistribution.hpp new file mode 100644 index 0000000000..b1c19fe2fa --- /dev/null +++ b/include/emp/math/CombinedBinomialDistribution.hpp @@ -0,0 +1,86 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018-2022 +*/ +/** + * @file + * @brief A means of quickly generating binomial random variables while only storing a small number of distributions. + * @note Status: ALPHA + * + * Quick check for theory: https://math.stackexchange.com/questions/1176385/sum-of-two-independent-binomial-variables + * + * If we want to generate binomial random variables of various trial counts (n's) using the + * Distribution class, we'd have to create a new Distribution for each unique trial count. + * + * This class leverages the fact that B(n, p) + B(m, p) = B(n + m, p) to calculate binomial + * draws with arbitrary trail counts without storing N distributions. + * By storing distributions for powers of 2, we only store log_2(N) distributions. + * + * Developor Notes: + * - We should come up with a more informative name for the file/class + */ + +#ifndef EMP_MATH_COMBINEDBINOMIALDISTRIBUTION_HPP_INCLUDE +#define EMP_MATH_COMBINEDBINOMIALDISTRIBUTION_HPP_INCLUDE + +#include "./Distribution.hpp" + +namespace emp{ + /// \brief A collection of distributions that allows for pulls from a binomial distribution with arbitrary N while only storing log_2(N) distributions + class CombinedBinomialDistribution{ + protected: + emp::vector distribution_vec; /**< The collection of binomial distributions + used to construct any N */ + double p; ///< The success probability of a single Bernoulli trial + size_t cur_max_power; /**< The maximum power of two currently supported by our + distributions */ + + /// Fetch the smallest power of two that is larger than N + size_t GetMaxPower(size_t n) const { + size_t power = 0; + for(size_t val = 1; val < n; val <<= 1, ++power){ ; } + return power; + } + + public: + CombinedBinomialDistribution() : p(0), cur_max_power(0){ ; } + CombinedBinomialDistribution(double _p, size_t _starting_n) : p(_p), cur_max_power(0){ + Expand(_starting_n); + } + + /// Sample a binomial distribution with n events + size_t PickRandom(size_t n, Random & random){ + size_t local_max_power = GetMaxPower(n); + size_t result = 0; + if(local_max_power > cur_max_power) Expand(n); + for(size_t power = 0; power <= local_max_power; ++power){ + if( (n & (1 << power)) != 0){ + result += distribution_vec[power].PickRandom(random); + } + } + return result; + } + + /// Reset the distribution with a new probability, p, and a starting n value + void Setup(double _p, size_t _n){ + distribution_vec.clear(); + cur_max_power = 0; + p = _p; + if(_n > (1ull << cur_max_power)) Expand(_n); + } + + /// Create more distributions to handle the given value of n + void Expand(size_t max_n){ + cur_max_power = GetMaxPower(max_n); + for(size_t power = distribution_vec.size(); power <= cur_max_power; ++power){ + distribution_vec.emplace_back(p, 1 << power); + } + } + + /// Fetch the current maximum power handled by this combined distribution + size_t GetCurMaxPower(){ return cur_max_power; } + }; +} + +#endif // #ifndef EMP_MATH_COMBINEDBINOMIALDISTRIBUTION_HPP_INCLUDE diff --git a/include/emp/math/Distribution.hpp b/include/emp/math/Distribution.hpp index ccf52b6681..73a8d935af 100644 --- a/include/emp/math/Distribution.hpp +++ b/include/emp/math/Distribution.hpp @@ -1,16 +1,17 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018-2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018-2020. - * - * @file Distribution.hpp + * @file * @brief A set of pre-calculated discrete distributions that can quickly generate random values. * @note Status: ALPHA * * A Distribution is a pre-calculated set of probabilities to quickly pick a whole-number result. - * These should be used when either we need to draw from the same distribution many time (and hence - * the extra time to pre-calculate it is amortized away) -or- in functions that we want to call with - * a range of distributions that we may not know ahead of time. + * These should be used when either we need to draw from the same distribution many time (and + * hence the extra time to pre-calculate it is amortized away) -or- in functions that we want to + * call with a range of distributions that we may not know ahead of time. * * Currently, we have: * @@ -19,7 +20,7 @@ * NegativeBinomial - How many attempts to reach N successes, with p probability per attempt? * * - * Developor Notes: + * Developer Notes: * - We should setup an offset in the base Distribution class to ignore "impossible" low values. * */ @@ -27,6 +28,8 @@ #ifndef EMP_MATH_DISTRIBUTION_HPP_INCLUDE #define EMP_MATH_DISTRIBUTION_HPP_INCLUDE +#include + #include "../datastructs/UnorderedIndexMap.hpp" #include "Random.hpp" @@ -48,6 +51,7 @@ namespace emp { return weights.Index( in_value * GetTotalProb() ); } + /// Pick a random item using this distribution. size_t PickRandom(Random & random) const { emp_assert(weights.GetSize() > 0, "Distribution can only pick a random entry if it has at least one entry!"); return weights.Index( random.GetDouble(GetTotalProb()) ); @@ -123,7 +127,7 @@ namespace emp { }; - /// How many attempts to reach N successes, assumming p probability per attempt? + /// How many attempts to reach N successes, assuming p probability per attempt? class NegativeBinomial : public Distribution { private: double p = 0.0; diff --git a/include/emp/math/DistributionSet.hpp b/include/emp/math/DistributionSet.hpp new file mode 100644 index 0000000000..7065ad8b3a --- /dev/null +++ b/include/emp/math/DistributionSet.hpp @@ -0,0 +1,51 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2022 +*/ +/** + * @file + * @brief Management of pre-calculated distributions with different input values. + * @note Status: ALPHA + * + * A DistributionSet manages a set of pre-calculated distributions. When input values are + * provided, the correct distribution is identified, and the associated value is drawn. + * + */ + +#ifndef EMP_MATH_DISTRIBUTIONSET_HPP_INCLUDE +#define EMP_MATH_DISTRIBUTIONSET_HPP_INCLUDE + +#include "Distribution.hpp" + +#include +#include + +#include "Distribution.hpp" + +#include "../datastructs/tuple_utils.hpp" + +namespace emp { + + /// @param DIST_T Type of distribution being used. + /// @param Ts Types of parameters to choose the set based on. + template + class DistributionSet { + private: + /// Map parameters to pre-calculated distributions. + unordered_map< std::tuple, DIST_T, emp::TupleHash> dist_map; + + public: + size_t PickRandom(Random & random, Ts... args) { + auto arg_tup = std::make_tuple(args...); // Build the tuple to use as a key. + auto [it, success] = dist_map.emplace(arg_tup, DIST_T(args...)); + return it->second.PickRandom(random); + } + }; + + using BinomialSet = emp::DistributionSet; + using NegativeBinomialSet = emp::DistributionSet; + +} + +#endif // #ifndef EMP_MATH_DISTRIBUTIONSET_HPP_INCLUDE diff --git a/include/emp/math/Fraction.hpp b/include/emp/math/Fraction.hpp index d53eabbeec..f427280cf4 100644 --- a/include/emp/math/Fraction.hpp +++ b/include/emp/math/Fraction.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021. - * - * @file Fraction.hpp + * @file * @brief Tools to maintain a more exact fraction (rather than lose precision as a double) * @note Status: ALPHA */ @@ -11,7 +12,8 @@ #ifndef EMP_MATH_FRACTION_HPP_INCLUDE #define EMP_MATH_FRACTION_HPP_INCLUDE -#include "math.hpp" +#include +#include namespace emp { @@ -20,6 +22,7 @@ namespace emp { int64_t num; // Numerator int64_t denom; // Denominator + public: void Reduce() { if (denom == 0) return; // Undefined value! if (num == 0) { denom = 1; return; } // Zero value! @@ -29,16 +32,17 @@ namespace emp { denom = -denom; num = -num; } - const uint64_t gcd = emp::GCD(num, denom); + const int64_t gcd = std::gcd(num, denom); // overflows if uint64_t num /= gcd; denom /= gcd; } - public: - Fraction(int64_t in_num=0, int64_t in_denom=1) : num=in_num, denom=in_denom { } + + Fraction(int64_t in_num=0, int64_t in_denom=1) : num(in_num), denom(in_denom) { } Fraction(const Fraction &) = default; int64_t GetNumerator() const { return num; } - int64_t GetDenomonator() const { return denom; } + int64_t GetDenominator() const { return denom; } }; +} #endif // #ifndef EMP_MATH_FRACTION_HPP_INCLUDE diff --git a/include/emp/math/Random.hpp b/include/emp/math/Random.hpp index afe416ad0b..ef82900e61 100644 --- a/include/emp/math/Random.hpp +++ b/include/emp/math/Random.hpp @@ -1,11 +1,12 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2021. - * - * @file Random.hpp + * @file * @brief A versatile and non-patterned pseudo-random-number generator. - * @note Status: RELEASE + * Status: RELEASE */ #ifndef EMP_MATH_RANDOM_HPP_INCLUDE @@ -13,9 +14,11 @@ #include #include +#include #include #include #include +#include #include "../base/assert.hpp" #include "../base/Ptr.hpp" @@ -24,7 +27,9 @@ #include "Range.hpp" namespace emp { + #ifndef DOXYGEN_SHOULD_SKIP_THIS using namespace emp; + #endif // DOXYGEN_SHOULD_SKIP_THIS /// Middle Square Weyl Sequence: A versatile and non-patterned pseudo-random-number /// generator. @@ -71,6 +76,9 @@ namespace emp { /// Starts a new sequence of pseudo random numbers. A negative seed means that the random /// number generator gets its seed from the current system time and the process memory. void ResetSeed(const int64_t seed) noexcept { + value = 0; + expRV = 0.0; + // If the provided seed is <= 0, choose a unique seed based on time and memory location. if (seed <= 0) { uint64_t seed_time = (uint64_t) time(NULL); @@ -85,6 +93,10 @@ namespace emp { weyl_state *= 2; // Make sure starting state is even. + // Reset other internal state + value = 0; + expRV = 0.0; + Get(); // Prime the new sequence by skipping the first number. } @@ -105,7 +117,14 @@ namespace emp { /// @return A pseudo-random double in the provided range. inline double GetDouble(const Range range) noexcept { return GetDouble(range.GetLower(), range.GetUpper()); - } + } + + /// @return A pseudo-random double value between (0.0, 1.0] + inline double GetDoubleNonZero() noexcept { + double d = Get() / (double) RAND_CAP; + while(d == 0.0) {d = Get() / (double) RAND_CAP;} + return d; + } /// @return A pseudo-random 32-bit (4 byte) unsigned int value. @@ -162,7 +181,7 @@ namespace emp { inline uint64_t GetUInt64(const uint64_t max) noexcept { if (max <= RAND_CAP) return (uint64_t) GetUInt(max); // Don't need extra precision. - size_t mask = emp::MaskUsed(max); // Create a mask for just the bits we need. + uint64_t mask = emp::MaskUsed(max); // Create a mask for just the bits we need. uint64_t val = GetUInt64() & mask; // Grab a value using just the current bits. while (val >= max) val = GetUInt64() & mask; // Grab new values until we find a valid one. @@ -377,15 +396,15 @@ namespace emp { // Distributions // /// Generate a random variable drawn from a unit normal distribution. - double GetRandNormal() noexcept { + double GetNormal() noexcept { // Draw from a Unit Normal Dist // Using Rejection Method and saving of initial exponential random variable double expRV2; while (1) { - expRV2 = -log(GetDouble()); + expRV2 = -log(GetDoubleNonZero()); expRV -= (expRV2-1)*(expRV2-1)/2; if (expRV > 0) break; - expRV = -log(GetDouble()); + expRV = -log(GetDoubleNonZero()); } if (P(.5)) return expRV2; return -expRV2; @@ -394,18 +413,18 @@ namespace emp { /// @return A random variable drawn from a normal distribution. /// @param mean Center of distribution. /// @param std Standard deviation of distribution. - inline double GetRandNormal(const double mean, const double std) { return mean + GetRandNormal() * std; } + inline double GetNormal(const double mean, const double std) { return mean + GetNormal() * std; } /// Generate a random variable drawn from a Poisson distribution. - inline uint32_t GetRandPoisson(const double n, const double p) { + inline uint32_t GetPoisson(const double n, const double p) { emp_assert(p >= 0.0 && p <= 1.0, p); // Optimizes for speed and calculability using symetry of the distribution - if (p > .5) return (uint32_t) n - GetRandPoisson(n * (1 - p)); - else return GetRandPoisson(n * p); + if (p > .5) return (uint32_t) n - GetPoisson(n * (1 - p)); + else return GetPoisson(n * p); } /// Generate a random variable drawn from a Poisson distribution. - inline uint32_t GetRandPoisson(const double mean) { + inline uint32_t GetPoisson(const double mean) { // Draw from a Poisson Dist with mean; if cannot calculate, return UINT_MAX. // Uses Rejection Method const double a = exp(-mean); @@ -424,7 +443,7 @@ namespace emp { /// This function is exact, but slow. /// @see Random::GetApproxRandBinomial /// @see emp::Binomial in source/tools/Distribution.h - inline uint32_t GetRandBinomial(const double n, const double p) { // Exact + inline uint32_t GetBinomial(const double n, const double p) { // Exact emp_assert(p >= 0.0 && p <= 1.0, p); emp_assert(n >= 0.0, n); // Actually try n Bernoulli events, each with probability p @@ -433,17 +452,18 @@ namespace emp { return k; } - inline uint32_t GetRandGeometric(double p){ - emp_assert(p >= 0 && p <= 1, "Pobabilities must be between 0 and 1"); - // TODO: When we have warnings, add one for passing a really small number to - // this function. Alternatively, make this function not ludicrously slow with small numbers. - // Looks like return floor(ln(GetDouble())/ln(1-p)) might be sufficient? - if (p == 0) { - return std::numeric_limits::infinity(); - } - uint32_t result = 1; - while (!P(p)) { result++;} - return result; + /// Generate a random variable drawn from an exponential distribution. + inline double GetExponential(double p) { + emp_assert(p > 0.0 && p <= 1.0, p); + // if (p == 0.0) return std::numeric_limits::infinity(); + if (p == 1.0) return 0.0; + return std::log(GetDouble()) / std::log(1.0 - p); + } + + /// Generate a random variable drawn from a geometric distribution. + inline uint32_t GetGeometric(double p) { + emp_assert(p > 0.0 && p <= 1.0, p); + return static_cast( GetExponential(p) ) + 1; } }; diff --git a/include/emp/math/Range.hpp b/include/emp/math/Range.hpp index 11954c2998..07934bd93b 100644 --- a/include/emp/math/Range.hpp +++ b/include/emp/math/Range.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2023 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2019 - * - * @file Range.hpp + * @file * @brief A simple way to track value ranges * @note Status: BETA */ @@ -12,70 +13,215 @@ #define EMP_MATH_RANGE_HPP_INCLUDE #include +#include +#include #include "../base/assert.hpp" #include "../base/vector.hpp" +#include "../tools/String.hpp" namespace emp { /// A range of values from a lower limit to and upper limit, of any provided type. - template + template class Range { private: - T lower = std::numeric_limits::min(); ///< Beginning of range, inclusive. - T upper = std::numeric_limits::max(); ///< End of range, inclusive. + T lower = std::numeric_limits::lowest(); ///< Beginning of range, inclusive. + T upper = std::numeric_limits::max(); ///< End of range, (included if INCLUDE_UPPER) + using this_t = Range; public: + static constexpr bool is_integral = std::is_integral(); + Range() = default; - Range(T _l, T _u) : lower(_l), upper(_u) { emp_assert(_l < _u); } + Range(T val) : lower(val), upper(val) { + if constexpr (!INCLUDE_UPPER) upper += GetEpsilon(); + } + Range(T _l, T _u) : lower(_l), upper(_u) { emp_assert(_l <= _u, _l, _u); } + Range(const Range &) = default; + + Range & operator=(const Range&) = default; + bool operator==(const Range& _in) const = default; + bool operator!=(const Range& _in) const = default; T GetLower() const { return lower; } T GetUpper() const { return upper; } - - size_t CalcBin(T value, size_t num_bins) const { - return (size_t) (((double) (value - lower)) / ((double) (upper - lower)) * (double) num_bins); + T GetEpsilon() const { + if constexpr (is_integral) return 1; + else return upper * std::numeric_limits::epsilon(); + } + T GetMaxValue() const { // What is the maximum included value? + if constexpr (INCLUDE_UPPER) return upper; + else return upper - GetEpsilon(); + } + T GetSize() const { return upper - lower + (INCLUDE_UPPER && is_integral); } + [[nodiscard]] static constexpr T MaxLimit() { return std::numeric_limits::max(); } + [[nodiscard]] static constexpr T MinLimit() { return std::numeric_limits::lowest(); } + + emp::String ToString() const { + if constexpr (INCLUDE_UPPER) { + return emp::MakeString('[', lower, ',', upper, ']'); + } else { + return emp::MakeString('[', lower, ',', upper, ')'); + } } - - Range & operator=(const Range&) = default; - bool operator==(const Range& _in) const { return lower==_in.lower && upper==_in.upper; } - bool operator!=(const Range& _in) const { return lower!=_in.lower || upper!=_in.upper; } void SetLower(T l) { lower = l; } void SetUpper(T u) { upper = u; } void Set(T _l, T _u) { emp_assert(_l < _u); lower = _l; upper = _u; } + void ShiftDown(T shift) { + emp_assert(shift > 0); + emp_assert(lower <= upper, lower, upper); + // Guard against underflow + upper = (MinLimit() + shift < upper) ? (upper - shift) : MinLimit(); + lower = (MinLimit() + shift < lower) ? (lower - shift) : MinLimit(); + } + void ShiftUp(T shift) { + emp_assert(shift > 0); + emp_assert(lower <= upper, lower, upper); + // Guard against overflow + upper = (MaxLimit() - shift > upper) ? (upper + shift) : MaxLimit(); + lower = (MaxLimit() - shift > lower) ? (lower + shift) : MaxLimit(); + } + void Shift(T shift) { + if (shift > 0) ShiftUp(shift); + else ShiftDown(-shift); + } - void SetMaxLower() { lower = std::numeric_limits::min(); } + void SetMinLower() { lower = std::numeric_limits::min(); } void SetMaxUpper() { upper = std::numeric_limits::max(); } - /// Determine if a provided value is in the range. - bool Valid(T value) const { return value >= lower && value <= upper; } + void Grow(T amount=1) { + if (amount > 0) upper += amount; + else lower += amount; + } + + // Flexible lower/upper accessor that can get and set. + T & Lower() { return lower; } + T & Upper() { return upper; } + + const T & Lower() const noexcept { return lower; } + const T & Upper() const noexcept { return upper; } + + /// Determine if a provided value is in the range INCLUSIVE of the endpoints. + bool Has(T value) const { + return (value >= lower && value < upper) || (INCLUDE_UPPER && value == upper); + } + [[deprecated("Renamed to Has()")]] + bool Valid(T value) const { return Has(value); } + + bool HasRange(this_t in_range) { + return Has(in_range.lower) && Has(in_range.upper); + } + + /// Will identify if two ranges are next to each other or overlapping. + bool IsConnected(this_t in) const { + return (in.lower >= lower && in.lower <= upper) || + (lower >= in.lower && lower <= in.upper); + } + + /// Determine if there is overlap between two range. + /// Similar to IsConnected, but cannot be merely adjacent. + bool HasOverlap(this_t in) const { + return (in.lower >= lower && in.lower < upper) || + (lower >= in.lower && lower < in.upper); + } + + /// Determine the amount of overlap between two range. + T CalcOverlap(this_t in) const { + const T combo_upper = std::min(upper, in.upper); + const T combo_lower = std::max(lower, in.lower); + return (combo_upper > combo_lower) ? (combo_upper - combo_lower) : T{}; + } + + /// @brief Expand this range to encompass a provided value. + /// @param val Value to expand through. + /// @return Whether the range has changed due to this expansion. + bool Expand(T val) { + if (val < lower) lower = val; + else if (val > upper) { + upper = val; + if constexpr (INCLUDE_UPPER) upper += GetEpsilon(); + } else return false; + return true; + } + + /// @brief Expand this range to encompass all provided values. + /// @return Whether the range has changed due to this expansion. + template + bool Expand(T val1, T val2, Ts... args) { + return Expand(val1) + Expand(val2, args...); // Use + to avoid short-circuiting. + } + + /// Merge this range with another. Must be adjacent or overlap (return false if not!) + bool Merge(this_t in) { + if (!IsConnected(in)) return false; + Expand(in.lower, in.upper); + return true; + } + + /// Add a specified value to the end of a range (or return false if failed). + bool Append(T val) { + emp_assert(is_integral, "Only integral ranges can call Append() with a single value."); + if (val != upper + INCLUDE_UPPER) return false; + upper++; + return true; + } /// Force a value into range - T Limit(T _in) const { return (_in < lower) ? lower : ((_in > upper) ? upper : _in); } + T Clamp(T _in) const { + return (_in < lower) ? lower : ((_in >= upper) ? GetMaxValue() : _in); + } + [[deprecated("Renamed to Clamp()")]] + T LimitValue(T _in) const { return Clamp(_in); } + + double ToFraction(T _in) const { + emp_assert(GetSize() != 0); + return static_cast(_in - lower) / static_cast(GetSize()); + } + T FromFraction(double frac) const { return frac * GetSize() + lower; } + + // Adjust the upper or lower if provided value is more limiting. + void LimitLower(T in) { if (in > lower) lower = in; } + void LimitUpper(T in) { if (in < upper) upper = in; } + + size_t CalcBin(T value, size_t num_bins) const { + if (upper == lower) return 0; + return (size_t) (((double) (value - lower)) / ((double) (upper - lower)) * (double) num_bins); + } /// Produce a vector that spreads values evenly across the range. - emp::vector Spread(size_t s) const { + emp::vector Spread(const size_t s) const { emp_assert(s >= 1); + if (s == 1) return emp::vector(1,lower/2+upper/2); // On point is in the middle of the range. emp::vector out(s); out[0] = lower; - if (s > 1) { - T range = upper - lower; - for (size_t i = 1; i < s; i++) { - out[i] = lower + (T) ((((double) i) / (double)(s-1)) * range); - } + T range = upper - lower; + for (size_t i = 1; i < s; i++) { + double frac = static_cast(i)/static_cast(s-1); + out[i] = lower + static_cast(frac * range); } return out; } }; /// Build a new range with auto-detected type. - template Range MakeRange(T _l, T _u) { return Range(_l,_u); } + template + Range MakeRange(T _l, T _u) { + return Range(_l,_u); + } /// Build a new range of type int. - inline Range IntRange(int _l, int _u) { return Range(_l,_u); } + template + inline Range IntRange(int _l, int _u) { + return Range(_l,_u); + } /// Build a new range of type double. - inline Range DRange(double _l, double _u) { return Range(_l,_u); } + template + inline Range DRange(double _l, double _u) { + return Range(_l,_u); + } } #endif // #ifndef EMP_MATH_RANGE_HPP_INCLUDE diff --git a/include/emp/math/RangeSet.hpp b/include/emp/math/RangeSet.hpp new file mode 100644 index 0000000000..b30c43fb05 --- /dev/null +++ b/include/emp/math/RangeSet.hpp @@ -0,0 +1,500 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2023 +*/ +/** + * @file + * @brief A collection of ranges that can be operated on collectively. + * @note Status: BETA + */ + +#ifndef EMP_MATH_RANGESET_HPP_INCLUDE +#define EMP_MATH_RANGESET_HPP_INCLUDE + +#include + +#include "../base/vector.hpp" +#include "../datastructs/vector_utils.hpp" + +#include "Range.hpp" + +namespace emp { + + /// RangeSet maintains a collection of ranges. The ranges are exclusive of the endpoint + /// and kept sorted and non-adjacent (i.e., there is a gap between successive ranges). + template + class RangeSet { + public: + using range_t = emp::Range; + using this_t = RangeSet; + + private: + emp::vector range_set; + + // Helper function to find the id of an Range that a value belongs in or can extend; + // returns next-higher index (where a new range would be place) if none fit perfectly. + // size_t _FindRange(size_t val) const { + // for (size_t id = 0; id < range_set.size(); ++id) { + // if (id <= range_set[id].GetEnd()) return id; + // } + // return range_set.size(); + // } + size_t _FindRange(T value) const { + auto it = std::lower_bound( + range_set.begin(), + range_set.end(), + value, + [](const range_t & range, T value) { return range.GetUpper() < value; } + ); + return it - range_set.begin(); + }; + + void _InsertRange(size_t id, range_t range) { emp::InsertAt(range_set, id, range); } + void _RemoveRange(size_t id) { emp::RemoveAt(range_set, id); } + void _RemoveRanges(size_t id, size_t count) { emp::RemoveAt(range_set, id, count); } + + // Helper function to remove empty ranges at the beginning. + void _PruneEmptyFront() { + size_t count = 0; + while (count < range_set.size() && range_set[count].GetSize() == 0) ++count; + if (count) _RemoveRanges(0, count); + } + + // Helper function to remove empty ranges at the end. + void _PruneEmptyBack() { + size_t count = 0; + while (count < range_set.size() && + range_set[range_set.size()-count-1].GetSize() == 0) ++count; + if (count) range_set.resize(range_set.size() - count); + } + + // Helper function to increase the side of a range, possibly merging it with the next range. + void _CleanupMerge(size_t id) { + while (id+1 < range_set.size() && range_set[id].Merge(range_set[id+1])) { + _RemoveRange(id+1); // Delete next range (merged in to current) + } + emp_assert(OK()); + } + + // Helper function to convert a string into a RangeSet. + // Two formats are available + // bitstring: 010001110101111 + // RangeSet: [1,2),[5,8),[9,10),[11,15) + // In the bitstring format, if the final character is '+', all additional positions are + // assumed to be 1. In the RangeSet format, if the first or last element is a '*', it is + // assumed to be the limit for the type; also commas are optional. + // A star by itself is a full RangeSet. + void _FromString(emp::String in) { + if (in.size() == 0) { Clear(); } + else if (in[0] == '*') { + emp::notify::TestError(in.size() > 1, "Star indicates a full range, but must be by itself."); + SetAll(); + } + else if (in[0] == '0' || in[0] == '1') { + Clear(); + for (size_t i=0; i < in.size(); ++i) { + if (in[i] != '0') Insert((T)i); + } + } + else if (in[0] == '[') { + while (in.size()) { + emp::String segment = in.Pop(')'); + segment.PopIf(','); + emp::notify::TestError(!segment.PopIf('['), "Each segment of a RangeSet must begin with '['"); + T start = segment.PopIf('*') ? MinLimit() : segment.PopLiteral(); + emp::notify::TestError(!segment.PopIf(',') && !segment.PopIf('-'), + "Each segment of a RangeSet must be separated by ',' or '-'"); + T end = segment.PopIf('*') ? MaxLimit() : segment.PopLiteral(); + InsertRange(start, end); + } + } + } + + public: + static constexpr bool is_integral = std::is_integral(); + + RangeSet() = default; + explicit RangeSet( range_t start_range) { Insert(start_range); } + RangeSet(T start, T end) { InsertRange(start, end); } + RangeSet(const RangeSet &) = default; + RangeSet(RangeSet &&) = default; + RangeSet(const std::string & bitstring) { + emp_assert(is_integral, "RangeSets can be represented as strings only if they are integral."); + for (size_t i=0; i < bitstring.size(); ++i) { + if (bitstring[i] == '1') Insert((T)i); + } + } + + RangeSet & operator=(const RangeSet &) = default; + RangeSet & operator=(RangeSet &&) = default; + RangeSet & operator=(const std::string & bitstring) { + emp_assert(is_integral, "RangeSets can be represented as strings only if they are integral."); + Clear(); + for (size_t i=0; i < bitstring.size(); ++i) { + if (bitstring[i] == '1') Insert((T)i); + } + return *this; + } + + [[nodiscard]] bool operator<=>(const RangeSet &) const = default; + + [[nodiscard]] bool Has(T val) const { + const size_t id = _FindRange(val); + return (id < range_set.size()) ? range_set[id].Has(val) : false; + } + [[nodiscard]] bool HasRange(range_t range) const { + const size_t id = _FindRange(range.Lower()); + return (id < range_set.size()) ? range_set[id].HasRange(range) : false; + } + [[nodiscard]] bool IsEmpty() const { return !range_set.size(); } + [[nodiscard]] static constexpr T MaxLimit() { return std::numeric_limits::max(); } + [[nodiscard]] static constexpr T MinLimit() { return std::numeric_limits::lowest(); } + + /// @return Overall start of all ranges (or max value if no ranges exist.) + [[nodiscard]] T GetStart() const { return IsEmpty() ? MaxLimit() : range_set[0].Lower(); } + + /// @return Overall end of all ranges (or min value if no ranges exist.) + [[nodiscard]] T GetEnd() const { return IsEmpty() ? MinLimit() : range_set.back().Upper(); } + + [[nodiscard]] size_t GetNumRanges() const { return range_set.size(); } + + /// @brief Calculate the total combined size of all ranges. + [[nodiscard]] T GetSize() const { + T total = 0; + for (const auto & x : range_set) total += x.GetSize(); + return total; + } + + /// Present this set of ranges as a string. + [[nodiscard]] emp::String ToString() const { + emp::String out; + for (size_t i = 0; i < range_set.size(); ++i) { + if (i) out += ','; + out += range_set[i].ToString(); + } + return out; + } + + // Return all of the internal ranges (can only be called on l-values) + [[nodiscard]] const emp::vector & GetRanges() const & { return range_set; } + + // Calculate the size of the overlap with a provided range. + [[nodiscard]] bool HasOverlap(range_t range) const { + size_t low_id = _FindRange(range.GetLower()); + if (low_id >= range_set.size()) return false; // Entirely after ranges. + if (range_set[low_id].HasOverlap(range)) return true; // Overlaps at beginning. + return low_id+1 < range_set.size() && range_set[low_id+1].HasOverlap(range); + } + + // Calculate the size of the overlap with a provided range. + [[nodiscard]] T CalcOverlap(range_t range) const { + size_t low_id = _FindRange(range.GetLower()); + size_t up_id = _FindRange(range.GetUpper()); + T result = range_set[low_id].CalcOverlap(range); + if (low_id < up_id) { + for (size_t id=low_id+1; id < up_id; id++) result += range_set[id].GetSize(); + result += range_set[up_id].CalcOverlap(range); + } + return result; + } + + /// @brief Remove all ranges in the set. + RangeSet & Clear() { range_set.resize(0); return *this; } + + /// @brief Set a single range that includes all value. + RangeSet & SetAll() { InsertRange(MinLimit(), MaxLimit()); return *this; } + + /// @brief Shift all ranges by a fixed amount. + /// @param shift How much should the range be shifted by? + RangeSet & Shift(T shift) { + if (shift > 0) ShiftUp(shift); + else if (shift < 0) ShiftDown(shift); + return *this; + } + + RangeSet & ShiftUp(T shift) { + for (auto & range : range_set) range.ShiftUp(shift); + _PruneEmptyBack(); + return *this; + } + + RangeSet & ShiftDown(T shift) { + for (auto & range : range_set) range.ShiftDown(shift); + _PruneEmptyFront(); + return *this; + } + + + [[nodiscard]] this_t CalcShift(T shift) const { + this_t out(*this); + return out.Shift(shift); + } + + [[nodiscard]] this_t CalcShiftDown(T shift) const { + this_t out(*this); + return out.ShiftDown(shift); + } + + [[nodiscard]] this_t CalcShiftUp(T shift) const { + this_t out(*this); + return out.ShiftUp(shift); + } + + /// @brief Insert a value into this range set + /// @param val Value to insert. + /// @return This RangeSet after insertion. + RangeSet & Insert(T val) { + emp_assert(is_integral, "Only integral ranges can call Insert() with a single value."); + + // If empty or beyond the end, append a new range. + if (range_set.size() == 0 || val > GetEnd()) { + range_set.emplace_back(val); + } + + else { + const size_t id = _FindRange(val); + emp_assert(id < range_set.size(), id, range_set.size()); + range_t & range = range_set[id]; + + if (range.Has(val)) return *this; // Already has the value! + else if (range.Append(val)) _CleanupMerge(id); // Extending 'upper' on range + else if (range.GetLower() == val+1) range.Lower()--; // Extending 'lower' on range + else range_set.emplace(range_set.begin()+id, val); // Inserting NEW range. + } + + return *this; + } + + /// @brief Insert a whole range into this set, merging other ranges if needed. + /// @param in New range to include. + /// @return This RangeSet after insertion. + RangeSet & Insert(range_t in) { + const size_t start_id = _FindRange(in.GetLower()); + + // Are we adding a whole new range to the end? + if (start_id == range_set.size()) range_set.push_back(in); + + // Is it already included in the found range? No change! + else if (range_set[start_id].HasRange(in)) return *this; + + // Should we merge in with an existing range? + else if (range_set[start_id].IsConnected(in)) { + range_set[start_id].Merge(in); + _CleanupMerge(start_id); + } + + // Otherwise insert as a new range. + else _InsertRange(start_id, in); + + return *this; + } + + /// @brief Merge an entire range set into this one. + /// @param in_set Range set to add in. + /// @return This RangeSet after insertion. + /// @note Can be optimized to handle big set mergers more efficiently! + RangeSet & Insert(const this_t & in_set) { + for (const range_t & range : in_set.GetRanges()) Insert(range); + return *this; + } + + /// @brief Insert a range into this set, specifying the start and end points. + /// @param start Beginning of new range to include. + /// @param stop Ending of new range to include (range is not inclusive of stop) + /// @return This RangeSet after insertion. + RangeSet & InsertRange(T start, T stop) { return Insert(range_t{start, stop}); } + + /// @brief Remove a single value from this RangeSet. + /// @param val Value to remove + /// @return This RangeSet after removal. + RangeSet & Remove(T val) { + emp_assert(is_integral, "Only integral ranges can call Remove() with a single value."); + + if (!Has(val)) return *this; // Nothing to remove. + + const size_t id = _FindRange(val); + range_t & range = range_set[id]; + if (range.GetSize() == 1) _RemoveRange(id); // Remove whole range + else if (range.GetLower() == val) range.Lower()++; // Inc lower end + else if (range.GetUpper()-1 == val) range.Upper()--; // Dec upper end + else { // Split a range! + _InsertRange(id+1, range_t{val+1,range.GetUpper()}); + range_set[id].SetUpper(val); + } + return *this; + } + + /// @brief Remove all ranges (or partial range) less than a target value. + /// @param val New floor for ranges. + /// @return This RangeSet after removal. + RangeSet & RemoveTo(T val) { + if (val <= GetStart()) return *this; // Nothing to remove. + size_t id = _FindRange(val); + if (val == range_set[id].GetUpper()) ++id; + _RemoveRanges(0, id); // Remove everything before the new start. + if (range_set.size() && range_set[0].Lower() < val) range_set[0].SetLower(val); + return *this; + } + + /// @brief Remove all ranges (or partial range) greater than a target value. + /// @param val New cap for ranges. + /// @return This RangeSet after removal. + RangeSet & RemoveFrom(T val) { + if (val >= GetEnd()) return *this; // Nothing to remove. + size_t id = _FindRange(val); + if (val > range_set[id].GetLower()) ++id; // Include current range if needed. + range_set.resize(id); // Remove everything past new end. + if (GetEnd() > val) range_set.back().SetUpper(val); + return *this; + } + + /// @brief Remove a whole Range from this RangeSet. + /// @param rm_range Range to remove + /// @return This RangeSet after removal. + RangeSet & Remove(range_t rm_range) { + if (!HasOverlap(rm_range)) return *this; + if (rm_range.Lower() <= GetStart()) return RemoveTo(rm_range.Upper()); + if (rm_range.Upper() >= GetEnd()) return RemoveFrom(rm_range.Lower()); + + // Must be removing from the middle. + size_t start_id = _FindRange(rm_range.Lower()); + range_t & start_range = range_set[start_id]; + + // Fully internal to a single Range? Split it! + if (start_range.Lower() < rm_range.Lower() && start_range.Upper() > rm_range.Upper()) { + _InsertRange(start_id+1, range_t{rm_range.Upper(), start_range.Upper()}); + range_set[start_id].SetUpper(rm_range.Lower()); + return *this; + } + + // Deal with beginning of removal - cut it down if needed, and move on to next range. + if (rm_range.Lower() > start_range.Lower()) { + start_range.Upper() = rm_range.Lower(); + ++start_id; + } + + // Deal with end of removal. + size_t end_id = _FindRange(rm_range.Upper()); + if (rm_range.Upper() >= range_set[end_id].Upper()) end_id++; + else range_set[end_id].Lower() = std::max(range_set[end_id].Lower(), rm_range.Upper()); + + // Remove middle. + _RemoveRanges(start_id, end_id - start_id); + + return *this; + } + + /// @brief Remove all ranges in an entire range set from this one. + /// @param in_set Range set to remove. + /// @return This RangeSet after removal. + /// @note Can be optimized to handle big sets more efficiently! + RangeSet & Remove(const this_t & in_set) { + for (const range_t & range : in_set.GetRanges()) Remove(range); + return *this; + } + + + RangeSet & RemoveRange(T start, T stop) { return Remove(range_t{start, stop}); } + + /// @brief Remove everything outside of the provided range. + RangeSet & KeepOnly(T start, T stop) { + emp_assert(start < stop); + RemoveTo(start); + return RemoveFrom(stop); + } + + /// @brief Remove everything outside of the provided range. + RangeSet & KeepOnly(range_t keep_range) { + return KeepOnly(keep_range.GetLower(), keep_range.GetUpper()); + } + + /// @brief Remove everything outside of the provided set of ranges. + RangeSet & KeepOnly(const this_t & in_set) { return Remove(~in_set); } + + + // Some more advanced functions. + + /// @brief Calculate the inverted range set, swapping included and excluded values. + /// @return The inverted RangeSet. + [[nodiscard]] this_t CalcInverse() const { + emp_assert(OK()); + // If this is an empty set, return a full set. + if (range_set.size() == 0) return this_t(MinLimit(), MaxLimit()); + + // Determine if we need to extend the the limits on each side. + const bool add_begin = (GetStart() != MinLimit()); + const bool add_end = (GetEnd() != MaxLimit()); + this_t out; + out.range_set.reserve(range_set.size() + add_begin + add_end - 1); + if (add_begin) out.range_set.emplace_back(MinLimit(),GetStart()); + for (size_t i = 1; i < range_set.size(); ++i) { + out.range_set.emplace_back(range_set[i-1].Upper(), range_set[i].Lower()); + } + if (add_end) out.range_set.emplace_back(GetEnd(), MaxLimit()); + emp_assert(out.OK()); + return out; + } + + this_t & Invert() { *this = CalcInverse(); return *this; } + + // Simple operators: + [[nodiscard]] this_t operator~() const { return CalcInverse(); } + [[nodiscard]] this_t operator|(const this_t & in) const { + emp_assert(in.OK()); + this_t out(*this); + return out.Insert(in); + } + [[nodiscard]] this_t operator&(const this_t & in) const { + emp_assert(in.OK()); + this_t out(*this); + return out.Remove(~in); + } + [[nodiscard]] this_t operator^(const this_t & in) { + emp_assert(in.OK()); + return (*this | in) & ~(*this & in); + } + [[nodiscard]] this_t operator<<(const T shift) const { return CalcShiftUp(shift); } + [[nodiscard]] this_t operator>>(const T shift) const { return CalcShiftDown(shift); } + [[nodiscard]] bool operator[](T val) const { return Has(val); } + + this_t & operator|=(const this_t & in) { Insert(in); return *this; } + this_t & operator&=(const this_t & in) { Remove(~in); return *this; } + this_t & operator^=(const this_t & in) { emp_assert(in.OK()); *this = *this^in; return *this; } + this_t & operator<<=(const T shift) { ShiftUp(shift); return *this; } + this_t & operator>>=(const T shift) { ShiftDown(shift); return *this; } + + explicit operator bool() const { return range_set.size(); } + + + /// @brief Overload ostream operator to return Print. + friend std::ostream& operator<<(std::ostream &out, const this_t & range) { + out << range.ToString(); + return out; + } + + /// @brief Check for internal errors in this RangeSet. + bool OK() const { + // Check each range individually. + for (const auto & range : range_set) { + if (range.GetLower() > range.GetUpper()) { + emp::notify::Message("RangeSet::OK() Failed due to invalid range: ", range.ToString()); + return false; + } + } + + // Make sure ranges are in order and have gaps between them. + for (size_t i = 1; i < range_set.size(); ++i) { + if (range_set[i-1].GetUpper() >= range_set[i].GetLower()) { + emp::notify::Message("RangeSet::OK() Failed at range ", i, " of ", range_set.size(), + ". Ranges are: ", ToString()); + return false; + } + } + return true; + } + }; + +} + +#endif // #ifndef EMP_MATH_RANGESET_HPP_INCLUDE diff --git a/include/emp/math/combos.hpp b/include/emp/math/combos.hpp index 0327fff62b..35c10c1b35 100644 --- a/include/emp/math/combos.hpp +++ b/include/emp/math/combos.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017 - * - * @file combos.hpp + * @file * @brief Tools to step through combinations of items. * * Step through all combinations of size K from a set of N values. For ComboIDs just return the @@ -21,6 +22,7 @@ #define EMP_MATH_COMBOS_HPP_INCLUDE #include +#include #include "../base/assert.hpp" #include "../base/vector.hpp" diff --git a/include/emp/math/constants.hpp b/include/emp/math/constants.hpp index a6dfa20444..a70126a905 100644 --- a/include/emp/math/constants.hpp +++ b/include/emp/math/constants.hpp @@ -1,11 +1,12 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2015-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2015-2017 - * - * @file constants.hpp + * @file * @brief Commonly used constant values. - * @note Status: RELEASE + * Status: RELEASE */ #ifndef EMP_MATH_CONSTANTS_HPP_INCLUDE @@ -29,6 +30,8 @@ namespace emp { constexpr const int32_t MIN_INT = -2147483648; ///< (- 2^31) + constexpr const size_t MAX_SIZE_T = static_cast(-1); + /// Determine the maximum value for any type. // @CAO: Prevent inf to get more realistic numbers for double/float? template diff --git a/include/emp/math/distances.hpp b/include/emp/math/distances.hpp index 3407dc83f9..dda0f34abe 100644 --- a/include/emp/math/distances.hpp +++ b/include/emp/math/distances.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2017-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2017-2018 - * - * @file distances.hpp + * @file * @brief Library of commonly used distance functions * @note Status: BETA */ @@ -11,6 +12,8 @@ #ifndef EMP_MATH_DISTANCES_HPP_INCLUDE #define EMP_MATH_DISTANCES_HPP_INCLUDE +#include + #include "../meta/type_traits.hpp" #include "math.hpp" diff --git a/include/emp/math/info_theory.hpp b/include/emp/math/info_theory.hpp index 2e8cb28a99..d2d9abad82 100644 --- a/include/emp/math/info_theory.hpp +++ b/include/emp/math/info_theory.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2021. - * - * @file info_theory.hpp + * @file * @brief Tools to calculate Information Theory metrics. * @note Status: ALPHA * diff --git a/include/emp/math/math.hpp b/include/emp/math/math.hpp index 27b801c7ed..012f1691fe 100644 --- a/include/emp/math/math.hpp +++ b/include/emp/math/math.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021. - * - * @file math.hpp + * @file * @brief Useful mathematical functions (that are constexpr when possible.) * @note Status: BETA (though new functions are added frequently) */ @@ -14,8 +15,10 @@ #include #include +#include #include #include +#include #include "../base/assert.hpp" #include "../meta/reflection.hpp" @@ -269,7 +272,7 @@ namespace emp { // exclude clang versions with compiler bug https://reviews.llvm.org/D35190 #if defined(__clang__) && __clang_major__>=9 || defined(__GNUC__) && !defined(__clang__) // if base is not known at compile time, use std::pow which is faster - if ( !__builtin_constant_p( base ) ) return std::pow(base, exp); + if ( !__builtin_constant_p( base ) ) return static_cast(std::pow(base, exp)); // otherwise, use constexpr-friendly implementations else #endif diff --git a/include/emp/math/random_utils.hpp b/include/emp/math/random_utils.hpp index 7a8aaec4df..9a828dbe2c 100644 --- a/include/emp/math/random_utils.hpp +++ b/include/emp/math/random_utils.hpp @@ -1,17 +1,19 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2017 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2017 - * - * @file random_utils.hpp + * @file * @brief Helper functions for emp::Random for common random tasks. - * @note Status: RELEASE + * Status: RELEASE */ #ifndef EMP_MATH_RANDOM_UTILS_HPP_INCLUDE #define EMP_MATH_RANDOM_UTILS_HPP_INCLUDE #include +#include #include "../base/vector.hpp" #include "../bits/BitVector.hpp" @@ -20,6 +22,12 @@ namespace emp { + /// Choose a random element from an indexable container. + template + inline auto SelectRandom(Random & random, const T & container) { + return container[random.GetUInt(container.size())]; + } + /// Randomly reorder all of the elements in a vector. /// If max_count is provided, just make sure that the first max_count entries are randomly /// drawn from entire vector. @@ -27,6 +35,7 @@ namespace emp { template inline void Shuffle(Random & random, emp::vector & v, size_t max_count) { + emp_assert(max_count <= v.size()); for (size_t i = 0; i < max_count; i++) { const size_t pos = random.GetUInt(i, v.size()); if (pos == i) continue; @@ -37,6 +46,17 @@ namespace emp { template inline void Shuffle(Random & random, emp::vector & v) { Shuffle(random, v, v.size()); } + template + inline void ShuffleRange(Random & random, emp::vector & v, size_t first, size_t last) + { + emp_assert(first <= last); + emp_assert(last <= v.size()); + for (size_t i = first; i < last; i++) { + const size_t pos = random.GetUInt(i, last); + if (pos == i) continue; + std::swap(v[i], v[pos]); + } + } /// Return an emp::vector numbered 0 through size-1 in a random order. diff --git a/include/emp/math/sequence_utils.hpp b/include/emp/math/sequence_utils.hpp index f61c148ce7..b6184e6cf6 100644 --- a/include/emp/math/sequence_utils.hpp +++ b/include/emp/math/sequence_utils.hpp @@ -1,25 +1,67 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2020. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2020. - * - * @file sequence_utils.hpp + * @file * @brief Functions for analyzing with generic sequence types. * @note Status: BETA * * A set of functions for analyzing sequences, including distance metrics (Hamming and - * Edit/Levenschtein) and alignment. + * Edit/Levenshtein) and alignment. */ #ifndef EMP_MATH_SEQUENCE_UTILS_HPP_INCLUDE #define EMP_MATH_SEQUENCE_UTILS_HPP_INCLUDE +#include + +#include "../base/notify.hpp" #include "../base/vector.hpp" +#include "../tools/string_utils.hpp" #include "math.hpp" namespace emp { + /// Generate a sequence from a string. + /// Format: "entry1,entry2,entry3" etc. + /// Entries can be single values (Eg: "72") or ranges using start[:step]:stop format + /// (Eg: "0:100" or "3:5:33"). + + template + emp::vector ToSequence(std::string sequence_str) { + // Clean up input sequence and slice by commas. + emp::remove_whitespace(sequence_str); + emp::vector seq_slices = emp::slice(sequence_str, ','); + emp::vector out; + + // Convert each slice into a value or range of values. + emp::vector range_slices; + for (const std::string & slice : seq_slices) { + emp::slice(slice, range_slices, ':'); + T start = emp::from_string(range_slices[0]); + T step = static_cast(1); + T stop = start + static_cast(1); + + if (range_slices.size() == 2) stop = emp::from_string(range_slices[1]); + else if (range_slices.size() == 3) { + step = emp::from_string(range_slices[1]); + stop = emp::from_string(range_slices[2]); + } + else if (range_slices.size() > 3) { + emp::notify::Exception("math::sequence_utils::ToSequence::invalid_range", + "emp::ToSequence() provided with range with too many ':'", + slice); + } + + for (T i = start; i < stop; i += step) out.push_back(i); + } + + return out; + } + // --- Distance functions for any array-type objects --- /// Hamming distance is a simple count of substitutions needed to convert one array to another. @@ -105,7 +147,7 @@ namespace emp { emp::vector prev_row(size1); // The previous row we calculated emp::vector > edit_info(size2, emp::vector(size1)); - // Initialize the previous row to record the differece from nothing. + // Initialize the previous row to record the difference from nothing. for (size_t i = 0; i < size1; i++) { prev_row[i] = i + 1; edit_info[0][i] = 'i'; diff --git a/include/emp/math/spatial_stats.hpp b/include/emp/math/spatial_stats.hpp index f471d142ff..57bd48eb50 100644 --- a/include/emp/math/spatial_stats.hpp +++ b/include/emp/math/spatial_stats.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2019 - * - * @file spatial_stats.hpp + * @file * @brief Functions for calculating various spatial statistics. * @note Status: BETA */ @@ -12,6 +13,7 @@ #define EMP_MATH_SPATIAL_STATS_HPP_INCLUDE #include +#include #include "../base/vector.hpp" #include "../Evolve/World.hpp" diff --git a/include/emp/math/stats.hpp b/include/emp/math/stats.hpp index 16fac153cc..30417d9436 100644 --- a/include/emp/math/stats.hpp +++ b/include/emp/math/stats.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2019 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2019 - * - * @file stats.hpp + * @file * @brief Functions for calculating various statistics about an ensemble. * @note Status: BETA */ diff --git a/include/emp/meta/ConceptWrapper.hpp b/include/emp/meta/ConceptWrapper.hpp index 36d024e13a..d0ea5f8d8b 100644 --- a/include/emp/meta/ConceptWrapper.hpp +++ b/include/emp/meta/ConceptWrapper.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2018-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2018-2021. - * - * @file ConceptWrapper.hpp + * @file * @brief A template wrapper that will either enforce functionality or provide default functions. * * Starting in future versions of C++, a concept is a set of requirements for a class to be used @@ -29,7 +30,7 @@ * * REQUIRED_OVERLOAD_FUN ( FUNCTION_NAME, ERROR_MESSAGE, RETURN_TYPE, ARG1_TYPES, OTHER_ARGS... ) * Setup a set of overloaded member functions called FUNCTION_NAME that varies the first - * parameter (and may have additional paramters with fixed types. ARG1_TYPES must be an + * parameter (and may have additional parameters with fixed types. ARG1_TYPES must be an * emp::TypePack that includes the full set of types to be used for the first parameter. * Zero or more additional parameters may be included in OTHER_ARGS. The wrapped class must * already define the full set of overloaded functions by the correct name and with the correct @@ -90,13 +91,14 @@ * OPTIONAL_VAR ( VAR_NAME, DEFAULT_VALUE, TYPE ) * Setup a member variable called VAR_NAME. If it already exists in the wrapped class, use * that version. If it does not already exist, create it with the provided TYPE and set it to - * the DEFAULT_VALUE prodided. + * the DEFAULT_VALUE provided. * */ #ifndef EMP_META_CONCEPTWRAPPER_HPP_INCLUDE #define EMP_META_CONCEPTWRAPPER_HPP_INCLUDE +#include #include #include @@ -107,7 +109,7 @@ #define EMP_BUILD_CONCEPT( WRAPPER_NAME, BASE_NAME, ... ) \ - /* Do error-checkig on the inputs! */ \ + /* Do error-checking on the inputs! */ \ EMP_WRAP_EACH(EMP_BUILD_CONCEPT__ERROR_CHECK, __VA_ARGS__) \ /* Build the interface class. */ \ class BASE_NAME { \ @@ -138,8 +140,14 @@ #define EMP_BUILD_CONCEPT__EC_PROTECTED(...) /* PROTECTED okay */ #define EMP_BUILD_CONCEPT__EC_PUBLIC(...) /* PUBLIC okay */ -#define EMP_BUILD_CONCEPT__CHECK_EMPTY(A, CMD) EMP_GET_ARG_2( EMP_BUILD_CONCEPT__SPACER ## A, \ - static_assert(false, "\n\n \033[1;31mInvalid EMP_BUILD_CONCEPT.\033[0m May be invalid command or missing comma in:\n \033[1;32m" #CMD "\033[0m;\n\n"); ) +#define EMP_BUILD_CONCEPT__CHECK_EMPTY(A, CMD) \ + EMP_GET_ARG_2( EMP_BUILD_CONCEPT__SPACER ## A, \ + static_assert(false, \ + "\n\n \033[1;31mInvalid EMP_BUILD_CONCEPT.\033" \ + "[0m May be invalid command or missing comma in:\n" \ + " \033[1;32m" #CMD "\033[0m;\n\n" \ + ); \ + ) #define EMP_BUILD_CONCEPT__SPACER ~, /* EMPTY! */ #define EMP_BUILD_CONCEPT__ERROR @@ -153,7 +161,7 @@ #define EMP_BUILD_CONCEPT__BASE_REQUIRED_FUN(NAME, X, RETURN_T, ...) virtual RETURN_T NAME( __VA_ARGS__ ) = 0; #define EMP_BUILD_CONCEPT__BASE_OPTIONAL_FUN(NAME, X, RETURN_T, ...) virtual RETURN_T NAME( __VA_ARGS__ ) = 0; -// Since you cannot have virtual tempalated functions, we need to do a bit of work in the bast class. +// Since you cannot have virtual templated functions, we need to do a bit of work in the bast class. // ARGS are: FUNCTION_NAME, ERROR_MESSAGE, RETURN_TYPE, ARG1_TYPES, OTHER_ARGS... #define EMP_BUILD_CONCEPT__BASE_REQUIRED_OVERLOAD_FUN(NAME, X, RETURN_TYPE, ...) \ static_assert(emp::is_TypePack() == true, \ @@ -193,7 +201,7 @@ #define EMP_BUILD_CONCEPT__REQUIRED_FUN_impl(FUN_NAME, ERROR, NUM_ARGS, RETURN_T, ...) \ protected: \ /* Determine return type if we try to call this function in the base class. \ - It should be undefined if the member functon does not exist! */ \ + It should be undefined if the member function does not exist! */ \ template \ using return_t_ ## FUN_NAME = \ EMP_IF( NUM_ARGS, \ @@ -239,7 +247,7 @@ #define EMP_BUILD_CONCEPT__OPTIONAL_impl(FUN_NAME, DEFAULT, NUM_ARGS, RETURN_T, ...) \ protected: \ /* Determine return type if we try to call this function in the base class. \ - It should be undefined if the member functon does not exist! */ \ + It should be undefined if the member function does not exist! */ \ template \ using return_t_ ## FUN_NAME = \ EMP_IF( NUM_ARGS, \ @@ -276,7 +284,7 @@ #define EMP_BUILD_CONCEPT__PROCESS_REQUIRED_OVERLOAD_FUN(FUN_NAME, ERROR_MESSAGE, RETURN_T, ...) \ protected: \ /* Determine return type if we try to call this function in the base class. \ - It should be undefined if the member functon does not exist! */ \ + It should be undefined if the member function does not exist! */ \ template \ using return_t_ ## FUN_NAME = \ decltype( std::declval().FUN_NAME( std::declval() ) ); \ diff --git a/include/emp/meta/FunInfo.hpp b/include/emp/meta/FunInfo.hpp new file mode 100644 index 0000000000..a682221ad7 --- /dev/null +++ b/include/emp/meta/FunInfo.hpp @@ -0,0 +1,257 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2021 +*/ +/** + * @file + * @brief Wrap a function to provide more information about it. + * @note Status: ALPHA + * + * FunInfo will collect information about a provided function and facilitate + * manipulations. + * + * + * Developer Notes: + * - Will not currently handle return by reference! + */ + +#ifndef EMP_META_FUNINFO_HPP_INCLUDE +#define EMP_META_FUNINFO_HPP_INCLUDE + +#include + +#include "TypePack.hpp" +#include "ValPack.hpp" + +namespace emp { + + // A generic base class that expands anything with operator() + template + struct FunInfo : public FunInfo< decltype(&T::operator()) > {}; + + // Specialization for functions; redirect to function-object specialization. + template + struct FunInfo + : public FunInfo< std::function > {}; + + // Specialization for functions; redirect to function-object specialization. + template + struct FunInfo + : public FunInfo< std::function > {}; + + + // Specialization for function objects with AT LEAST ONE parameter... + template + struct FunInfo { + private: + // template struct is_templated_converter : std::false_type{}; + // template + // struct is_templated_converter().template operator()(0))>> : std::true_type{}; + + /// Helper function to lock an argument at a designated position in a function. + template + static auto BindAt_impl(CLASS_T fun, BOUND_T && bound, + TypePack, TypePack) { + // If the function needs a reference for the parameter, send the supplied value through. + if constexpr (std::is_reference()) { + return [fun, &bound](BEFORE_Ts &&... before_args, AFTER_Ts &&... after_args) { + return fun(std::forward(before_args)..., + std::forward(bound), + std::forward(after_args)...); + }; + } + // Otherwise, a copy is fine. + else { + return [fun, bound](BEFORE_Ts &&... before_args, AFTER_Ts &&... after_args) { + return fun(std::forward(before_args)..., + bound, + std::forward(after_args)...); + }; + } + } + + + public: + using fun_t = RETURN_T(PARAM1_T, PARAM_Ts...); + using return_t = RETURN_T; + using params_t = TypePack; + + template + using arg_t = typename params_t::template get; + + static constexpr size_t num_args = 1 + sizeof...(PARAM_Ts); + + /// Test if this function can be called with a particular set of arguments. + template + static constexpr bool InvocableWith(ARG1 &&, ARG_Ts &&...) { + return std::is_invocable(); + } + + /// Test if this function can be called with a particular set of argument TYPEs. + template + static constexpr bool InvocableWith() { + return std::is_invocable(); + } + + /// Change a function's return type using a converter function. + template + static auto ChangeReturnType(FUN_T fun, CONVERTER_T convert_fun) + { + return [fun=fun, c=convert_fun](PARAM1_T && arg1, PARAM_Ts &&... args) { + return c( fun(std::forward(arg1), std::forward(args)...) ); + }; + } + + /// Change a function's arguments using a fixed converter function. + template + static auto ChangeParameterTypes(FUN_T fun, CONVERTER_T convert_fun) + { + return [fun=fun, c=convert_fun](NEW_T arg1, decoy_t... args) { + return fun(c(arg1), c(args)...); + }; + } + + /// Convert a function's arguments using a dynamic (tempalted) lambda function. + template + static auto ConvertParameterTypes(FUN_T fun, CONVERTER_T convert_lambda) + { + // If the converter can take two arguments, assume the second is for type. + if constexpr ( std::is_invocable()) { + return [fun=fun, c=convert_lambda](NEW_T arg1, decoy_t... args) { + return fun(c(arg1, std::decay_t{}), + c(args, std::decay_t{})...); + }; + } + + // Otherwise assume that we are using a templated lambda (or similar object) + else { + return [fun=fun, c=convert_lambda](NEW_T arg1, decoy_t... args) { + return fun(c.template operator()(arg1), + c.template operator()(args)...); + }; + } + } + + /// Lock in a specified argument of a function. + template + static auto BindAt(CLASS_T fun, T && bound) { + using before_pack = typename params_t::template shrink; + using after_pack = typename params_t::template popN; + return BindAt_impl(fun, std::forward(bound), before_pack(), after_pack()); + } + + /// Lock in multiple function arguments. + template + static auto Bind(CLASS_T fun, T1 && bound1, Ts &&... bound) { + static_assert(emp::ValPack::IsSorted() && emp::ValPack::IsUnique(), + "FunInfo::Bind must be given unique, sorted indicies."); + static_assert(sizeof...(IDs) == sizeof...(Ts), + "FunInfo::Bind must have exactly one ID per bound value."); + + // Bind all LATER positions first, if there are any. + if constexpr (sizeof...(IDs) > 0) { + auto new_fun = Bind(fun, std::forward(bound)...); + return FunInfo::template BindAt(new_fun, bound1); + } + + // Otherwise just bind THIS position. + else return FunInfo::template BindAt(fun, bound1); + } + }; + + // Specialization for function objects with NO parameters... + template + struct FunInfo + { + using fun_t = RETURN_T(); + using return_t = RETURN_T; + using params_t = TypePack<>; + + static constexpr size_t num_args = 0; + + /// Test if this function can be called with a particular set of arguments. + template + static constexpr bool InvocableWith(ARG_Ts...) { return sizeof...(ARG_Ts) == 0; } + + /// Change a function's return type using a converter function. + template + static auto ChangeReturnType(FUN_T fun, CONVERTER_T convert_fun) + { + return [fun=fun, c=convert_fun]() { + return c(fun()); + }; + } + + /// Change a function's arguments using a converter function. + template + static auto ChangeParameterTypes(FUN_T fun, CONVERTER_T /*convert_fun*/) + { + // No parameters, so no changes to make. + return fun; + } + + /// Convert a function's arguments using a dynamic (tempalted) lambda function. + template + static auto ConvertParameterTypes(FUN_T fun, CONVERTER_T /*convert_lambda*/) + { + // No parameters, so no conversions to make. + return fun; + } + + }; + + + // === Stand-alone helper functions === + + /// Change a function's return type using a converter function. + template + static auto ChangeReturnType(FUN_T fun, CONVERTER_T convert_fun) + { + return FunInfo::ChangeReturnType(fun, convert_fun); + } + + /// Change a function's arguments using a simple converter function. + template + static auto ChangeParameterTypes(FUN_T fun, CONVERTER_T convert_fun) + { + return FunInfo::template ChangeParameterTypes(fun, convert_fun); + } + + /// Convert a function's arguments using a templated lambda. + /// @note: Will not work until C++20!! + template + static auto ConvertParameterTypes(FUN_T fun, CONVERTER_T convert_fun) + { + return FunInfo::template ConvertParameterTypes(fun, convert_fun); + } + + /// Convert both return type AND parameter type. + /// Convert a function's arguments using a templated lambda. + template + static auto ChangeTypes(FUN_T fun, R_CONVERTER_T ret_convert_fun, P_CONVERTER_T param_convert_fun) + { + auto partial = FunInfo::template ChangeParameterTypes(fun, param_convert_fun); + return FunInfo::ChangeReturnType(partial, ret_convert_fun); + } + + /// Lock in a specified argument of a function. + template + auto BindAt(FUN_T fun, BOUND_T && bound) { + return FunInfo::template BindAt(fun, std::forward(bound)); + } + + /// Lock in the first argument of a function. + template + auto BindFirst(FUN_T fun, BOUND_T && bound) { + return FunInfo::template BindAt<0>(fun, std::forward(bound)); + } + + /// Lock in a series of specified arguments to a function. + template + auto Bind(FUN_T fun, Ts &&... bound) { + return FunInfo::template Bind(fun, std::forward(bound)...); + } +} + +#endif // #ifndef EMP_META_FUNINFO_HPP_INCLUDE diff --git a/include/emp/meta/TypeID.hpp b/include/emp/meta/TypeID.hpp index 9b715cdfbd..3712921b27 100644 --- a/include/emp/meta/TypeID.hpp +++ b/include/emp/meta/TypeID.hpp @@ -1,14 +1,66 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2022 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021 - * - * @file TypeID.hpp + * @file * @brief TypeID provides an easy way to convert types to strings. * + * TypeID provides an easy way to compare types, analyze them, and convert to strings. + * All TypeID objects are consistent within a type, and are ordinal and hashable. + * + * To get the unique type information for type T use: + * TypeID t = emp::GetTypeID(); + * + * To make TypeID work more effectively with your custom class, implement the static member + * function EMPGetTypeName() which returns a string with its full name (including namespace). + * static std::string EMPGetTypeName() { return "myns::MyClass"; } + * + * MEMBER FUNCTIONS: + * + * std::string GetName() - Return a human readable (ideally) version of type's name. + * void SetName(in_name) - Set the name that should be used henceforth for this type. + * size_t GetSize() - Return number of bytes used by this type. + * + * -- TYPE TESTS -- + * bool IsAbstract() - Is this type a pure-virtual class? + * bool IsArithmetic() - Is this type numeric? + * bool IsArray() - Does this type represent a sequence of objects in memory? + * bool IsClass() - Is this type a non-union class? + * bool IsConst() - Is this contents of this type prevented from changing? + * bool IsEmpty() - Does type type have no contents? + * bool IsObject() - Is this type ANY object type? + * bool IsPointer() - Is this type a pointer? + * bool IsReference() - Is this type a reference? + * bool IsTrivial() - Is this type trivial? + * bool IsVoid() - Is this the type "void"? + * bool IsVolatile() - Is this type volatile qualified? + * bool IsTypePack() - Is this type an emp::TypePack? + * + * -- COMPARISON TESTS -- + * bool IsType() - Is this type the specified type T? + * bool IsTypeIn() - Is this type one of the listed types? + * + * -- TYPE CONVERSIONS -- + * TypeID GetDecayTypeID() - Remove all qualifications (const, reference, etc.) + * TypeID GetElementTypeID() - Return type that makes up this type (i.e. for arrays) + * TypeID GetRemoveConstTypeID() - Remove const-ness of this type, if any. + * TypeID GetRemoveCVTypeID() - Remove constness and volatility of this type. + * TypeID GetRemoveExtentTypeID() - Flatten one level of a multi-dimensional array. + * TypeID GetRemoveAllExtentsTypeID() - Flatten multi-dimensional arrays. + * TypeID GetRemovePointerTypeID() - If this is a pointer, change to type pointed to. + * TypeID GetRemoveReferenceTypeID() - If this is a reference, change to type referred to. + * TypeID GetRemoveVolatileTypeID() - Remove volatility of this type, if any + * + * -- VALUE CONVERSIONS -- + * double ToDouble(pointer) - Convert pointed-to object (of this type) to a double. + * std::string ToString(pointer) - Convert pointed-to object (of this type) to a std::string. + * bool FromDouble(value, pointer) - Use double value to set pointed-to object (of this type) + * bool FromString(string, pointer) - Use string value to set pointed-to object (of this type) + * * Developer notes: * * Fill out defaults for remaining standard library classes (as possible) - * * If a class has a static TypeID_GetName() defined, use that for the name. * * If a type is a template, give access to parameter types. * * If a type is a function, give access to parameter types. */ @@ -17,7 +69,9 @@ #define EMP_META_TYPEID_HPP_INCLUDE #include +#include #include +#include #include #include "../base/Ptr.hpp" @@ -68,6 +122,7 @@ namespace emp { virtual bool IsTrivial() const { return false; } virtual bool IsVoid() const { return false; } virtual bool IsVolatile() const { return false; } + virtual bool IsFunction() const { return false; } virtual bool IsTypePack() const { return false; } @@ -119,6 +174,7 @@ namespace emp { bool IsTrivial() const override { return std::is_trivial(); } bool IsVoid() const override { return std::is_same(); } bool IsVolatile() const override { return std::is_volatile(); } + bool IsFunction() const override { return std::is_function(); } bool IsTypePack() const override { return emp::is_TypePack(); } @@ -170,6 +226,7 @@ namespace emp { size_t GetSize() const override { if constexpr (std::is_void()) return 0; + else if constexpr (std::is_function()) return 0; else return sizeof(T); } @@ -181,9 +238,9 @@ namespace emp { return ptr.ReinterpretCast()->ToDouble(); } - // If this type is convertable to a double, cast the pointer to the correct type, de-reference it, + // If this type is convertible to a double, cast the pointer to the correct type, de-reference it, // and then return the conversion. Otherwise return NaN - if constexpr (std::is_convertible::value) { + else if constexpr (std::is_convertible::value) { return (double) *ptr.ReinterpretCast(); } else return std::nan(""); @@ -220,7 +277,8 @@ namespace emp { return "[N/A]"; } - bool FromDouble(double value, const emp::Ptr ptr) const override { + bool FromDouble([[maybe_unused]] double value, + [[maybe_unused]] const emp::Ptr ptr) const override { using base_t = std::decay_t; // If this variable has a built-in FromDouble() trait, use it! @@ -228,7 +286,7 @@ namespace emp { return ptr.ReinterpretCast()->FromDouble(value); } - // If this type is convertable to a double, cast the pointer to the correct type, de-reference it, + // If this type is convertible to a double, cast the pointer to the correct type, de-reference it, // and then return the conversion. Otherwise return NaN if constexpr (std::is_convertible::value) { *ptr.ReinterpretCast() = (base_t) value; @@ -384,6 +442,11 @@ namespace emp { return internal::TypePackIDs_impl::GetIDs(); } + // Determine if a type has a static EMPGetTypeName() member function. + template struct HasEMPGetTypeName : std::false_type { }; + template + struct HasEMPGetTypeName> : std::true_type{}; + /// Build the information for a single TypeID. template static emp::Ptr BuildInfo() { @@ -392,7 +455,12 @@ namespace emp { TypeID type_id(&info); info.init = true; - info.name = typeid(T).name(); + + if constexpr (HasEMPGetTypeName()) { + info.name = T::EMPGetTypeName(); + } else { + info.name = typeid(T).name(); + } // Now, fix the name if we can be more precise about it. if constexpr (std::is_const()) { @@ -435,10 +503,16 @@ namespace emp { /// Setup a bunch of standard type names to be more readable. void SetupTypeNames() { - - // Built-in types. GetTypeID().SetName("void"); + // Probably replaced later, but good to have for systems where it's not. + GetTypeID().SetName("size_t"); + GetTypeID().SetName("long"); + GetTypeID().SetName("long long"); + GetTypeID().SetName("unsigned long"); + GetTypeID().SetName("unsigned long long"); + + // Main built-in types. GetTypeID().SetName("bool"); GetTypeID().SetName("double"); GetTypeID().SetName("float"); @@ -463,7 +537,7 @@ namespace emp { } } - +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace std { /// Hash function to allow TypeID to be used with maps and sets (must be in std). template <> @@ -479,5 +553,6 @@ namespace std { return out; } } +#endif // DOXYGEN_SHOULD_SKIP_THIS #endif // #ifndef EMP_META_TYPEID_HPP_INCLUDE diff --git a/include/emp/meta/TypePack.hpp b/include/emp/meta/TypePack.hpp index 74f38a8647..c5dcef61a4 100644 --- a/include/emp/meta/TypePack.hpp +++ b/include/emp/meta/TypePack.hpp @@ -1,15 +1,16 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021. - * - * @file TypePack.hpp + * @file * @brief A set of types that can be manipulated at compile time (good for metaprogramming) * - * TypePacks are static structues that provide a large set of mechanisms to access and adjust + * TypePacks are static structures that provide a large set of mechanisms to access and adjust * the included types. * - * To create a typepack, just pass in zero or more types into the TypePack template. + * To create a TypePack, just pass in zero or more types into the TypePack template. * * using my_pack = emp::TypePack; * @@ -69,6 +70,8 @@ #ifndef EMP_META_TYPEPACK_HPP_INCLUDE #define EMP_META_TYPEPACK_HPP_INCLUDE +#include + #include "meta.hpp" namespace emp { @@ -291,7 +294,7 @@ namespace emp { /// Rearrange types in TypePack into reverse order. using reverse = typename pop::reverse::template push_back; - /// Rotate types through typepack by the specified number of steps. + /// Rotate types through TypePack by the specified number of steps. using rotate = typename pop::template push_back; /// Set the type at the specified position to the new type provided. Return as new TypePack. diff --git a/include/emp/meta/ValPack.hpp b/include/emp/meta/ValPack.hpp index 3fbcf45024..060e1be0c5 100644 --- a/include/emp/meta/ValPack.hpp +++ b/include/emp/meta/ValPack.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2021. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021. - * - * @file ValPack.hpp + * @file * @brief A set of values that can be manipulated at compile time (good for metaprogramming) * * Any built-in type can be added to ValPack to be manipulated at compile time. @@ -12,11 +13,12 @@ #ifndef EMP_META_VALPACK_HPP_INCLUDE #define EMP_META_VALPACK_HPP_INCLUDE +#include +#include +#include #include "meta.hpp" -#include -#include namespace emp { @@ -26,11 +28,13 @@ namespace emp { // Anonymous implementations of ValPack interface. #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace internal { + // Helper. DONE arg starts as true, but set to false when sequence finished. template struct vp_range { static constexpr auto NEXT = START + STEP; using type = typename vp_range<(NEXT >= END), NEXT, END, STEP, VALS..., START>::type; }; + // Specialization for when DONE is true. template struct vp_range { using type = ValPack; @@ -200,6 +204,9 @@ namespace emp { /// Find the overall maximum value in an ValPack. constexpr static auto Max() { return pop::Max(V1); } + /// Determine if the pack is sorted. + constexpr static bool IsSorted() { return V1 <= Min() && pop::IsSorted(); } + /// Use each value in an ValPack as an index and return results as a tuple. template constexpr static auto ApplyIndex(T && container) { @@ -251,6 +258,8 @@ namespace emp { template constexpr static auto Max(T floor) { return floor; } + constexpr static bool IsSorted() { return true; } + static std::string ToString() { return ""; } static void PrintVals(std::ostream & /* os */=std::cout) { ; } diff --git a/include/emp/meta/macro_math.hpp b/include/emp/meta/macro_math.hpp index 59ca7f3989..3d43e822be 100644 --- a/include/emp/meta/macro_math.hpp +++ b/include/emp/meta/macro_math.hpp @@ -1,11 +1,12 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file macro_math.hpp + * @file * @brief Macros to build a pre-processor calculator system. - * @note Status: RELEASE + * Status: RELEASE * * Working macros include: * EMP_INC(A) : converts to result of A+1 diff --git a/include/emp/meta/macros.hpp b/include/emp/meta/macros.hpp index 0504b4632c..6dd2876dc5 100644 --- a/include/emp/meta/macros.hpp +++ b/include/emp/meta/macros.hpp @@ -1,11 +1,12 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file macros.hpp + * @file * @brief Generally useful macros that can perform cools tricks. - * @note Status: RELEASE + * Status: RELEASE * * * Generally useful macros that can perform cools tricks. As with all macros, use only @@ -89,7 +90,7 @@ /// The below values allow you to have EMP_FAKE_ARG or EMP_FAKE_2ARG as a single argument. /// If you prepend it with EMP_CONVERT it will trigger a conversion. If you prepend anything -/// else similarly, it wil NOT triggera a conversion (and stay a single argument) +/// else similarly, it wil NOT trigger a conversion (and stay a single argument) #define EMP_CONVERT_ARG_EMP_FAKE_ARG(A) A #define EMP_CONVERT_ARG_EMP_FAKE_2ARG(A) ~, A @@ -203,7 +204,7 @@ /// @cond MACROS // S = Size of each pack // N = Number of packs -// P = Pack representatio of number of packs +// P = Pack representation of number of packs #define EMP_ARGS_TO_PACKS_impl(S, N, ...) EMP_ARGS_TO_PACKS_implB(S, EMP_DEC_TO_PACK(N), __VA_ARGS) #define EMP_ARGS_TO_PACKS_implB(S, P, ...) @CAO @@ -422,7 +423,7 @@ /// @endcond -// @cond MACROS +/// @cond MACROS // ********************** @@ -441,7 +442,6 @@ #define EMP_INTERNAL_CALL_BY_PACKS(C, F, ...) \ EMP_INTERNAL_CALL_BY_PACKS_impl(C, F, EMP_DEC_TO_PACK(EMP_COUNT_ARGS(__VA_ARGS__)), __VA_ARGS__, ~) -/// @cond MACROS // Internal helpers... // P is the pack of call counts the still need to be done diff --git a/include/emp/meta/meta.hpp b/include/emp/meta/meta.hpp index af32c39d37..ccd23cc122 100644 --- a/include/emp/meta/meta.hpp +++ b/include/emp/meta/meta.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2021 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021 - * - * @file meta.hpp + * @file * @brief A bunch of C++ Template Meta-programming tricks. * * Developer notes: @@ -16,6 +17,7 @@ #define EMP_META_META_HPP_INCLUDE #include +#include #include #include @@ -29,10 +31,59 @@ namespace emp { /// Effectively create a function (via constructor) where all args are computed, then ignored. struct run_and_ignore { template run_and_ignore(T&&...) {} }; + template struct type_index; + + template <> struct type_index<> { + using t1 = void; using t2 = void; using t3 = void; using t4 = void; + }; + + template struct type_index { + using t1 = T1; using t2 = void; using t3 = void; using t4 = void; + }; + + template struct type_index { + using t1 = T1; using t2 = T2; using t3 = void; using t4 = void; + }; + + template struct type_index { + using t1 = T1; using t2 = T2; using t3 = T3; using t4 = void; + }; + + template + struct type_index { + using t1 = T1; using t2 = T2; using t3 = T3; using t4 = T4; + }; + + /// Trim off a specific type position from a pack. - template using first_type = T1; - template using second_type = T2; - template using third_type = T3; + template using first_type = typename type_index::t1; + template using second_type = typename type_index::t2; + template using third_type = typename type_index::t3; + template using fourth_type = typename type_index::t4; + + // Index into a template parameter pack to grab a specific type. + #ifndef DOXYGEN_SHOULD_SKIP_THIS + namespace internal { + template + struct pack_id_impl { using type = typename pack_id_impl::type; }; + + template struct pack_id_impl<0,T,Ts...> { using type = T; }; + } + #endif // DOXYGEN_SHOULD_SKIP_THIS + + /// Pick a specific position from a type pack. + template + using pack_id = typename internal::pack_id_impl::type; + + /// Trim off the last type from a pack. + template using last_type = pack_id; + + /// A struct declaration with no definition to show a type name in a compile time error. + template struct ShowType; + + /// A false type that does NOT resolve in unexecuted if-constexpr branches. + /// By Brian Bi; from: https://stackoverflow.com/questions/69501472/best-way-to-trigger-a-compile-time-error-if-no-if-constexprs-succeed + template struct dependent_false : std::false_type {}; /// Create a placeholder template to substitute for a real type. template struct PlaceholderType; @@ -76,23 +127,6 @@ namespace emp { return out_v; } - // Index into a template parameter pack to grab a specific type. - #ifndef DOXYGEN_SHOULD_SKIP_THIS - namespace internal { - template - struct pack_id_impl { using type = typename pack_id_impl::type; }; - - template - struct pack_id_impl<0,T,Ts...> { using type = T; }; - } - - template - using pack_id = typename internal::pack_id_impl::type; - #endif // DOXYGEN_SHOULD_SKIP_THIS - - // Trim off the last type from a pack. - template using last_type = pack_id; - // Trick to call a function using each entry in a parameter pack. #define EMP_EXPAND_PPACK(PPACK) ::emp::run_and_ignore{ 0, ((PPACK), void(), 0)... } @@ -295,6 +329,8 @@ namespace emp { static constexpr int Product() { return I; } }; + #ifndef DOXYGEN_SHOULD_SKIP_THIS + //This bit of magic is from //http://meh.schizofreni.co/programming/magic/2013/01/23/function-pointer-from-lambda.html //and is useful for fixing lambda function woes @@ -324,6 +360,8 @@ namespace emp { return static_cast::function>(lambda); } + #endif /*DOXYGEN_SHOULD_SKIP_THIS*/ + /// Determine the size of a built-in array. template constexpr size_t GetSize(T (&)[N]) { return N; } diff --git a/include/emp/meta/reflection.hpp b/include/emp/meta/reflection.hpp index 2ed2a0d991..a51d5c4784 100644 --- a/include/emp/meta/reflection.hpp +++ b/include/emp/meta/reflection.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2018 +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2018 - * - * @file reflection.hpp + * @file * @brief Macros and template utilities to help determine details about unknown classes. */ diff --git a/include/emp/meta/type_traits.hpp b/include/emp/meta/type_traits.hpp index 6d212abbf9..4ea71db94a 100644 --- a/include/emp/meta/type_traits.hpp +++ b/include/emp/meta/type_traits.hpp @@ -1,9 +1,10 @@ +/* + * This file is part of Empirical, https://github.com/devosoft/Empirical + * Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * date: 2016-2022. +*/ /** - * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2016-2021. - * - * @file type_traits.hpp + * @file * @brief Extensions on the standard library type traits to handle Empirical classes (such as Ptr). */ @@ -11,7 +12,10 @@ #define EMP_META_TYPE_TRAITS_HPP_INCLUDE +#include // uint8_t, uint16_t, etc. #include +#include +#include #include #include #include @@ -22,6 +26,7 @@ #include "meta.hpp" + namespace emp { // Predeclarations used below. @@ -75,6 +80,7 @@ namespace emp { template struct HasToDouble().ToDouble())>> : std::true_type{}; + #ifndef DOXYGEN_SHOULD_SKIP_THIS // Determine if a type has a FromString() member function. template struct HasFromString : std::false_type { }; template @@ -84,6 +90,7 @@ namespace emp { template struct HasFromDouble : std::false_type { }; template struct HasFromDouble().FromDouble(0.0))>> : std::true_type{}; + #endif // DOXYGEN_SHOULD_SKIP_THIS /// Determine if a type passed in is an std::function type (vs a lambda or a raw function) template struct is_std_function : std::false_type { }; @@ -94,7 +101,7 @@ namespace emp { template struct remove_std_function_type> { using type = T; }; template using remove_std_function_t = typename remove_std_function_type::type; - // Collect the reference type for any container. + // Collect the reference type for any standard container. template struct element_type { using type = T; }; template

Word" + << "ExpectedWords" + << "MaximumWords" + << "Information
" << word.word << "" + << "" << word.ave_options + << "" << word.max_options + << "" << word.entropy + << "