Commit 6ab7693c authored by Pierre-Henri Wuillemin's avatar Pierre-Henri Wuillemin

synchronized with master

parents 89c28cc1 ed34ac13
aGrUM is a C++ library designed for easily building applications using graphical models such as Bayesian networks, influence diagrams, decision trees, GAI networks or Markov decision processes.
aGrUM is a C++ library designed for easily building applications using graphical
models such as Bayesian networks, influence diagrams, decision trees or Markov
decision processes.
It is written to provide the basic building blocks to perform the following tasks :
It is written to provide the basic building blocks to perform the following
tasks :
* graphical model learning/elicitation,
* inference within the graphical model,
* graphical models learning/elicitation,
* probabilistic inference with graphical models,
* planification.
Homepage
========
[aGrUM/pyAgrum on gitlab.io](http://agrum.org)
For a less technical introduction, checkout
[the project's homepage](http://agrum.gitlab.io).
# Philosophy & Design
aGrUM initial purpose was to support, sustain and share new frameworks and algorithms from the Graphical Models and Decision team at [LIP6](http://www.lip6.fr): [Christophe Gonzales](https://www.lip6.fr/actualite/personnes-fiche.php?ident=P37&LANG=en), [Pierre-Henri Wuillemin](https://www.lip6.fr/actualite/personnes-fiche.php?ident=P67) and [students and former students](http://agrum.gitlab.io/pages/hall-of-fame.html). aGrUM then took the form of a generic framework designed to ease the emergence and experimentation of new ideas. More and more, as a consequence, it began to fulfill all the requirements for providing a complete library to build applications using Graphical Models, with a strong emphasis on Bayesian Networks. Once the prohect reached this state, the team decided to provide it as an open source contribution for the decision support and data science communities. The last move in that direction has been to port its main code repository at gitlab.
aGrUM main goals include code quality, code reuse and performance: aGrUM is written in modern C++11/17 (cross-platform : gcc>=4.8, clang and visual C++); tests and continuous integration are at the core of aGrUM's development; many classical algorithms have been reimplemented and multi-threaded to speed up learning and inference.
Moreover, aGrUM allows its users to very finely tune learning (choosing priors, score, constraints, algorithms and more), inference (choosing their algorithms but also their components like message passing algorithms, triangulation algorithms, etc.) and modeling (choosing the type of discrete variables, making qualitative and quantitative introspection in the model, etc.). The different frameworks (Bayesian networks, credal networks, FMDP, etc.) are treated in the same way (see [here](http://agrum.gitlab.io/pages/agrum.html) for a more exhaustive list of features).
## Wrappers
The main drawback of such a complex toolbox is of course the long and arduous learning curve associated to its inherent complexities. It is the reason why it has been decided to ship wrappers in easier language with easier APIs together with aGrUM. pyAgrum (for python) is the main wrapper of aGrUM. jAgrum and other experimental wrappers could be shipped soon (if needed or asked).
aGrUM uses [swig](http://www.swig.org/) to build these wrappers.
## Documentation
Documentation is a never-ending story for a library with a complex API. The team tries its best to keep
- [doxygen aGrUM documentation](http://www-desir.lip6.fr/~phw/aGrUM/docs/last/doxygen/)
- [sphinx pyAgrum documentation](http://www-desir.lip6.fr/~phw/aGrUM/docs/last/pyAgrum/)
- [jupyter notebooks as tutorials](http://www-desir.lip6.fr/~phw/aGrUM/docs/last/notebooks/) (pyAgrum)
up to date but maintaining them is a difficult task and we are actively looking for people to help us writing
those documentations and tutorials.
## Dependencies
aGrUM's team tries its best to not introduce external dependencies with aGrUM (understand no external dependencies for the C++ code of aGrUM). All external dependencies are included in aGrUM's source. At the moment the external programs shipped with aGrUM are:
- [nanodbc](https://github.com/lexicalunit/nanodbc)
- [lrs](http://cgm.cs.mcgill.ca/~avis/C/lrs.html)
- [tinyxml](http://www.grinninglizard.com/tinyxml/)
- [CxxTest](http://cxxtest.com/)
# Project Structure
The project's root is composed of the following folders:
- [acttools](/acttools): implementation of ACT, the aGrUM Compiler Tool
- [apps](/apps): Applications using aGrUM or pyAgrum
- [src](/src): aGrUM's C++ source code
- [wrappers](/wrappers): aGrUM's wrappers
In [src](/src) you can find the following folders:
- [agrum](/src/agrum): aGrUM's C++ source code
- [cmake](/src/cmake): CMake files used to compile aGrUM
- [docs](/src/docs): aGrUM's documentation
- [run](/src/run): minmial code to run aGrUM
- [testunits](/src/testunits): aGrUM's tests
In [wrappers](/wrappers) you can find the following folders:
- [swig](/wrappers/swig): generic files declaring the part of aGrUM's API that will be wrapped.
- [pyAgrum](/wrapper/pyAgrum): pyAgrum files (including proper tests and sphinx documentation)
- [jAgrum](/wrappers/jAgrum): experimental java wrapper
# Building
It is strongly recommended to use ACT to build aGrUM. Act requires Python (2.7 or >=3.5 )
and you will need, depending of your platform, the following tools:
- Linux:
- Python
- g++
- CMake
- MacOS:
- Python
- xCode
- CMake
- Microsoft Windows:
- Python
- Microsoft Visual 2015 Comunity Edition
- CMake
If you chose not to use ACT, you can directly use CMake to build aGrUM, but you will need
some tweaking to run tests or use specific compilation options.
## act: aGrUM Compilation Tool
To get the full list of `act` commands, you can run `act --help`. `act` uses three
agruments: a `target`, a `version` and an `action`. Values for each argument do
not intersect, so order is not important.
The `target` argument tells ACT what to build: `aGrUM`, `pyAgrum` or `jAgrum`.
The `version` argument tells ACT to build either in `release` or `debug`.
The `action` argument tells ACT what to do one the build is finished. Some actions
are only available for one target. For example the `wheel` action only works with
the `pyAgrum` target and builds the pyAgrum Python wheel.
Common actions to all targets are:
- `install`: installs the target, use option `-d` to set installation path
- `uninstall`: uninstalls the target
- `test`: execute tests for the given target
- `lib`: compiles the target
- `doc`: builds the target's documentation
Actions `install`, `uninstall` and `test` are self explanatory (we will cover
options later). Action `lib` will just compile the target.
Some actions are not related to any target:
- `clean`: cleans the build folder
- `autoindent`: applies clang formatting to all C++ source code
- `show`: shows current values for each options of act
`act` caches the last command, so you can simply execute `act` to rerun the previous
command.
One action only applies to the target `pyAgrum`:
- wheel: builds a PIP wheel, use option `-d` to define the wheels localisation
## Building aGrUM
To build aGrUM, the following command can be used: `act test release aGrUM`.
Testing your build IS recommended, but it running all tests is long so use action
`lib` to not run any tests or use option `-m` or `-t` to reduce the number of test.
## Building pyAgrum
To build pyAgrum, the following command can be used: `act test release pyAgrum`.
As for aGrUM, testing your build IS recommended. You can use action `lib` to not
run any tests or use option `-t quick` to not execute notebooks tests (which are
quite long).
### Building Wheels
To build a wheel of pyAgrum use the following command: `act wheel release pyAgrum -d $(pwd)`
to copy the wheel in your current folder.
Building wheels do not use CMake to compile aGrUM and pyAgrum, so the compile can be
quite longer on some systems.
### Building Conda Packages
Conda package are automatically build [here](https://github.com/conda-forge/pyagrum-feedstock).
## Building with MVSC
To build with MVSC you will need to use the same commands as stated above with
the option `--mvsc` for 64 bits builds or `--mvsc32` for 32 bits builds.
# Contributing
To contribute to aGrUM, you should fork the project and proceed with a merge request
one your contribution is done.
Please note that there is a contribution policy to sign and email us before we
can accept any contribution. You can find the details for upload your contribution
agreement [here](https://gitlab.com/agrumery/aGrUM/blob/master/CONTRIBUTING.md).
# Branches
We use the following convention for branch names:
- `documentation/*` for documentation branches
- `internal/*` for any utility development (CI, act, etc..)
- `bug/*` for debugging
- `feature/*` for implementing new features
- `backport/*` for maintaining specific tags
# Continuous Integration
Each commits are tested on Ubuntu 17.04, Mac OS El Capitan and Windows 10. The build
process as follow:
1. Builds aGrUM on all platforms
2. Builds pyAgrum on all platforms
3. Tests aGrUM on all platforms
4. Tests pyAgrum on all platforms
We are continuously improving on automatic builds, so expect this to change on a
regularly basis.
# Testing
## Testing aGrUM
Testing aGrUM is done using action `test` on target `aGrUM`. Important options are:
- `-m MODULES`: executes tests for modules specified by `MODULES`
- `-t TETS`: executes tests for tests suites specified by `TESTS`
The `-t` option supersedes `-m` options, but act will raise an error if you ask
for test suites not in the modules list set by `-m`.
Test files are located in [src/testunits](/src/testunits). Test suites are placed
in there respective module folder:
- [module_BASE](/src/testunits/module_BASE) is used to test modules core, graph,
multidim, variables
- module_XXXX is used to test module agrum/XXX
## Testing pyAgrum
Testing pyAgrum is done using action `test` on target `pyAgrum`. It is not possible
to test a single test suite for pyAgrum, you can however run tests including
notebooks with option `-t all` or only tests with `-t quick`.
Notebooks are not written with the intent to serve as tests, but we find it
useful to make our tests fails when a given cell in a notebooks fails to keep
them up-to-date.
# Bibliography
See [aGrUM's reference](agrum.gitlab.io/pages/reference.html) and
......@@ -34,7 +34,7 @@ namespace gum {
namespace samplers {
/**
* @class Gibbs Gibbs.h <agrum/BN/particle/Gibbs.h>
* @class GibbsSampler Gibbs.h <agrum/BN/samplers/Gibbs.h>
* @brief class for particle using GIBBS sampling
* @ingroup bn_particles
*
......@@ -58,17 +58,17 @@ namespace gum {
*/
/**
* Insert new evidence in the sampler.
* @warning if an evidence already w.r.t. a given node and a new
* evidence w.r.t. this node is inserted, the old evidence is removed.
*/
* Insert new evidence in the sampler.
* @warning if an evidence already w.r.t. a given node and a new
* evidence w.r.t. this node is inserted, the old evidence is removed.
*/
virtual void addSoftEvidenceSampler( const Potential<GUM_SCALAR>& pot );
/**
* Insert new hard evidence in the sampler.
* @warning if an evidence already w.r.t. a given node and a new
* evidence w.r.t. this node is inserted, the old evidence is removed.
*/
* Insert new hard evidence in the sampler.
* @warning if an evidence already w.r.t. a given node and a new
* evidence w.r.t. this node is inserted, the old evidence is removed.
*/
virtual void addHardEvidenceSampler( NodeId id, Idx pos );
/**
......
......@@ -51,7 +51,7 @@
namespace gum {
/**
* @class sdyna sdyna.h <agrum/FMDP/SDyna/sdyna.h>
* @class SDYNA sdyna.h <agrum/FMDP/SDyna/sdyna.h>
* @brief
* @ingroup fmdp_group
*
......@@ -59,7 +59,6 @@ namespace gum {
* Instance of SDyna architecture should inherit
*
*/
class SDYNA {
// ###################################################################
......
......@@ -68,15 +68,14 @@ namespace gum {
PutDown = 5,
FillUp = 6
};
// clang-format off
/**
* @class Factory factorySimulator.h
* <agrum/FMDP/simulation/factorySimulator.h>
* @class FactorySimulator factorySimulator.h <agrum/FMDP/simulation/factorySimulator.h>
* @brief A class to simulate the Factory problem
* @ingroup fmdp_group
*
*
*
*/
// clang-format on
class FactorySimulator : public AbstractSimulator {
public:
......
......@@ -68,14 +68,14 @@ namespace gum {
PutDown = 6,
FillUp = 7
};
// clang-format off
/**
* @class Taxi taxiSimulator.h <agrum/FMDP/simulation/taxiSimulator.h>
* @class TaxiSimulator taxiSimulator.h <agrum/FMDP/simulation/taxiSimulator.h>
* @brief A class to simulate the Taxi problem
* @ingroup fmdp_group
*
*
*
*/
// clang-format on
class TaxiSimulator : public AbstractSimulator {
public:
......
......@@ -34,26 +34,24 @@
namespace gum {
namespace prm {
// clang-format off
/**
* @class ClassBayesNet<GUM_SCALAR> classBayesNet.h
*<agrum/PRM/classBayesNet.h>
* @class ClassBayesNet classBayesNet.h <agrum/PRM/classBayesNet.h>
* @brief This class decorates a gum::prm::Class<GUM_SCALAR> has an
*IBaseBayesNet.
* IBaseBayesNet.
* @tparam GUM_SCALAR The type of scalar to use.
*
* This class filters PRMAttribute and PRMAggregate in a way it can be
*interpreted
*as
* a IBayesNet. SlotChains and PRMReferenceSlot are not represented.
* interpreted as a IBayesNet.
*
* SlotChains and PRMReferenceSlot are not represented.
*
* Remember that a ClassBayesNet<GUM_SCALAR> does not contain input nodes
*parents
*and
* output nodes children. Thus you should be careful when using one of the
* BayesNetInference over a ClassBayesNet<GUM_SCALAR> since some variables
*are
*missing in
* the DAG but not in the nodes CPT.
* parents and output nodes children. Thus you should be careful when using
* one of the BayesNetInference over a ClassBayesNet<GUM_SCALAR> since some
* variables are missing in the DAG but not in the nodes CPT.
*/
// clang-format on
template <typename GUM_SCALAR>
class ClassBayesNet : public IBayesNet<GUM_SCALAR> {
public:
......
......@@ -46,12 +46,11 @@ namespace gum {
namespace prm {
/**
* @class Class<GUM_SCALAR> class.h <agrum/PRM/class.h>
* @brief A Class<GUM_SCALAR> is an object of a PRM representing a fragment
*of a
* Bayesian Network which can be instantiated in PRMInstance.
* @class PRMClass PRMClass.h <agrum/PRM/elements/PRMClass.h>
* @brief A PRMClass is an object of a PRM representing a fragment
* of a Bayesian Network which can be instantiated in PRMInstance.
*
* @see PRM Class<GUM_SCALAR> PRMClassElement<GUM_SCALAR>
* @see PRMClassElement
* @ingroup prm_group
*/
template <typename GUM_SCALAR>
......
......@@ -40,23 +40,20 @@ namespace gum {
/**
* @class PRMInstance<GUM_SCALAR> instance.h <agrum/PRM/elements/instance.h>
* @class PRMInstance instance.h <agrum/PRM/elements/instance.h>
*
* @brief An PRMInstance<GUM_SCALAR> is a Bayesian Network fragment defined
*by
*a Class<GUM_SCALAR> and
* used in a PRMSystem.
* @brief An PRMInstance is a Bayesian Network fragment defined by a Class
* and used in a PRMSystem.
*
* Before using an PRMInstance<GUM_SCALAR> for inference YOU MUST call
* PRMInstance<GUM_SCALAR>::instantiateSlotChains() and
*PRMInstance<GUM_SCALAR>::instantiate() on it before,
* otherwise DiscreteVariable pointers will be inconsistent and inference
*will
* be erroneous. Of course, this must be done once you have set all
*reference
* in the current system.
* Before using an PRMInstance for inference YOU MUST call
* PRMInstance::instantiateSlotChains() and PRMInstance::instantiate() on
* it before, otherwise DiscreteVariable pointers will be inconsistent and
* inference will be erroneous. Of course, this must be done once you have
* set all reference in the current system.
*
* @see Class<GUM_SCALAR> PRM PRMClassElement @ingroup prm_group
* @see PRMClass PRM PRMClassElement
*
* @ingroup prm_group
*/
template <typename GUM_SCALAR>
class PRMInstance : public PRMObject {
......
......@@ -61,8 +61,7 @@ namespace gum {
* @class DFSTree DFSTree.h <agrum/PRM/gspan/DFSTree.h>
*
* A DFSTree is used by gspan to sort lexicographically patterns
*discovered
* in an interface graph.
* discovered in an interface graph.
*/
template <typename GUM_SCALAR>
class DFSTree : private DiGraph {
......@@ -144,12 +143,6 @@ namespace gum {
*/
void addRoot( LabelData& data );
/**
* @class EdgeGrowth DFSTree.h <agrum/PRM/DFSTree.h>
* This class is used to define an edge growth of a pattern
* in this DFSTree.
*/
/**
* @brief Add a one edge growth of p as one of its child.
*
......
......@@ -50,6 +50,11 @@ namespace gum {
template <typename GUM_SCALAR>
class DFSTree;
/**
* @class EdgeGrowth DFSTree.h <agrum/PRM/DFSTree.h>
* This class is used to define an edge growth of a pattern
* in this DFSTree.
*/
template <typename GUM_SCALAR>
class EdgeGrowth {
public:
......
......@@ -73,8 +73,7 @@ namespace gum {
std::ostream& operator<<( std::ostream& out, const LabelData& data );
/**
* @struct NodeData<GUM_SCALAR> interfaceGraph.h
* <agrum/PRM/gspan/interfaceGraph.h>
* @struct NodeData interfaceGraph.h <agrum/PRM/gspan/interfaceGraph.h>
* Inner class to handle data about nodes in __graph.
*/
template <typename GUM_SCALAR>
......@@ -107,8 +106,7 @@ namespace gum {
const NodeData<GUM_SCALAR>& data );
/**
* @struct EdgeData<GUM_SCALAR> interfaceGraph.h
* <agrum/PRM/gspan/interfaceGraph.h>
* @struct EdgeData interfaceGraph.h <agrum/PRM/gspan/interfaceGraph.h>
* Inner class to handle data about edges in __graph.
*/
template <typename GUM_SCALAR>
......
......@@ -55,14 +55,16 @@ namespace gum {
template <typename GUM_SCALAR>
class DFSTree;
// clang_format off
/**
* @class SearchStrategy<GUM_SCALAR> DFSTree.h <agrum/PRM/gspan/DFSTree.h>
* @class SearchStrategy searchStrategy.h <agrum/PRM/gspan/searchStrategy.h>
*
* This is an abstract class used to tune search strategies in the gspan
* algorithm. Since GSpan uses a DFS to expand the search tree, this class
* works as a stack regarding adding and removing informations about the
*growths.
* growths.
*/
// clang_format on
template <typename GUM_SCALAR>
class SearchStrategy {
......
......@@ -43,7 +43,7 @@
namespace gum {
/**
* @struct maximizes functors.h <agrum/multidim/patterns/functors.h>
* @struct Maximizes functors.h <agrum/core/functors.h>
* @brief Maximization function object class
* @ingroup core
*
......@@ -69,7 +69,7 @@ namespace gum {
};
/**
* @struct minimizes functors.h <agrum/multidim/patterns/functors.h>
* @struct Minimizes functors.h <agrum/core/functors.h>
* @brief Minimization function object class
* @ingroup core
*
......@@ -95,7 +95,7 @@ namespace gum {
};
/**
* @struct argmax functors.h <agrum/multidim/patterns/functors.h>
* @struct ArgumentMaximises functors.h <agrum/core/functors.h>
* @brief Arg Max function object class
* @ingroup core
*
......
#ifndef DOXYGEN_SHOULD_SKIP_THIS
/* lrslib.hpp (vertex enumeration using lexicographic reverse search) */
#define TITLE "lrslib "
#define VERSION "v.6.2 2016.3.28"
......@@ -483,3 +485,5 @@ void lrs_set_obj_mp(
lrs_mp_vector num,
lrs_mp_vector den,
int64_t max ); /* same as lrs_set_obj but num/den has lrs_mp type */
#endif // DOXYGEN_SHOULD_SKIP_THIS
#ifndef DOXYGEN_SHOULD_SKIP_THIS
/* lrslong.h (lrs long integer arithmetic library */
/* Copyright: David Avis 2000, avis@cs.mcgill.ca */
/* Version 4.0, February 17, 2000 */
......@@ -269,3 +271,5 @@ void* xcalloc( int64_t n, int64_t s, int64_t l, char* f );
void lrs_default_digits_overflow();
/* end of lrs_mp.h (vertex enumeration using lexicographic reverse search) */
#endif // DOXYGEN_SHOULD_SKIP_THIS
#ifndef DOXYGEN_SHOULD_SKIP_THIS
/* lrsmp.h (lrs extended precision arithmetic library) */
/* Copyright: David Avis 2000, avis@cs.mcgill.ca */
/* Version 4.1, February 17, 2000 */
......@@ -279,3 +281,5 @@ void lrs_default_digits_overflow();
void digits_overflow();
/* end of lrsmp.h (vertex enumeration using lexicographic reverse search) */
#endif // DOXYGEN_SHOULD_SKIP_THIS
......@@ -10,6 +10,8 @@
#ifndef DIRENT_H
#define DIRENT_H
#ifndef DOXYGEN_SHOULD_SKIP_THIS
/*
* Define architecture flags so we don't need to include windows.h.
* Avoiding windows.h makes it simpler to use windows sockets in conjunction
......@@ -870,4 +872,7 @@ static void dirent_set_errno( int error ) {
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
#endif /*DIRENT_H*/
#ifndef _UNISTD_H
#define _UNISTD_H 1
#ifndef DOXYGEN_SHOULD_SKIP_THIS
/* This file intended to serve as a drop-in replacement for
* unistd.h on Windows
* Please add functionality as neeeded
......@@ -52,4 +54,6 @@ typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#endif /* unistd.h */
\ No newline at end of file
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
#endif /* unistd.h */
......@@ -53,8 +53,9 @@ namespace gum {
*/
class FixedAllocator {
// clang-format off
/**
* @struct Chunk fixedAllocator.h <agrum/core/fixedAllocator.h>
* @struct __Chunk fixedAllocator.h <agrum/core/smallobjectallocator/fixedAllocator.h>
*
* @brief Allocates objects of one given size. Has a fixed limit of
* allocation
......@@ -72,6 +73,7 @@ namespace gum {
*
* @ingroup core
*/
// clang-format on
struct __Chunk {
// ============================================================================
......
......@@ -39,25 +39,25 @@ namespace gum {
namespace learning {
/* =========================================================================
*/
/* === INDEP TEST G2 CLASS ===
*/
/* =========================================================================
*/
/** @class ScoreG2
// clang-format off
/* ========================================================================= */
/* === INDEP TEST G2 CLASS === */
/* ========================================================================= */
/**
* @class IndepTestG2 indepTestG2.h <agrum/learning/scores_and_tests/indepTestG2.h>
* @brief the class for computing G2 independence test scores
* @ingroup learning_group
*
* The class should be used as follows: first, to speed-up computations, you
* should consider computing all the independence tests you need in one
*pass.
* The class should be used as follows: first, to speed-up computations,
* you should consider computing all the independence tests you need in one
* pass.
*
* To do so, use the appropriate addNodeSet methods. These will compute
* everything you need. Use method score to retrieve the scores related to
* the independence test that were computed. See the IndependenceTest class
*for
* details.
* for details.
*/
// clang-format on
template <typename IdSetAlloc = std::allocator<Idx>,
typename CountAlloc = std::allocator<double>>
class IndepTestG2 : public IndependenceTest<IdSetAlloc, CountAlloc> {
......
......@@ -43,7 +43,7 @@ namespace gum {
// ===========================================================================
// clang-format off
/**
* @class MultiDimCIModel
* @class MultiDimICIModel
* @headerfile multiDimICIModel.h <agrum/multidim/ICIModels/multiDimICIModel.h>
* @ingroup multidim_group
*
......
......@@ -55,7 +55,7 @@ namespace gum {
class MultiDimFunctionGraphTreeManager;
/**
* @class MultiDimFunctionGraph
* @class MultiDimFunctionGraph
* @ingroup multidim_group
*
* @warning Doxygen does not like spanning command on multiple line, so we
......
......@@ -764,6 +764,7 @@ WARN_LOGFILE = warning.txt
# Note: If this tag is empty the current directory is searched.
INPUT = @AGRUM_SOURCE_DIR@/agrum \
@AGRUM_SOURCE_DIR@/docs/mainpage.dox \
@AGRUM_SOURCE_DIR@/docs/howtos \
@AGRUM_SOURCE_DIR@/docs/modules
......@@ -834,7 +835,8 @@ EXCLUDE_PATTERNS = */cocoR/*
EXCLUDE_SYMBOLS = IApproximationPolicy \
ICPTDisturber \
ICPTGenerator \
SetIteratorStaticEnd
SetIteratorStaticEnd \
TerminalNodePolicy
# 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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment