diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 267e5bd584..0abbb86079 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -13,6 +13,7 @@ build: jobs: pre_build: - doxygen + - cd doc && python make_md.py # Build documentation in the docs/ directory with Sphinx sphinx: diff --git a/Doxyfile b/Doxyfile index 94c6d2809d..7b9507c5f0 100644 --- a/Doxyfile +++ b/Doxyfile @@ -155,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 @@ -380,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. @@ -540,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 @@ -588,7 +588,7 @@ HIDE_UNDOC_CLASSES = NO # 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 @@ -691,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 @@ -921,7 +921,7 @@ WARN_LINE_FORMAT = "at line $line of file $file" # 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 @@ -1020,7 +1020,12 @@ EXCLUDE_PATTERNS = */demos/* \ # ANamespace::AClass, ANamespace::*Test EXCLUDE_SYMBOLS = EMP_* \ - EM_* + EM_* \ + internal \ + detail \ + decltypedetail \ + __* + # 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 @@ -1072,7 +1077,7 @@ IMAGE_PATH = # 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 @@ -2535,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. diff --git a/Makefile b/Makefile index 4659e1e347..c2c8a002f4 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,7 @@ test-cookiecutter: ../cookiecutter-empirical-project doc: doxygen + cd doc && python make_md.py cd doc && make html coverage coverage: 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/conf.py b/doc/conf.py index 000d309aab..84ae412a22 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -41,6 +41,7 @@ 'sphinx_rtd_theme', 'breathe', 'myst_parser', + 'sphinx_tippy' ] myst_heading_anchors = 4 diff --git a/doc/filter_namespace b/doc/filter_namespace new file mode 100755 index 0000000000..d4c528fd7a --- /dev/null +++ b/doc/filter_namespace @@ -0,0 +1,28 @@ +#!/usr/bin/python3 + +import sys + +with open(sys.argv[1]) as infile: + open_braces = 0 + namespace_removed = 0 + 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 {"): + namespace_removed += 1 + open_braces = 1 + print() + elif namespace_removed and line == "}": + print() + namespace_removed -= 1 + else: + if namespace_removed: + open_braces += line.count("{") + open_braces -= line.count("}") + line = line.replace("emp::", "") + line = line.replace("web::", "") + line = line.replace("prefab::", "") + print(line) \ No newline at end of file diff --git a/doc/library/base/base.md b/doc/library/base/base.md index a3e9deb0b9..840bf37cef 100644 --- a/doc/library/base/base.md +++ b/doc/library/base/base.md @@ -39,13 +39,12 @@ emp::array array({1,2,3}); ## 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: @@ -59,20 +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: + +```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 @@ -95,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 4106c4ad65..daf71df77a 100644 --- a/doc/library/bits/bits.md +++ b/doc/library/bits/bits.md @@ -1,36 +1,14 @@ # Bits -## BitMatrix +Stuff about bits -```{eval-rst} -.. doxygenfile:: emp/bits/BitMatrix.hpp - :project: Empirical -``` - -## BitArray +## API + + ```{eval-rst} -.. doxygenfile:: emp/bits/BitArray.hpp - :project: Empirical -``` +.. toctree:: + :glob: -## BitSet - -```{eval-rst} -.. doxygenfile:: emp/bits/BitSet.hpp - :project: Empirical + api/* ``` - -## BitSet Utilities - -```{eval-rst} -.. doxygenfile:: emp/bits/bitset_utils.hpp - :project: Empirical -``` - -## BitVector - -```{eval-rst} -.. doxygenfile:: emp/bits/BitVector.hpp - :project: Empirical -``` diff --git a/doc/library/compiler/compiler.md b/doc/library/compiler/compiler.md index f36e7a177e..935ae6f615 100644 --- a/doc/library/compiler/compiler.md +++ b/doc/library/compiler/compiler.md @@ -1,42 +1,12 @@ # Compiler -## Deterministic Finite Automata +## API + + ```{eval-rst} -.. doxygenfile:: emp/compiler/DFA.hpp -``` +.. toctree:: + :glob: -## Lexer Utilities - -```{eval-rst} -.. doxygenfile:: emp/compiler/lexer_utils.hpp -``` - -## Lexer - -```{eval-rst} -.. doxygenfile:: emp/compiler/Lexer.hpp -``` - -## Non-Deterministic Finite Automata - - - - + api/* +``` \ No newline at end of file diff --git a/doc/library/data/data.md b/doc/library/data/data.md index cca68494a8..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 0a13ca9580..ccdf664412 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: -``` +.. toctree:: + :glob: -## 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 - -```{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/* +``` \ No newline at end of file diff --git a/doc/library/debug/debug.md b/doc/library/debug/debug.md index 85ee6fb6d0..86f1310c6a 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: -``` +.. toctree:: + :glob: -## Debugging Tools - -```{eval-rst} -.. doxygenfile:: emp/debug/debug.hpp - :project: Empirical - :no-link: -``` - -## Errors - -```{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/* +``` \ No newline at end of file diff --git a/doc/library/evolve/_systematics_docs.md b/doc/library/evolve/_systematics_docs.md deleted file mode 100644 index 354f5096c2..0000000000 --- a/doc/library/evolve/_systematics_docs.md +++ /dev/null @@ -1,252 +0,0 @@ - -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) -- Sackin index -- Phylogenetic diversity - -#### 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 `remove_before` 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 `add_snapshot_fun` 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` keyword 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` keyword 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` keyword argument in the constructor to true. - -The second decision is slightly trickier. Once you start adding organisms to the systematics manager, it will create `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 `self.taxon` attribute 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` keyword 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 `add_org` 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 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 `remove_org` 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 remove_org -// Note that remove_org takes the taxon of the dead organism as an argument, not -// the organism itself -sys.remove_org(taxon) - -``` - -#### Taxon properties - -Taxon objects maintain the following information: - -- 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()`` - -#### Systematics manager properties - -A systematics manager object maintains the following information: - -- 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. - -#### Phylogeny metrics - -Many different metrics can be used to quantify th topology of a phylogeny. For more information, see (Winters et al., 2013; Tucker et al. 2017). - -The Empirical systematics manager can calculate - -- Phylogenetic diversity (Faith, 1992) -- Taxon Distinctiveness (From Vane-Wright et al., 1991) -- Evolutionary Distinctiveness (Isaac, 2007) (mean, sum, and variance) -- Mean pairwise distance (Webb and Losos, 2000), which is equivalent to Average Taxonomic Diversity (Warwick and Clark, 1998, Tucker et al., 2016) -- Sum pairwise distance -- Variance pairwise distance -- Out-degree distribution -- Average origination time -- Colless-like Index (Mir, 2018, PLoS One) -- Sackin Index (Sackin, 1972; reviewed in Shao, 1990) -- Depth of most recent common ancestor -- Phenotypic volatility (Dolson et al., 2019) -- Unique taxa on lineage (Dolson et al., 2019) -- Mutation count along lineage (Dolson et al., 2019) -- Tree size -- Maximum depth diff --git a/doc/library/evolve/evolve.md b/doc/library/evolve/evolve.md index c3bb4a65df..1f95028d02 100644 --- a/doc/library/evolve/evolve.md +++ b/doc/library/evolve/evolve.md @@ -1,36 +1,266 @@ # Evolution tools -## World +## Phylotracklib (Systematics Manager) -```{eval-rst} -.. doxygenfile:: Evolve/World.hpp - :project: Empirical - :no-link: +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;}); ``` -## Phylotracklib (Systematics Manager) +#### 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: -```{include} systematics_docs.md +- Mean/max/min/sum/variance pairwise distance +- Colless-like index (a variant of the Colless index adjusted for trees with multifurcations) +- Sackin index +- Phylogenetic diversity + +#### 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 `remove_before` 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 `add_snapshot_fun` 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 ``` -```{eval-rst} -.. doxygenfile:: Evolve/Systematics.hpp - :project: Empirical - :no-link: +Then in your C++ file: + +```cpp +#include "Evolve/Systematics.hpp" ``` -## NK +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): -```{eval-rst} -.. doxygenfile:: Evolve/NK.hpp - :project: Empirical - :no-link: +```bash +g++ -IEmpirical/include/emp my_source_file.cc ``` -## Selection +#### 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` keyword 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` keyword 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` keyword argument in the constructor to true. + +The second decision is slightly trickier. Once you start adding organisms to the systematics manager, it will create `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 `self.taxon` attribute 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` keyword 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 `add_org` 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 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 `remove_org` 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 remove_org +// Note that remove_org takes the taxon of the dead organism as an argument, not +// the organism itself +sys.remove_org(taxon) + +``` + +### Taxon properties + +Taxon objects maintain the following information: + +- 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()`` + +### Systematics manager properties + +A systematics manager object maintains the following information: + +- 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. + +### Phylogeny metrics + +Many different metrics can be used to quantify th topology of a phylogeny. For more information, see (Winters et al., 2013; Tucker et al. 2017). + +The Empirical systematics manager can calculate + +- Phylogenetic diversity (Faith, 1992) +- Taxon Distinctiveness (From Vane-Wright et al., 1991) +- Evolutionary Distinctiveness (Isaac, 2007) (mean, sum, and variance) +- Mean pairwise distance (Webb and Losos, 2000), which is equivalent to Average Taxonomic Diversity (Warwick and Clark, 1998, Tucker et al., 2016) +- Sum pairwise distance +- Variance pairwise distance +- Out-degree distribution +- Average origination time +- Colless-like Index (Mir, 2018, PLoS One) +- Sackin Index (Sackin, 1972; reviewed in Shao, 1990) +- Depth of most recent common ancestor +- Phenotypic volatility (Dolson et al., 2019) +- Unique taxa on lineage (Dolson et al., 2019) +- Mutation count along lineage (Dolson et al., 2019) +- Tree size +- Maximum depth + +## API + + + ```{eval-rst} -.. doxygenfile:: Evolve/World_select.hpp - :project: Empirical - :no-link: +.. toctree:: + :glob: + + api/* ``` diff --git a/doc/library/functional/functional.md b/doc/library/functional/functional.md index bd0d64ccec..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:: functional/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/GenericFunction.hpp - :project: Empirical - :no-link: -``` - -## Memoized Functions - -```{eval-rst} -.. doxygenfile:: emp/functional/memo_function.hpp - :project: Empirical - :no-link: + api/* ``` diff --git a/doc/library/index.md b/doc/library/index.md index 3f9c52606b..b6002c88cf 100644 --- a/doc/library/index.md +++ b/doc/library/index.md @@ -3,22 +3,10 @@ Contents: ```{toctree} -:maxdepth: 1 +:maxdepth: 4 +:glob: -base/base -bits/bits -compiler/compiler -data/data -datastructs/datastructs -debug/debug -evolve/evolve -functional/functional -io/io -math/math -prefab/prefab -testing/testing -tools/tools -web/web +*/[!_]* ``` - {ref}`genindex` diff --git a/doc/library/io/io.md b/doc/library/io/io.md index 5a43b32d62..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 035f0bb2c5..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 286b527229..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 b1417a70c4..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 2de8318c86..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 100644 index 0000000000..558684a627 --- /dev/null +++ b/doc/make_md.py @@ -0,0 +1,53 @@ +import os +import glob + + +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" + + +header_files = glob.glob("../include/emp/**/*.hpp") + +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] + if just_file.startswith("_"): + continue + if just_dir.startswith("in_progress"): + continue + if just_dir.startswith("polyfill"): + continue + new_dir = "library/" + just_dir + "/api" + print(new_dir) + toc_text = format_heading(2, "API") + "```{eval-rst}\n.. toctree::\n :glob:\n\n api/*\n```\n" + if not os.path.exists(new_dir): + os.makedirs(new_dir) + 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) + with open(toc_file_name, "w") as target: + target.write(toc_text) + # elif toc_text 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 b828b68d43..799c4c954f 100644 --- a/doc/requirements.in +++ b/doc/requirements.in @@ -1,6 +1,7 @@ sphinx exhale -sphinx-rtd-theme +sphinx-rtd-theme>=1.0.0 coverxygen breathe myst-parser +sphinx-tippy diff --git a/doc/requirements.txt b/doc/requirements.txt index 688edaba1f..01466d8bf9 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -2,14 +2,16 @@ # This file is autogenerated by pip-compile with Python 3.9 # by the following command: # -# pip-compile --resolver=backtracking +# pip-compile # alabaster==0.7.13 # via sphinx babel==2.12.1 # via sphinx beautifulsoup4==4.12.2 - # via exhale + # via + # exhale + # sphinx-tippy breathe==4.35.0 # via # -r requirements.in @@ -20,11 +22,12 @@ charset-normalizer==3.1.0 # via requests coverxygen==1.7.0 # via -r requirements.in -docutils==0.19 +docutils==0.18.1 # via # breathe # myst-parser # sphinx + # sphinx-rtd-theme exhale==0.2.4 # via -r requirements.in idna==3.4 @@ -37,6 +40,7 @@ jinja2==3.1.2 # via # myst-parser # sphinx + # sphinx-tippy lxml==4.9.2 # via exhale markdown-it-py==2.2.0 @@ -58,7 +62,9 @@ pygments==2.15.1 pyyaml==6.0 # via myst-parser requests==2.31.0 - # via sphinx + # via + # sphinx + # sphinx-tippy six==1.16.0 # via exhale snowballstemmer==2.2.0 @@ -72,7 +78,11 @@ sphinx==6.2.1 # exhale # myst-parser # sphinx-rtd-theme -sphinx-rtd-theme==0.5.1 + # sphinx-tippy + # sphinxcontrib-jquery +sphinx-rtd-theme==1.2.2 + # via -r requirements.in +sphinx-tippy==0.4.1 # via -r requirements.in sphinxcontrib-applehelp==1.0.4 # via sphinx @@ -80,6 +90,8 @@ sphinxcontrib-devhelp==1.0.2 # via sphinx sphinxcontrib-htmlhelp==2.0.1 # via sphinx +sphinxcontrib-jquery==4.1 + # via sphinx-rtd-theme sphinxcontrib-jsmath==1.0.1 # via sphinx sphinxcontrib-qthelp==1.0.3 diff --git a/include/emp/Evolve/Systematics.hpp b/include/emp/Evolve/Systematics.hpp index 32edb585e4..471428f2cd 100644 --- a/include/emp/Evolve/Systematics.hpp +++ b/include/emp/Evolve/Systematics.hpp @@ -1408,6 +1408,7 @@ namespace emp { }; + #ifndef DOXYGEN_SHOULD_SKIP_THIS // ============================================================= // === === // === Out-of-class member function definitions from above === @@ -1835,6 +1836,8 @@ namespace emp { double Systematics::CalcDiversity() const { return emp::Entropy(active_taxa, [](Ptr x){ return x->GetNumOrgs(); }, (double) org_count); } + + #endif // DOXYGEN_SHOULD_SKIP_THIS } #endif // #ifndef EMP_EVOLVE_SYSTEMATICS_HPP_INCLUDE diff --git a/include/emp/Evolve/World.hpp b/include/emp/Evolve/World.hpp index 54f2b9c1f8..f4a243fe81 100644 --- a/include/emp/Evolve/World.hpp +++ b/include/emp/Evolve/World.hpp @@ -944,6 +944,7 @@ namespace emp { }; + #ifndef DOXYGEN_SHOULD_SKIP_THIS // ============================================================= // === === // === Out-of-class member function definitions from above === @@ -1735,7 +1736,7 @@ namespace emp { os << std::endl; } } - + #endif // DOXYGEN_SHOULD_SKIP_THIS } #endif // #ifndef EMP_EVOLVE_WORLD_HPP_INCLUDE diff --git a/include/emp/base/MapProxy.hpp b/include/emp/base/MapProxy.hpp index 09ab236757..446a28ac15 100644 --- a/include/emp/base/MapProxy.hpp +++ b/include/emp/base/MapProxy.hpp @@ -108,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 { }; @@ -116,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> @@ -159,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) { @@ -171,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 9ed0e0f14e..f479cd8287 100644 --- a/include/emp/base/Ptr.hpp +++ b/include/emp/base/Ptr.hpp @@ -180,7 +180,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() @@ -216,7 +216,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. @@ -271,7 +271,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; @@ -286,14 +286,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]; @@ -437,7 +437,7 @@ 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) { @@ -592,7 +592,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; @@ -603,7 +603,7 @@ 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(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!)"); diff --git a/include/emp/base/always_assert.hpp b/include/emp/base/always_assert.hpp index fe06b461e3..e5ec365210 100644 --- a/include/emp/base/always_assert.hpp +++ b/include/emp/base/always_assert.hpp @@ -22,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 3fd557b1c9..2e24bb4ff2 100644 --- a/include/emp/base/always_assert_warning.hpp +++ b/include/emp/base/always_assert_warning.hpp @@ -21,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 a52c71d579..feca6fa706 100644 --- a/include/emp/base/array.hpp +++ b/include/emp/base/array.hpp @@ -204,7 +204,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 @@ -218,7 +218,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/emscripten_assert.hpp b/include/emp/base/emscripten_assert.hpp index 62fad7cf5b..6f985b42ae 100644 --- a/include/emp/base/emscripten_assert.hpp +++ b/include/emp/base/emscripten_assert.hpp @@ -20,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/optional.hpp b/include/emp/base/optional.hpp index 565890b098..ca12f6d58d 100644 --- a/include/emp/base/optional.hpp +++ b/include/emp/base/optional.hpp @@ -100,6 +100,7 @@ namespace emp { } // namespace emp +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace std { template @@ -110,7 +111,7 @@ namespace std { }; } // namespace std - +#endif // DOXYGEN_SHOULD_SKIP_THIS #endif #endif // #ifndef EMP_BASE_OPTIONAL_HPP_INCLUDE diff --git a/include/emp/bits/BitArray.hpp b/include/emp/bits/BitArray.hpp index aaa9d54447..d1759b4303 100644 --- a/include/emp/bits/BitArray.hpp +++ b/include/emp/bits/BitArray.hpp @@ -43,8 +43,10 @@ namespace emp { 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; @@ -66,7 +68,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. @@ -122,10 +124,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 @@ -138,7 +140,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. @@ -168,7 +170,7 @@ namespace emp { /// Destructor. ~BitArray() = default; - /// Assignment operator (no separate move opperator since no resources to move...) + /// 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. @@ -188,7 +190,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? @@ -291,12 +293,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; @@ -315,7 +319,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. @@ -429,7 +433,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. @@ -440,7 +444,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. @@ -450,7 +454,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. @@ -645,10 +649,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(); } @@ -685,7 +689,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. @@ -721,7 +725,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. @@ -761,7 +765,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. @@ -801,7 +805,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; @@ -813,7 +817,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 )); @@ -824,7 +828,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 @@ -885,7 +889,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) { @@ -899,7 +903,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 )); @@ -910,7 +914,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; @@ -960,14 +964,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) { @@ -995,7 +999,7 @@ namespace emp { } } - /// Assignment operator from a std::bitset. + // Assignment operator from a std::bitset. template BitArray & BitArray::operator=(const std::bitset & bitset) { @@ -1003,7 +1007,7 @@ namespace emp { 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) { @@ -1019,7 +1023,7 @@ namespace emp { } - /// Assign from a BitArray of a different size. + // Assign from a BitArray of a different size. template template BitArray & BitArray::Import( @@ -1065,7 +1069,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 { @@ -1075,7 +1079,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. @@ -1096,7 +1100,7 @@ namespace emp { 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); @@ -1110,7 +1114,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; @@ -1119,7 +1123,7 @@ namespace emp { } - /// Flip a single bit + // Flip a single bit template BitArray & BitArray::Toggle(size_t index) { emp_assert(index >= 0 && index < NUM_BITS); @@ -1136,7 +1140,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); @@ -1144,7 +1148,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, @@ -1157,7 +1161,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) { @@ -1169,7 +1173,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( @@ -1232,7 +1236,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, @@ -1249,7 +1253,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, @@ -1265,7 +1269,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, @@ -1281,7 +1285,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) @@ -1291,7 +1295,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) @@ -1301,7 +1305,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) @@ -1314,7 +1318,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 { @@ -1326,7 +1330,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 { @@ -1342,7 +1346,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); @@ -1352,8 +1356,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( @@ -1363,7 +1367,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); @@ -1373,8 +1377,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. @@ -1399,7 +1403,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 { @@ -1416,7 +1420,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) { @@ -1430,7 +1434,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 { @@ -1444,7 +1448,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 @@ -1465,10 +1469,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); } @@ -1488,7 +1492,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; @@ -1501,7 +1505,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; @@ -1514,7 +1518,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; @@ -1523,7 +1527,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. @@ -1543,7 +1547,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. @@ -1570,7 +1574,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(); @@ -1578,7 +1582,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. @@ -1590,7 +1594,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; @@ -1605,14 +1609,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; @@ -1621,7 +1625,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; @@ -1630,7 +1634,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; @@ -1638,7 +1642,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 @@ -1648,7 +1652,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--) { @@ -1657,7 +1661,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++) { @@ -1673,7 +1677,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; @@ -1686,7 +1690,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, @@ -1710,7 +1714,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]; @@ -1718,21 +1722,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]); @@ -1740,7 +1744,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]); @@ -1748,14 +1752,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]); @@ -1763,8 +1767,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); @@ -1773,8 +1777,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); @@ -1782,7 +1786,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() { @@ -1808,7 +1812,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); @@ -1816,8 +1820,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); @@ -1826,8 +1830,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); @@ -1835,7 +1839,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() { @@ -1847,7 +1851,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 )); @@ -1916,7 +1920,7 @@ namespace emp { } - /// Helper for calling ROTATE with positive number + // Helper for calling ROTATE with positive number template template BitArray & BitArray::ROTR_SELF() { @@ -1929,7 +1933,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 )); @@ -1985,18 +1989,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; @@ -2022,18 +2026,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){ @@ -2057,6 +2061,7 @@ namespace emp { return *this; } + #endif // DOXYGEN_SHOULD_SKIP_THIS // ------------------------- Extra Functions ------------------------- @@ -2078,7 +2083,8 @@ namespace emp { } -/// For hashing BitArrays +#ifndef DOXYGEN_SHOULD_SKIP_THIS +// For hashing BitArrays namespace std { template @@ -2090,5 +2096,5 @@ namespace std } }; } - +#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 53c5b49323..97a73ba00d 100644 --- a/include/emp/bits/BitMatrix.hpp +++ b/include/emp/bits/BitMatrix.hpp @@ -21,10 +21,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 @@ -37,10 +37,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 @@ -67,7 +70,6 @@ namespace emp { return mask; } -// public: BitMatrix() { ; } BitMatrix(const BitSet & in_bits) : bits(in_bits) { ; } BitMatrix(const BitMatrix & in_matrix) : bits(in_matrix.bits) { ; } @@ -112,15 +114,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; } @@ -130,10 +131,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(); @@ -150,10 +151,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/BitVector.hpp b/include/emp/bits/BitVector.hpp index 787f533019..fc3d76bb16 100644 --- a/include/emp/bits/BitVector.hpp +++ b/include/emp/bits/BitVector.hpp @@ -19,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. * @@ -48,9 +48,9 @@ namespace emp { - /// @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. @@ -85,7 +85,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? @@ -227,7 +227,7 @@ 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. + /// Retrieve the bit value from the specified index. [[nodiscard]] bool Get(size_t index) const; /// A safe version of Get() for indexing out of range. Useful for representing collections. @@ -344,7 +344,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 BitVector. @@ -483,7 +483,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. @@ -494,7 +494,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. @@ -504,7 +504,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. @@ -693,7 +693,7 @@ 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; } @@ -728,6 +728,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) { @@ -749,6 +751,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. @@ -757,7 +761,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. @@ -877,7 +881,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 )); } @@ -887,7 +891,7 @@ namespace emp { 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; @@ -953,7 +957,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 )); @@ -964,7 +968,7 @@ namespace emp { 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; @@ -1256,7 +1260,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); @@ -1547,7 +1551,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); @@ -2056,7 +2060,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 )); @@ -2139,7 +2143,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 )); diff --git a/include/emp/compiler/lexer_utils.hpp b/include/emp/compiler/lexer_utils.hpp index 24e80e4274..1c88401cc7 100644 --- a/include/emp/compiler/lexer_utils.hpp +++ b/include/emp/compiler/lexer_utils.hpp @@ -24,6 +24,10 @@ namespace emp { + /** \addtogroup + * @{ + */ + /// Converting DFA to DFA -- no change needed. static inline const DFA & to_DFA(const DFA & dfa) { return dfa; } @@ -53,7 +57,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); @@ -92,7 +96,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)); @@ -144,6 +148,9 @@ namespace emp { return ""; } + + // Close Doxygen group + /** @}*/ } #endif // #ifndef EMP_COMPILER_LEXER_UTILS_HPP_INCLUDE diff --git a/include/emp/config/config_web_interface.hpp b/include/emp/config/config_web_interface.hpp index 7fcdaf5459..7a462e85aa 100644 --- a/include/emp/config/config_web_interface.hpp +++ b/include/emp/config/config_web_interface.hpp @@ -32,11 +32,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/Signal.hpp b/include/emp/control/Signal.hpp index 27a04188af..12d8f6ad90 100644 --- a/include/emp/control/Signal.hpp +++ b/include/emp/control/Signal.hpp @@ -76,12 +76,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; @@ -360,6 +361,7 @@ 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 @@ -374,7 +376,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/data/DataNode.hpp b/include/emp/data/DataNode.hpp index 7e150fbb42..918fd72773 100644 --- a/include/emp/data/DataNode.hpp +++ b/include/emp/data/DataNode.hpp @@ -158,8 +158,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. @@ -186,8 +193,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. @@ -229,8 +243,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. @@ -294,8 +315,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. @@ -341,8 +369,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. @@ -395,8 +430,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. @@ -467,9 +509,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/ @@ -546,8 +594,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? @@ -586,7 +642,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;} @@ -631,7 +687,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(); @@ -650,8 +706,15 @@ 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. @@ -662,7 +725,6 @@ namespace emp { using base_t = DataNodeModule; using base_t::in_vals; - #endif /*DOXYGEN_SHOULD_SKIP_THIS*/ void PullData_impl() { in_vals = pull_funs.Run(); @@ -671,7 +733,8 @@ namespace emp { in_vals.insert(in_vals.end(), x.begin(), x.end()); } } - + #endif /*DOXYGEN_SHOULD_SKIP_THIS*/ + public: DataNodeModule() : pull_funs(), pull_set_funs() { ; } @@ -684,6 +747,8 @@ namespace emp { } }; + #ifndef DOXYGEN_SHOULD_SKIP_THIS + template class DataNode_Interface; /// Outermost interface to all DataNode modules. @@ -693,7 +758,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 { @@ -701,6 +766,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/datastructs/BloomFilter.hpp b/include/emp/datastructs/BloomFilter.hpp index 288fd035a7..b4c59407d7 100644 --- a/include/emp/datastructs/BloomFilter.hpp +++ b/include/emp/datastructs/BloomFilter.hpp @@ -16,7 +16,7 @@ #ifndef EMP_DATASTRUCTS_BLOOMFILTER_HPP_INCLUDE #define EMP_DATASTRUCTS_BLOOMFILTER_HPP_INCLUDE -/********************************************************************* +/* * Open Bloom Filter * * * * Author: Arash Partow - 2000 * @@ -54,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 { @@ -172,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/DynamicString.hpp b/include/emp/datastructs/DynamicString.hpp index 23afd8f187..29b9b56326 100644 --- a/include/emp/datastructs/DynamicString.hpp +++ b/include/emp/datastructs/DynamicString.hpp @@ -32,7 +32,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) @@ -84,6 +84,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 ) @@ -94,5 +95,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 4e460afaf3..d0ccd9b5ef 100644 --- a/include/emp/datastructs/Graph.hpp +++ b/include/emp/datastructs/Graph.hpp @@ -5,7 +5,7 @@ */ /** * @file - * @brief A simple, fast class for managing verticies (nodes) and edges. + * @brief A simple, fast class for managing vertices (nodes) and edges. * @note Status: BETA */ @@ -23,6 +23,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: @@ -75,6 +76,7 @@ namespace emp { } }; + #endif // DOXYGEN_SHOULD_SKIP_THIS protected: emp::vector nodes; ///< Set of vertices in this graph. @@ -189,7 +191,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/SmallVector.hpp b/include/emp/datastructs/SmallVector.hpp index a9c27a9e9f..84a6929faf 100644 --- a/include/emp/datastructs/SmallVector.hpp +++ b/include/emp/datastructs/SmallVector.hpp @@ -37,6 +37,7 @@ namespace emp { // helpers for AlignedCharArrayUnion +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace detail { template class AlignerImpl { @@ -57,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. @@ -126,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 @@ -283,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) { @@ -308,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. @@ -364,7 +370,7 @@ class SmallVectorTemplateBase : public SmallVectorTemplateCommon { void pop_back() { this->set_size(this->size() - 1); } }; -// #ifndef DOXYGEN_SHOULD_SKIP_THIS +#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. @@ -720,8 +726,6 @@ class SmallVectorImpl : public SmallVectorTemplateBase { } }; -// #endif /*DOXYGEN_SHOULD_SKIP_THIS*/ - template void SmallVectorImpl::swap(SmallVectorImpl &RHS) { if (this == &RHS) return; @@ -883,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 @@ -961,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. @@ -978,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/functional/AnyFunction.hpp b/include/emp/functional/AnyFunction.hpp index 4e679bee1e..ac88022637 100644 --- a/include/emp/functional/AnyFunction.hpp +++ b/include/emp/functional/AnyFunction.hpp @@ -164,7 +164,7 @@ namespace emp { } }; - + #ifndef DOXYGEN_SHOULD_SKIP_THIS ///////////////////////////////////// // Member function implementaions. @@ -198,6 +198,7 @@ namespace emp { template bool BaseFunction::ConvertOK() { return dynamic_cast *>(this); } + #endif // DOXYGEN_SHOULD_SKIP_THIS } diff --git a/include/emp/functional/GenericFunction.hpp b/include/emp/functional/GenericFunction.hpp index 3725c03238..2433ab58ce 100644 --- a/include/emp/functional/GenericFunction.hpp +++ b/include/emp/functional/GenericFunction.hpp @@ -84,6 +84,7 @@ namespace emp { const fun_t & GetFunction() const { return fun; } }; + #ifndef DOXYGEN_SHOULD_SKIP_THIS ///////////////////////////////////// // Member function implementaions. @@ -117,7 +118,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/geometry/Point2D.hpp b/include/emp/geometry/Point2D.hpp index e9a33f1888..bc30faf6b9 100644 --- a/include/emp/geometry/Point2D.hpp +++ b/include/emp/geometry/Point2D.hpp @@ -109,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, @@ -116,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/hardware/AvidaGP.hpp b/include/emp/hardware/AvidaGP.hpp index f0c298d62f..f432d77b2a 100644 --- a/include/emp/hardware/AvidaGP.hpp +++ b/include/emp/hardware/AvidaGP.hpp @@ -484,6 +484,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) @@ -500,6 +501,7 @@ namespace emp { } os << '\n'; } + #endif // DOXYGEN_SHOULD_SKIP_THIS template size_t AvidaCPU_Base::PredictNextInst() const { @@ -533,6 +535,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(); @@ -564,6 +567,7 @@ namespace emp { // emp::vector reg_stack; // emp::vector call_stack; } + #endif // DOXYGEN_SHOULD_SKIP_THIS class AvidaGP : public AvidaCPU_Base { public: @@ -583,6 +587,7 @@ namespace emp { }; } +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace std { /// operator<< to work with ostream (must be in std to work) @@ -591,5 +596,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 100ea32dc7..a3f991bd1d 100644 --- a/include/emp/hardware/BitSorter.hpp +++ b/include/emp/hardware/BitSorter.hpp @@ -21,7 +21,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) @@ -184,6 +184,7 @@ namespace emp { } +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace std { /// operator<< to work with ostream (must be in std to work) @@ -192,5 +193,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 8a0f1bbbff..78ff940918 100644 --- a/include/emp/hardware/EventDrivenGP.hpp +++ b/include/emp/hardware/EventDrivenGP.hpp @@ -819,7 +819,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/math/Random.hpp b/include/emp/math/Random.hpp index 140d596dee..e138e6c7a4 100644 --- a/include/emp/math/Random.hpp +++ b/include/emp/math/Random.hpp @@ -25,7 +25,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. @@ -407,7 +409,7 @@ namespace emp { /// Generate a random variable drawn from a Poisson distribution. inline uint32_t GetRandPoisson(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 + // Optimizes for speed and calculability using symmetry of the distribution if (p > .5) return (uint32_t) n - GetRandPoisson(n * (1 - p)); else return GetRandPoisson(n * p); } @@ -442,7 +444,7 @@ namespace emp { } inline uint32_t GetRandGeometric(double p){ - emp_assert(p >= 0 && p <= 1, "Pobabilities must be between 0 and 1"); + emp_assert(p >= 0 && p <= 1, "Probabilities 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? diff --git a/include/emp/meta/TypeID.hpp b/include/emp/meta/TypeID.hpp index 0225b26d70..c7e4827141 100644 --- a/include/emp/meta/TypeID.hpp +++ b/include/emp/meta/TypeID.hpp @@ -464,7 +464,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 <> @@ -480,5 +480,6 @@ namespace std { return out; } } +#endif // DOXYGEN_SHOULD_SKIP_THIS #endif // #ifndef EMP_META_TYPEID_HPP_INCLUDE diff --git a/include/emp/meta/type_traits.hpp b/include/emp/meta/type_traits.hpp index 1ae214dcd2..66501cbe4e 100644 --- a/include/emp/meta/type_traits.hpp +++ b/include/emp/meta/type_traits.hpp @@ -81,10 +81,12 @@ namespace emp { template struct HasFromString().FromString(""))>> : std::true_type{}; + #ifndef DOXYGEN_SHOULD_SKIP_THIS // Determine if a type has a FromDouble() member function. 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 { }; diff --git a/include/emp/prefab/CommentBox.hpp b/include/emp/prefab/CommentBox.hpp index bdc1111e05..477ee899f6 100644 --- a/include/emp/prefab/CommentBox.hpp +++ b/include/emp/prefab/CommentBox.hpp @@ -10,7 +10,7 @@ * TODO: When prefab tools for adding mobile only and desktop only * content are created, remove AddMobileContent(), desktop_content * and mobile_content divs, and ConfigPanel as a friend class. - * AddConent() should stream into all_content div. + * AddContent() should stream into all_content div. */ #ifndef EMP_PREFAB_COMMENTBOX_HPP_INCLUDE @@ -27,7 +27,9 @@ namespace prefab { * Optionally, it can contain text and other web elements. */ class CommentBox: public web::Div { + #ifndef DOXYGEN_SHOULD_SKIP_THIS friend ConfigPanel; + #endif DOXYGEN_SHOULD_SKIP_THIS private: // ID for the comment box Div std::string box_base = this->GetID(); diff --git a/include/emp/prefab/ConfigPanel.hpp b/include/emp/prefab/ConfigPanel.hpp index b9c8cfa556..31284bbf06 100644 --- a/include/emp/prefab/ConfigPanel.hpp +++ b/include/emp/prefab/ConfigPanel.hpp @@ -77,7 +77,7 @@ namespace prefab { * panel to your web app. Users can interact with the config panel * by updating values. * - * The ConfigPanel is constructed using subcomponents. Groups of + * The ConfigPanel is constructed using sub-components. Groups of * settings are placed in Cards, and individual settings are represented * by ValueControls. */ @@ -111,7 +111,7 @@ namespace prefab { } inline static std::set numeric_types = {"int", "double", "float", "uint32_t", "uint64_t", "size_t"}; - // Helper function to get prety names from config values + // Helper function to get pretty names from config values inline static std::function format_label = []( const std::string & name ) { @@ -314,7 +314,7 @@ namespace prefab { * Arranges config panel based configuration pass to constructor * @param config the config object used to create this panel * @param open should the card for the panel start open? - * @param id_prefix string appended to id for each setting (unusued) + * @param id_prefix string appended to id for each setting (unused) * @deprecated No longer necessary for config panel to function. * This function was a work around to fix a bug. */ diff --git a/include/emp/prefab/ValueBox.hpp b/include/emp/prefab/ValueBox.hpp index 8e2693acf9..6cb0e03037 100644 --- a/include/emp/prefab/ValueBox.hpp +++ b/include/emp/prefab/ValueBox.hpp @@ -5,7 +5,7 @@ */ /** * @file - * @brief UI subcomponent for ConfigPanel and ReadoutPanel. + * @brief UI sub-component for ConfigPanel and ReadoutPanel. */ #ifndef EMP_PREFAB_VALUEBOX_HPP_INCLUDE @@ -237,6 +237,7 @@ namespace emp::prefab { )>; // Determine the default range by finding the next highest order of magnitude (base 10) + #ifndef DOXYGEN_SHOULD_SKIP_THIS inline static range_setter_t applyDefaultRange = []( const std::string & value, const std::string & type, @@ -260,6 +261,7 @@ namespace emp::prefab { in.Step(step); } }; + #endif // DOXYGEN_SHOULD_SKIP_THIS public: /** diff --git a/include/emp/web/CanvasShape.hpp b/include/emp/web/CanvasShape.hpp index 36ac884233..1585acb1c3 100644 --- a/include/emp/web/CanvasShape.hpp +++ b/include/emp/web/CanvasShape.hpp @@ -7,7 +7,7 @@ * @file * @brief Define simple shapes to draw on a canvas. * - * Canvas shapes can be definied in detail, describing how they modify a canvas. + * Canvas shapes can be defined in detail, describing how they modify a canvas. * * Other, more specific actions defined here are: * CanvasCircle @@ -28,7 +28,7 @@ namespace emp { namespace web { - /// Define an arbitrary shape to draw on a canvas (base clase) + /// Define an arbitrary shape to draw on a canvas (base class) class CanvasShape : public CanvasAction { protected: Point p; ///< Anchor point for this shape. @@ -93,7 +93,7 @@ namespace web { /// Track a rectangle shape to be drawn on a canvas. class CanvasRect : public CanvasShape { - double w; ///< Rectangle widgth. + double w; ///< Rectangle width. double h; ///< Rectangle height. public: CanvasRect(Point _p, double _w, double _h, @@ -116,7 +116,7 @@ namespace web { /// Clear a rectangular area in a canvas. class CanvasClearRect : public CanvasShape { - double w; ///< Rectangle widgth. + double w; ///< Rectangle width. double h; ///< Rectangle height. public: CanvasClearRect(Point _p, double _w, double _h) @@ -133,7 +133,7 @@ namespace web { /// An arbitrary-sized polygon to be drawn on a canvas. class CanvasPolygon : public CanvasShape { private: - emp::vector points; ///< Series of points defining the perimiter of the Polygon. + emp::vector points; ///< Series of points defining the perimeter of the Polygon. public: CanvasPolygon(const std::string & fc="", const std::string & lc="") : CanvasShape(0, 0, fc, lc) { ; } diff --git a/include/emp/web/DocuExtras.hpp b/include/emp/web/DocuExtras.hpp index 09187c87cd..83aed5bb1b 100644 --- a/include/emp/web/DocuExtras.hpp +++ b/include/emp/web/DocuExtras.hpp @@ -28,9 +28,9 @@ namespace web { protected: class DocuExtrasInfo : public internal::WidgetInfo { - + #ifndef DOXYGEN_SHOULD_SKIP_THIS friend DocuExtras; - + #endif // DOXYGEN_SHOULD_SKIP_THIS protected: DocuExtrasInfo(const std::string & in_id) @@ -62,7 +62,7 @@ namespace web { }; // end of InputInfo definition - // Get a properly cast version of indo. + // Get a properly cast version of info. DocuExtrasInfo * Info() { return (DocuExtrasInfo *) info; } const DocuExtrasInfo * Info() const { return (DocuExtrasInfo *) info; } diff --git a/include/emp/web/Table.hpp b/include/emp/web/Table.hpp index 5571c7cd2c..e41e33dd00 100644 --- a/include/emp/web/Table.hpp +++ b/include/emp/web/Table.hpp @@ -12,7 +12,7 @@ * object. * * A Table is composed of a series of rows, each with the same number of columns. - * TableDataInfo may be muliple cells wide/tall, masking other cells. + * TableDataInfo may be multiple cells wide/tall, masking other cells. * * * @todo Tables should more directly manage internal slates rather than just adding divs and @@ -37,6 +37,7 @@ namespace emp { namespace web { + #ifndef DOXYGEN_SHOULD_SKIP_THIS class TableWidget; class Table; class TableCell; @@ -45,7 +46,6 @@ namespace web { class TableRowGroup; class TableColGroup; - #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace internal { struct TableRowInfo; @@ -60,7 +60,7 @@ namespace web { emp::vector children; ///< Widgets contained in this cell. - /// Debug function to determine if this datum is structually consistent. + /// Debug function to determine if this datum is structurally consistent. bool OK(std::stringstream & ss, bool verbose=false, const std::string & prefix="") { bool ok = true; if (verbose) ss << prefix << "Scanning: emp::TableDataInfo" << std::endl; @@ -88,7 +88,7 @@ namespace web { return *this; } - /// Debug function to determine if this row is structually consistent. + /// Debug function to determine if this row is structurally consistent. bool OK(std::stringstream & ss, bool verbose=false, const std::string & prefix="") { bool ok = true; if (verbose) { ss << prefix << "Scanning: emp::TableRowInfo" << std::endl; } @@ -107,8 +107,10 @@ namespace web { }; class TableInfo : public internal::WidgetInfo { + #ifndef DOXYGEN_SHOULD_SKIP_THIS friend TableWidget; friend Table; friend TableCell; friend TableRow; friend TableCol; friend TableRowGroup; friend TableColGroup; + #endif // DOXYGEN_SHOULD_SKIP_THIS protected: size_t row_count; /// How big is this table? size_t col_count; @@ -236,18 +238,18 @@ namespace web { // Tables need to facilitate recursive registrations - void RegisterChildren(internal::DivInfo * regestrar) override { + void RegisterChildren(internal::DivInfo * registrar) override { for (size_t r = 0; r < row_count; r++) { for (size_t c = 0; c < col_count; c++) { - for (Widget & child : rows[r].data[c].children) regestrar->Register(child); + for (Widget & child : rows[r].data[c].children) registrar->Register(child); } } } - void UnregisterChildren(internal::DivInfo * regestrar) override { + void UnregisterChildren(internal::DivInfo * registrar) override { for (size_t r = 0; r < row_count; r++) { for (size_t c = 0; c < col_count; c++) { - for (Widget & child : rows[r].data[c].children) regestrar->Unregister(child); + for (Widget & child : rows[r].data[c].children) registrar->Unregister(child); } } } @@ -515,7 +517,9 @@ namespace web { #endif // DOXYGEN_SHOULD_SKIP_THIS class TableWidget : public internal::WidgetFacet { + #ifndef DOXYGEN_SHOULD_SKIP_THIS friend class internal::TableInfo; + #endif // DOXYGEN_SHOULD_SKIP_THIS protected: size_t cur_row; // Which row/col is currently active? size_t cur_col; @@ -588,14 +592,14 @@ namespace web { void ClearCells() { Info()->ClearTableCells(); } void ClearCell(size_t r, size_t c) { Info()->ClearCell(r, c); } - TableCell GetCell(size_t r, size_t c) const; ///< Focus on a specifc cell in the table. - TableRow GetRow(size_t r) const; ///< Focus on a specifc row in the table. - TableCol GetCol(size_t c) const; ///< Focus on a specifc column in the table. - TableRowGroup GetRowGroup(size_t r) const; ///< Focus on a specifc group of rows in the table. - TableColGroup GetColGroup(size_t c) const; ///< Focus on a specifc group of columns in the table. + TableCell GetCell(size_t r, size_t c) const; ///< Focus on a specific cell in the table. + TableRow GetRow(size_t r) const; ///< Focus on a specific row in the table. + TableCol GetCol(size_t c) const; ///< Focus on a specific column in the table. + TableRowGroup GetRowGroup(size_t r) const; ///< Focus on a specific group of rows in the table. + TableColGroup GetColGroup(size_t c) const; ///< Focus on a specific group of columns in the table. Table GetTable() const; ///< Focus on a the entire table. - /// Get the TExt widget assoited with the currently active cell. + /// Get the Text widget associated with the currently active cell. web::Text GetTextWidget() { return Info()->GetTextWidget(); } /// Add text to a specified cell in the table. diff --git a/include/emp/web/Text.hpp b/include/emp/web/Text.hpp index 8fb01da961..fefdc3356c 100644 --- a/include/emp/web/Text.hpp +++ b/include/emp/web/Text.hpp @@ -26,11 +26,15 @@ namespace web { /// A Text widget handles putting text on a web page that can be controlled and modified. class Text : public internal::WidgetFacet { + #ifndef DOXYGEN_SHOULD_SKIP_THIS friend class TextInfo; + #endif DOXYGEN_SHOULD_SKIP_THIS protected: class TextInfo : public internal::WidgetInfo { + #ifndef DOXYGEN_SHOULD_SKIP_THIS friend Text; + #endif DOXYGEN_SHOULD_SKIP_THIS protected: DynamicString strings; ///< All string (and functions returning strings) in Text widget. bool append_ok; ///< Can this Text widget be extended? @@ -45,10 +49,15 @@ namespace web { bool AppendOK() const override { return append_ok; } void PreventAppend() override { append_ok = false; } + /// Add new text to this string. Widget Append(const std::string & in_text) override; + /// Add a function that produces text to this widget. Every time the widget is re-drawn, the + /// function will be re-run to get the latest version of the text. When a Live() function + /// wraps a variable it simply makes sure that this version of Append is called so that the + /// value of the variable is kept live. Widget Append(const std::function & in_fun) override; - // All derived widgets must suply a mechanism for providing associated HTML code. + // All derived widgets must supply a mechanism for providing associated HTML code. virtual void GetHTML(std::stringstream & HTML) override { HTML.str(""); // Clear the current text. HTML << "" // Initial span tag to keep id. @@ -61,7 +70,7 @@ namespace web { }; // End of TextInfo - // Get a properly cast version of indo. + // Get a properly cast version of info. TextInfo * Info() { return (TextInfo *) info; } const TextInfo * Info() const { return (TextInfo *) info; } @@ -84,7 +93,9 @@ namespace web { Text & Clear() { Info()->strings.Clear(); return *this; } }; - /// Add new text to this string. + #ifndef DOXYGEN_SHOULD_SKIP_THIS + + // Add new text to this string. Widget Text::TextInfo::Append(const std::string & text) { if (!append_ok) return ForwardAppend(text); // If text widget cannot append, forward to parent. strings.Append(text); // Record the new string being added. @@ -92,10 +103,10 @@ namespace web { return web::Text(this); } - /// Add a function that produces text to this widget. Every time the widget is re-drawn, the - /// function will be re-run to get the latest version of the text. When a Live() function - /// wraps a variable it simply makes sure that this version of Append is called so that the - /// value of the variable is kept live. + // Add a function that produces text to this widget. Every time the widget is re-drawn, the + // function will be re-run to get the latest version of the text. When a Live() function + // wraps a variable it simply makes sure that this version of Append is called so that the + // value of the variable is kept live. Widget Text::TextInfo::Append(const std::function & fun) { if (!append_ok) return ForwardAppend(fun); // If text widget cannot append, forward to parent. strings.Append(fun); // Record the new function being added. @@ -103,6 +114,7 @@ namespace web { return web::Text(this); } + #endif // DOXYGEN_SHOULD_SKIP_THIS } } diff --git a/include/emp/web/TextFeed.hpp b/include/emp/web/TextFeed.hpp index f48e013efb..e410f5b91a 100644 --- a/include/emp/web/TextFeed.hpp +++ b/include/emp/web/TextFeed.hpp @@ -25,9 +25,11 @@ namespace web { /// A TextFeed widget handles putting text on a web page that can be controlled and modified. class TextFeed : public internal::WidgetFacet { + #ifndef DOXYGEN_SHOULD_SKIP_THIS friend class TextFeedInfo; + #endif // DOXYGEN_SHOULD_SKIP_THIS protected: - // #ifndef DOXYGEN_SHOULD_SKIP_THIS + #ifndef DOXYGEN_SHOULD_SKIP_THIS class TextFeedInfo : public internal::WidgetInfo { friend TextFeed; protected: @@ -57,9 +59,10 @@ namespace web { bool AppendOK() const override { return append_ok; } void PreventAppend() override { append_ok = false; } + /// Add new text to this string. Widget Append(const std::string & in_text) override; - // All derived widgets must suply a mechanism for providing associated HTML code. + // All derived widgets must supply a mechanism for providing associated HTML code. virtual void GetHTML(std::stringstream & HTML) override { HTML.str(""); // Clear the current text. HTML << ""; // Initial span tag to keep id. @@ -73,7 +76,7 @@ namespace web { public: virtual std::string GetType() override { return "web::TextFeedInfo"; } }; // End of TextFeedInfo - // #endif // DOXYGEN_SHOULD_SKIP_THIS + #endif // DOXYGEN_SHOULD_SKIP_THIS // Get a properly cast version of info. TextFeedInfo * Info() { return (TextFeedInfo *) info; } @@ -105,7 +108,8 @@ namespace web { TextFeed & PopBack() { Info()->strings.pop_back(); return *this; } }; - /// Add new text to this string. + #ifndef DOXYGEN_SHOULD_SKIP_THIS + // Add new text to this string. Widget TextFeed::TextFeedInfo::Append(const std::string & text) { // If text widget cannot append, forward to parent. if (!append_ok) return ForwardAppend(text); @@ -140,7 +144,7 @@ namespace web { return web::TextFeed(this); } - + #endif // DOXYGEN_SHOULD_SKIP_THIS } // namespace web } // namespace emp