// Copyright(c) 2006 to 2021 ZettaScale Technology and others
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
// v. 1.0 which is available at
// http://www.eclipse.org/org/documents/edl-v10.php.
//
// SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause

#ifndef DDSRT_AVL_H
#define DDSRT_AVL_H

/** \file avl.h
  The tree library never performs memory allocations or deallocations internally.

   - Treedef_t: defines the properties of the tree, offsets,
     comparison functions, augmented structures, flags -- these are
     related to the code/data structure in which the tree is embedded,
     and in nearly all cases known at compile time.
   - avlTree_t: represents the tree, i.e., pointer to the root.
   - avlNode_t: contains the administrative data for a single node in
     the tree.

   For a tree node:
     struct T {
       avlNode_t avlnode;
       int key;
     };
   by definition, avlnodeoffset == offsetof(struct T, avlnode) and
   keyoffset = offsetof(struct T, key). The user of the library only
   ever deals in pointers to (in this case) struct T, never with
   pointers to the avlNode_t, and the compare function operations on
   pointers to keys, in this case pointers to "int"s. If you wish, you
   can also do: keyoffset = 0, in which case the compare function
   would be operating on struct T's.

   The compare function is assumed to behave just like all compare
   functions in the C library: < 0, =0, >0 for left argument less
   than, equal to or greater than the right argument.

   The "augment" function is automatically called whenever some of the
   children of a node change, as well as when the "augment" function
   has been called on some of the children. It allows you to maintain
   a "summary" of the subtree -- currently only used in DDSI, in one
   spot.

   Trees come in various "variants", configured through "treedef"
   flags:
   - direct/indirect key: direct meaning the key value is embedded in
     the structure containing the avlNode_t, indirect meaning a
     pointer to the key value is. The compare function doesn't deal
     with tree nodes, but with key values.
   - re-entrant: in the style of the C library, meaning, the
     comparison function gets a user-supplied 3rd argument (in
     particular used by mmstat).
   - unique keys/duplicate keys: when keys must be unique, some
     optimizations apply; it is up to the caller to ensure one doesn't
     violate the uniqueness of the keys (it'll happily crash in insert
     if you don't); when duplicate keys are allowed, a forward scan of
     the tree will visit them in the order of insertion.

   For a tree node:
     struct T {
       avlnode_t avlnode;
      char *key;
     };
   you could set the "indirect" flag, and then you simply use
   strcmp(), avoiding the need for passing templates in looking up key
   values. Much nicer.

   There is also an orthogonal variant that is enforced through the
   type system -- note that would be possible for all of the above as
   well, but the number of cases simply explodes and none of the above
   flags affects the dynamically changing data structures (just the
   tree definition), unlike this one.

   - the "C" variant keeps track of the number of nodes in the tree to
     support a "count" operation in O(1) time, but is otherwise
     identical.

   The various initializer macros and TreedefInit functions should
   make sense with this.

   All functions for looking up nodes return NULL if there is no node
   satisfying the requirements.

   - Init: initializes a tree (really just: root = NULL, perhaps count = 0)
   - Free: calls "freefun" on each node, which may free the node
   - FreeArg: as "Free", but with an extra, user-supplied, argument
   - Root: returns the root node
   - Lookup: returns a node with key value "key" (ref allowdups flag)
   - LookupIPath: like Lookup, but also filling an IPath_t structure
     for efficient insertion in case of a failed lookup (or inserting
     duplicates)
   - LookupDPath: like Lookup, but also filling a DPath_t structure
     that helps with deleting a node
   - LookupPredEq: locates the node with the greatest key value <= "key"
   - LookupSuccEq: similar, but smallest key value >= "key"
   - LookupPred: similar, < "key"
   - LookupSucc: similar, > "key"
   - Insert: convenience function: LookupIPath ; InsertIPath
   - Delete: convenience function: LookupDPath ; DeleteDPath
   - InsertIPath: insert node based on the "path" obtained from LookupIPath
   - DeleteDPath: delete node, using information in "path" to do so efficiently
   - SwapNode: replace "oldn" by "newn" without modifying the tree
     structure (the key need not be equal, but must be
     FindPred(oldn).key < newn.key < FindSucc(oldn).key, where a
     non-existing predecessor has key -inf and a non-existing
     successor has key +inf, and where it is understood that the <
     operator becomes <= if allowdups is set
   - AugmentUpdate: to be called when something in "node" changes that
     affects the subtree "summary" computed by the configured
     "augment" function
   - IsEmpty: returns 1 if tree is empty, 0 if not
   - IsSingleton: returns 1 if tree contains exactly one node, 0 if not
   - FindMin: returns the node with the smallest key value in the tree
   - FindMax: similar, largest key value
   - FindPred: preceding node in in-order treewalk
   - FindSucc: similar, following node

   - Walk: calls "f" with user-supplied argument "a" once for each
     node, starting at FindMin and ending at FindMax
   - ConstWalk: same, but with a const tree
   - WalkRange: like Walk, but only visiting nodes with key values in
     range [min,max] (that's inclusive)
   - ConstWalkRange: same, but with a const tree
   - WalkRangeReverse: like WalkRange, but in the reverse direction
   - ConstWalkRangeReverse: same, but with a const tree
   - IterFirst: starts forward iteration, starting at (and returning) FindMin
   - IterSuccEq: similar, starting at LookupSuccEq
   - IterSucc: similar, starting at LookupSucc
   - IterNext: returns FindSucc(last returned node); may not be called
     if preceding IterXXX call on same "iter" returned NULL

   That's all there is to it.

   Note that all calls to Walk(f,a) can be rewritten as:
     for(n=IterFirst(&it); n; n=IterNext(&it)) { f(n,a) }
   or as
     for(n=FindMin(); n; n=FindSucc(n)) { f(n,a) }

   The walk functions and iterators may not alter the tree
   structure. If that is desired, the latter can easily be rewritten
   as:
     n=FindMin() ; while(n) { nn=FindSucc(n); f(n,a); n=nn }
   because FindMin/FindSucc doesn't store any information to allow
   fast processing. That'll allow every operation, with the obvious
   exception of f(n) calling Delete(FindSucc(n)).

   Currently, all trees maintain parent pointers, but it may be worth
   doing a separate set without it, as it reduces the size of
   avlNode_t. But in that case, the FindMin/FindSucc option would no
   longer be a reasonable option because it would be prohibitively
   expensive, whereas the IterFirst/IterNext option are alway
   efficiently. If one were to do a threaded tree variant, the
   implemetantion of IterFirst/IterNext would become absolute trivial
   and faster still, but at the cost of significantly more overhead in
   memory and updates.
*/

#include <stdint.h>
#include <stdlib.h>

#include "dds/export.h"
#include "dds/ddsrt/attributes.h"

#if defined (__cplusplus)
extern "C" {
#endif

#define DDSRT_AVL_MAX_TREEHEIGHT (12 * sizeof (void *))

/**
 * @brief User defined compare function.
 * 
 * Input arguments are expected to be pointers to the keys.
 * The expected return values are as follows:
 * - value < 0 to indicate key a is less than key b
 * - value > 0 to indicate key a is greater than key b
 * - value == 0 to indicate key a is equal to key b
 * @see ddsrt_avl_compare_r_t
 */
typedef int (*ddsrt_avl_compare_t) (const void *a, const void *b);

/** @brief Same as @ref ddsrt_avl_compare_t, but with extra argument for state information. */
typedef int (*ddsrt_avl_compare_r_t) (const void *a, const void *b, void *arg);

/**
 * @brief User defined augment function.
 * 
 * If provided, it is called whenever the structure of the tree changes,
 * or when explicitly calling @ref ddsrt_avl_augment_update.
 * It is called for each node on the path to (and inclusive of) the root.
 * It is intended to update user data in nodes when a change occurs in one or both child nodes,
 * or when a the change occurs in the node itself.
 */
typedef void (*ddsrt_avl_augment_t) (void *vnode, const void *left, const void *right);

/**
 * @brief User defined walk function to perform an action on each node in the tree.
 */
typedef void (*ddsrt_avl_walk_t) (void *vnode, void *arg);

/**
 * @brief Like @ref ddsrt_avl_walk_t, but works on const nodes.
 */
typedef void (*ddsrt_avl_const_walk_t) (const void *vnode, void *arg);

/// @brief The avl node is the basic element of the avl tree structure.
/// 
/// To store user data in the tree, the avl node must be embedded in a user node, which is a struct containing the user data.
/// All API calls with a node as input or output, deal with the user node. Example:
/// @code{.c}
/// typedef struct num_s{ // user node
///   ddsrt_avl_node_t node;
///   uint64_t val; // user data
/// }num_t;
/// @endcode
typedef struct ddsrt_avl_node {
    struct ddsrt_avl_node *cs[2]; ///< contains pointers to the left and right child node
    struct ddsrt_avl_node *parent; ///< points to the parent node
    int height; ///< is the height of the subtree starting at this node
} ddsrt_avl_node_t;

/**
 * Save a reference/dereference when using an indirect key. For details, see @ref ddsrt_avl_treedef_init.
 */
#define DDSRT_AVL_TREEDEF_FLAG_INDKEY 1
#define DDSRT_AVL_TREEDEF_FLAG_R 2 /**< Flag used internally by @ref ddsrt_avl_treedef_init_r : compare function has extra argument */
#define DDSRT_AVL_TREEDEF_FLAG_ALLOWDUPS 4 /**< Flag for @ref ddsrt_avl_treedef_init : multiple nodes with the same key are allowed */

/**
 * @brief The tree definition.
 * 
 * The treedef stores settings for the tree, but does not hold the actual tree itself. This implies that:
 * - A single treedef can be used for multiple trees of the same type.
 * - Most tree operations require you to provide both the treedef and the @ref ddsrt_avl_tree.
 * 
 * To initialize it, use @ref ddsrt_avl_treedef_init, @ref ddsrt_avl_treedef_init_r,
 * or one of the 'DDSRT_AVL_TREEDEF_INITIALIZER' macros.
 */
typedef struct ddsrt_avl_treedef {
#if defined (__cplusplus)
    ddsrt_avl_treedef() {}
#endif
    size_t avlnodeoffset;
    size_t keyoffset;
    union {
        ddsrt_avl_compare_t comparekk;
        ddsrt_avl_compare_r_t comparekk_r;
    } u;
    ddsrt_avl_augment_t augment;
    uint32_t flags;
    void *cmp_arg; /* for _r variant */
} ddsrt_avl_treedef_t;

/** @brief Counted version of @ref ddsrt_avl_treedef */
typedef struct ddsrt_avl_ctreedef {
    ddsrt_avl_treedef_t t;
} ddsrt_avl_ctreedef_t;

/**
 * @brief The avl tree.
 * 
 * The tree only knows the root @ref ddsrt_avl_node. The settings for the tree are stored separately in the @ref ddsrt_avl_treedef.
 * As a result, most tree operations require you to provide both the treedef and the tree.
 */
typedef struct ddsrt_avl_tree {
    ddsrt_avl_node_t *root; ///< is the root node of the tree
} ddsrt_avl_tree_t;

/** @brief Counted version of @ref ddsrt_avl_tree */
typedef struct ddsrt_avl_ctree {
    ddsrt_avl_tree_t t;
    size_t count;
} ddsrt_avl_ctree_t;

typedef struct ddsrt_avl_path {
    int depth; /* total depth of path */
    int pnodeidx;
    ddsrt_avl_node_t *parent; /* (nodeidx == 0 ? NULL : *(path[nodeidx-1])) */
    ddsrt_avl_node_t **pnode[DDSRT_AVL_MAX_TREEHEIGHT];
} ddsrt_avl_path_t;

/**
 * @brief Path for inserting a node.
 * @see ddsrt_avl_lookup_ipath
 * @see ddsrt_avl_insert_ipath
 */
typedef struct ddsrt_avl_ipath {
    ddsrt_avl_path_t p;
} ddsrt_avl_ipath_t;

/**
 * @brief Path for removing a node.
 * @see ddsrt_avl_lookup_dpath
 * @see ddsrt_avl_delete_dpath
 */
typedef struct ddsrt_avl_dpath {
    ddsrt_avl_path_t p;
} ddsrt_avl_dpath_t;

/**
 * @brief Iter object for the iterator to store its progress and know where to go next.
 */
typedef struct ddsrt_avl_iter {
    const ddsrt_avl_treedef_t *td;
    ddsrt_avl_node_t *right;
    ddsrt_avl_node_t **todop;
    ddsrt_avl_node_t *todo[1+DDSRT_AVL_MAX_TREEHEIGHT];
} ddsrt_avl_iter_t;

/** @brief Counted version of @ref ddsrt_avl_iter*/
typedef struct ddsrt_avl_citer {
    ddsrt_avl_iter_t t;
} ddsrt_avl_citer_t;

/* avlnodeoffset and keyoffset must both be in [0,2**31-1] */
#define DDSRT_AVL_TREEDEF_INITIALIZER(avlnodeoffset, keyoffset, comparekk_, augment) { (avlnodeoffset), (keyoffset), { .comparekk = (comparekk_) }, (augment), 0, 0 }
#define DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY(avlnodeoffset, keyoffset, comparekk_, augment) { (avlnodeoffset), (keyoffset), { .comparekk = (comparekk_) }, (augment), DDSRT_AVL_TREEDEF_FLAG_INDKEY, 0 }
#define DDSRT_AVL_TREEDEF_INITIALIZER_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk_, augment) { (avlnodeoffset), (keyoffset), { .comparekk = (comparekk_) }, (augment), DDSRT_AVL_TREEDEF_FLAG_ALLOWDUPS, 0 }
#define DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk_, augment) { (avlnodeoffset), (keyoffset), { .comparekk = (comparekk_) }, (augment), DDSRT_AVL_TREEDEF_FLAG_INDKEY|DDSRT_AVL_TREEDEF_FLAG_ALLOWDUPS, 0 }
#define DDSRT_AVL_TREEDEF_INITIALIZER_R(avlnodeoffset, keyoffset, comparekk_, cmparg, augment) { (avlnodeoffset), (keyoffset), { .comparekk_r = (comparekk_) }, (augment), DDSRT_AVL_TREEDEF_FLAG_R, (cmparg) }
#define DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY_R(avlnodeoffset, keyoffset, comparekk_, cmparg, augment) { (avlnodeoffset), (keyoffset), { .comparekk_r = (comparekk_) }, (augment), DDSRT_AVL_TREEDEF_FLAG_INDKEY|DDSRT_AVL_TREEDEF_FLAG_R, (cmparg) }
#define DDSRT_AVL_TREEDEF_INITIALIZER_R_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk_, cmparg, augment) { (avlnodeoffset), (keyoffset), { .comparekk_r = (comparekk_) }, (augment), DDSRT_AVL_TREEDEF_FLAG_R|DDSRT_AVL_TREEDEF_FLAG_ALLOWDUPS, (cmparg) }
#define DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY_R_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk_, cmparg, augment) { (avlnodeoffset), (keyoffset), { .comparekk_r = (comparekk_) }, (augment), DDSRT_AVL_TREEDEF_FLAG_INDKEY|DDSRT_AVL_TREEDEF_FLAG_R|DDSRT_AVL_TREEDEF_FLAG_ALLOWDUPS, (cmparg) }

/* Not maintaining # nodes */

/// @brief Initialize the @ref ddsrt_avl_treedef
/// 
/// Different types of trees are supported. This function defines which type to use.
/// Given a node defined as:
/// @code{.c}
/// typedef struct num_s{ // user node
///   ddsrt_avl_node_t node;
///   uint64_t val; // user data
/// }num_t;
/// @endcode
/// 
/// For a treedef initialized with default settings:
/// @code{.c}
/// ddsrt_avl_treedef_t td;
/// ddsrt_avl_treedef_init(&td, offsetof(num_t, node), offsetof(num_t, val), num_cmp, 0, 0);
/// @endcode
/// 
/// A lookup is performed as:
/// @code{.c}
/// uint64_t key = 7;
/// num_t* num_lookup = ddsrt_avl_lookup(&td, &tree, &key);
/// @endcode
/// 
/// By using keyoffset == 0, you can do the lookup using a dummy node:
/// @code{.c}
/// num_t dummy;
/// dummy.val = 7;
/// num_t* num_lookup = ddsrt_avl_lookup(&td, &tree, &dummy);
/// @endcode
/// 
/// Regarding the flags:
/// - The flag @ref DDSRT_AVL_TREEDEF_FLAG_INDKEY affects functions like @ref ddsrt_avl_compare_t and @ref ddsrt_avl_lookup.
///   These functions expect their 'key' argument to be a pointer, so by default you need to pass a reference to the key.
///   Alternatively, by using @ref DDSRT_AVL_TREEDEF_FLAG_INDKEY you indicate that the key is already a pointer, allowing you to pass the key directly.
///   As an example in the case of a C string, using the INDKEY flag means you can provide the key directly (char*), rather than a reference (char**).
///   @code{.c}
///   typedef struct name_s{ // user node
///     ddsrt_avl_node_t node;
///     char* val; // user data
///   }name_t;
///   @endcode
/// 
///   If using the default flag:
///   @code{.c}
///   ddsrt_avl_treedef_t td;
///   ddsrt_avl_treedef_init(&td, offsetof(name_t, node), offsetof(name_t, val), name_cmp_default, 0, 0);
///   @endcode
///
///   Then a lookup is done as:
///   @code{.c}
///   char* key = strdup("John");
///   name_t* name_lookup = ddsrt_avl_lookup(&td, &tree, &key); // Using default flag
///   @endcode
///
///   If using the INDKEY flag:
///   @code{.c}
///   ddsrt_avl_treedef_t td;
///   ddsrt_avl_treedef_init(&td, offsetof(name_t, node), offsetof(name_t, val), name_cmp_indkey, 0, DDSRT_AVL_TREEDEF_FLAG_INDKEY);
///   @endcode
/// 
///   Then a lookup is done as:
///   @code{.c}
///   char* key = strdup("John");
///   name_t* name_lookup = ddsrt_avl_lookup(&td, &tree, key); // Using INDKEY flag
///   @endcode
/// 
/// - Multiple nodes with the same key are not allowed, unless using @ref DDSRT_AVL_TREEDEF_FLAG_ALLOWDUPS.
/// 
/// To set multiple flags, use the bitwise or.
/// 
/// @param[out] td the treedef to initialize
/// @param[in] avlnodeoffset offset of @ref ddsrt_avl_node relative to the user node.
/// @param[in] keyoffset offset of the key relative to the user node
/// @param[in] comparekk a key compare function, see @ref ddsrt_avl_compare_t
/// @param[in] augment optional function @ref ddsrt_avl_augment_t, provide NULL if not used
/// @param[in] flags optional flags, use 0 for default
/// @see ddsrt_avl_treedef_init_r
/// @see ddsrt_avl_init
DDS_EXPORT void ddsrt_avl_treedef_init (ddsrt_avl_treedef_t *td, size_t avlnodeoffset, size_t keyoffset, ddsrt_avl_compare_t comparekk, ddsrt_avl_augment_t augment, uint32_t flags) ddsrt_nonnull((1,4));

/**
 * @brief Like @ref ddsrt_avl_treedef_init, but there is an extra argument for the compare function.
 * 
 * @param[out] td the treedef to initialize
 * @param[in] avlnodeoffset offset of @ref ddsrt_avl_node relative to the user node.
 * @param[in] keyoffset offset of the key relative to the user node
 * @param[in] comparekk_r a key compare function, see @ref ddsrt_avl_compare_r_t
 * @param[in] cmp_arg is fed to the compare function
 * @param[in] augment optional function @ref ddsrt_avl_augment_t, provide NULL if not used
 * @param[in] flags optional flags, use 0 for default
 * @see ddsrt_avl_treedef_init
 */
DDS_EXPORT void ddsrt_avl_treedef_init_r (ddsrt_avl_treedef_t *td, size_t avlnodeoffset, size_t keyoffset, ddsrt_avl_compare_r_t comparekk_r, void *cmp_arg, ddsrt_avl_augment_t augment, uint32_t flags) ddsrt_nonnull((1,4));

/**
 * @brief Initialize the tree.
 * 
 * The treedef must have been initialized prior to calling this.
 * 
 * @param[in] td treedef of the tree
 * @param[out] tree the avl tree
 * @see ddsrt_avl_free
 * @see ddsrt_avl_treedef_init
 */
DDS_EXPORT void ddsrt_avl_init (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree) ddsrt_nonnull_all;

/**
 * @brief Destroy the tree
 * 
 * Applies user defined free function to each node, and initializes the tree again.
 * The free function is optional, but if not provided, the user is responsible for freeing the memory.
 * 
 * @param[in] td treedef of the tree
 * @param[in,out] tree the avl tree
 * @param[in] freefun optional user defined free function to use on nodes, NULL if not used
 * @see ddsrt_avl_init
 */
DDS_EXPORT void ddsrt_avl_free (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, void (*freefun) (void *vnode)) ddsrt_nonnull((1,2));

/**
 * @brief Same as @ref ddsrt_avl_free, but with an extra argument for the free function.
 * 
 * Extra parameter:
 * @param[in] arg is for the user defined free function
 */
DDS_EXPORT void ddsrt_avl_free_arg (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, void (*freefun) (void *vnode, void *arg), void *arg) ddsrt_nonnull((1,2));

/**
 * @brief Get the root node
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @returns pointer to root node, NULL if tree is empty
 */
DDS_EXPORT void *ddsrt_avl_root (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree) ddsrt_nonnull_all;

/** Same as @ref ddsrt_avl_root, but has undefined behavior in case the tree is empty. */
DDS_EXPORT void *ddsrt_avl_root_non_empty (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree) ddsrt_nonnull_all ddsrt_attribute_returns_nonnull;

/**
 * @brief Lookup a node based on the key
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] key key to search for
 * @return pointer to the node satisfying (node.key == key), NULL if failed
 */
DDS_EXPORT void *ddsrt_avl_lookup (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *key) ddsrt_nonnull_all;

/**
 * @brief Same as @ref ddsrt_avl_lookup, but also initializes a @ref ddsrt_avl_ipath.
 * 
 * The path initialized is intended for use with @ref ddsrt_avl_insert_ipath.
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] key key to search for
 * @param[out] path is the insert path
 * @return pointer to the node satisfying (node.key == key), NULL if failed
 * @see ddsrt_avl_lookup_dpath
 */
DDS_EXPORT void *ddsrt_avl_lookup_ipath (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *key, ddsrt_avl_ipath_t *path) ddsrt_nonnull_all;

/**
 * @brief Same as @ref ddsrt_avl_lookup, but also initializes a @ref ddsrt_avl_dpath.
 * 
 * The path initialized is intended for use with @ref ddsrt_avl_delete_dpath.
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] key key to search for
 * @param[out] path is the delete path
 * @return pointer to the node satisfying (node.key == key), NULL if failed
 * @see ddsrt_avl_lookup_ipath
 */
DDS_EXPORT void *ddsrt_avl_lookup_dpath (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *key, ddsrt_avl_dpath_t *path) ddsrt_nonnull_all;

/**
 * @brief Like @ref ddsrt_avl_lookup, but match greatest node satisfying (node.key <= key)
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] key key to search for
 * @return pointer to the node satisfying (node.key <= key), NULL if failed
 */
DDS_EXPORT void *ddsrt_avl_lookup_pred_eq (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *key) ddsrt_nonnull_all;

/**
 * @brief Like @ref ddsrt_avl_lookup, but match smallest node satisfying (node.key >= key)
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] key key to search for
 * @return pointer to the node satisfying (node.key >= key), NULL if failed
 */
DDS_EXPORT void *ddsrt_avl_lookup_succ_eq (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *key) ddsrt_nonnull_all;

/**
 * @brief Like @ref ddsrt_avl_lookup, but match greatest node satisfying (node.key < key)
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] key key to search for
 * @return pointer to the node satisfying (node.key < key), NULL if failed
 */
DDS_EXPORT void *ddsrt_avl_lookup_pred (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *key) ddsrt_nonnull_all;

/**
 * @brief Like @ref ddsrt_avl_lookup, but match smallest node satisfying (node.key > key)
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] key key to search for
 * @return pointer to the node satisfying (node.key > key), NULL if failed
 */
DDS_EXPORT void *ddsrt_avl_lookup_succ (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *key) ddsrt_nonnull_all;

/**
 * @brief Insert a node into the tree.
 * 
 * There is no protection against duplicates (which causes a crash if the treedef says duplicates are not allowed).
 * If you want a safe insert, you should use @ref ddsrt_avl_lookup_ipath to check for existence, then insert using @ref ddsrt_avl_insert_ipath.
 * 
 * @param[in] td treedef of the tree
 * @param[in,out] tree the avl tree
 * @param[in] vnode the node to insert
 * @see ddsrt_avl_delete
 */
DDS_EXPORT void ddsrt_avl_insert (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, void *vnode) ddsrt_nonnull_all;

/**
 * @brief Remove a node from the tree.
 * 
 * Despite the name 'delete', it only removes the node from the tree. To fully delete it, you still need to free the memory associated with the node.
 * There is no protection against a crash due to trying to remove a nonexisting node.
 * If you want a safe remove, you should use @ref ddsrt_avl_lookup_dpath to check for existence, then remove using @ref ddsrt_avl_delete_dpath.
 * 
 * @param[in] td treedef of the tree
 * @param[in,out] tree the avl tree
 * @param[in] vnode the node to remove
 * @see ddsrt_avl_insert
 */
DDS_EXPORT void ddsrt_avl_delete (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, void *vnode) ddsrt_nonnull_all;

/**
 * @brief Same as @ref ddsrt_avl_insert, but also requires a @ref ddsrt_avl_ipath.
 * 
 * The path required can be initialized by @ref ddsrt_avl_lookup_ipath.
 * 
 * @param[in] td treedef of the tree
 * @param[in,out] tree the avl tree
 * @param[in] vnode the node to insert
 * @param[in] path is the insert path
 * @see ddsrt_avl_delete_dpath
 */
DDS_EXPORT void ddsrt_avl_insert_ipath (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, void *vnode, ddsrt_avl_ipath_t *path) ddsrt_nonnull_all;

/**
 * @brief Same as @ref ddsrt_avl_delete, but also requires a @ref ddsrt_avl_dpath.
 * 
 * The path required can be initialized by @ref ddsrt_avl_lookup_dpath.
 * 
 * @param[in] td treedef of the tree
 * @param[in,out] tree the avl tree
 * @param[in] vnode the node to remove
 * @param[in] path is the delete path
 * @see ddsrt_avl_insert_ipath
 */
DDS_EXPORT void ddsrt_avl_delete_dpath (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, void *vnode, ddsrt_avl_dpath_t *path) ddsrt_nonnull_all;

/**
 * @brief Replace a node in the tree.
 * 
 * Replace a node in the tree without using @ref ddsrt_avl_delete and @ref ddsrt_avl_insert.
 * This operation can invalidate the tree as it doesn't check whether the key of the new node preserves the ordering of the set.
 * 
 * @param[in] td treedef of the tree
 * @param[in,out] tree the avl tree
 * @param[in] voldn the node to replace
 * @param[in] vnewn the node to take the old node's place
 * @see ddsrt_avl_insert
 * @see ddsrt_avl_delete
 */
DDS_EXPORT void ddsrt_avl_swap_node (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, void *voldn, void *vnewn) ddsrt_nonnull_all;

/**
 * @brief Call the user defined augment function @ref ddsrt_avl_augment_t on a node and its parents up to the root.
 * 
 * It is intended to update user data in nodes when a change occurs in one or both child nodes, or when a the change occurs in the node itself.
 * Note that @ref ddsrt_avl_augment_t is called automatically whenever the structure of the tree changes, such as in rotations.
 * It is called for each node on the path to (and inclusive of) the root.
 * 
 * @param[in] td treedef of the tree
 * @param[in] vnode is the node from which to start the augmentation
 */
DDS_EXPORT void ddsrt_avl_augment_update (const ddsrt_avl_treedef_t *td, void *vnode) ddsrt_nonnull_all;

/**
 * @brief Check whether the tree is empty or not.
 * 
 * @param[in] tree the avl tree
 * @return int meant to be interpreted as bool (1 is true, 0 is false)
 */
DDS_EXPORT int ddsrt_avl_is_empty (const ddsrt_avl_tree_t *tree) ddsrt_nonnull_all;

/**
 * @brief Check whether the tree contains exactly one node.
 * 
 * @param[in] tree the avl tree
 * @return int meant to be interpreted as bool (1 is true, 0 is false)
 */
DDS_EXPORT int ddsrt_avl_is_singleton (const ddsrt_avl_tree_t *tree) ddsrt_nonnull_all;

/**
 * @brief Lookup node with smallest key.
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @return pointer to the node
 * @see ddsrt_avl_find_max
 */
DDS_EXPORT void *ddsrt_avl_find_min (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree) ddsrt_nonnull_all;

/**
 * @brief Lookup node with greatest key.
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @return pointer to the node
 * @see ddsrt_avl_find_min
 */
DDS_EXPORT void *ddsrt_avl_find_max (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree) ddsrt_nonnull_all;

/**
 * @brief Get the previous node in the ordered set.
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] vnode the current node
 * @return pointer to the node
 * @see ddsrt_avl_find_succ
 */
DDS_EXPORT void *ddsrt_avl_find_pred (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *vnode) ddsrt_nonnull((1,2));

/**
 * @brief Get the next node in in the ordered set
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] vnode the current node
 * @return pointer to the node
 * @see ddsrt_avl_find_pred
 * @see ddsrt_avl_iter_next
 */
DDS_EXPORT void *ddsrt_avl_find_succ (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *vnode) ddsrt_nonnull((1,2));

/**
 * @brief Walk the tree and apply a user defined function @ref ddsrt_avl_walk_t to each node.
 * 
 * Functionally the same as doing a loop using @ref ddsrt_avl_find_succ or @ref ddsrt_avl_iter_next, and calling the function on each node.
 * Modifying the tree structure is not allowed when using the walk function. If that is desired, use @ref ddsrt_avl_find_succ
 * (or @ref ddsrt_avl_find_pred for the reverse walk).
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] f is the user defined walk function
 * @param[in,out] a is an argument for the user defined function
 * @see ddsrt_avl_find_succ
 * @see ddsrt_avl_iter_next
 */
DDS_EXPORT void ddsrt_avl_walk (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, ddsrt_avl_walk_t f, void *a) ddsrt_nonnull((1,2,3));

/**
 * @brief Like @ref ddsrt_avl_walk, but the user defined function @ref ddsrt_avl_const_walk_t works on const nodes.
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] f is the user defined walk function
 * @param[in,out] a is an argument for the user defined function
 */
DDS_EXPORT void ddsrt_avl_const_walk (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, ddsrt_avl_const_walk_t f, void *a) ddsrt_nonnull((1,2,3));

/**
 * @brief Like @ref ddsrt_avl_walk, but walk within a restricted range defined by [min, max].
 * 
 * - If min is smaller than the smallest value, it starts at the smallest value.
 * - If max is larger than the largest value, it finishes at the largest value.
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] min The smallest value to start at
 * @param[in] max The largest value to finish at (inclusive)
 * @param[in] f is the user defined walk function
 * @param[in,out] a is an argument for the user defined function
 */
DDS_EXPORT void ddsrt_avl_walk_range (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, const void *min, const void *max, ddsrt_avl_walk_t f, void *a)  ddsrt_nonnull((1,2,3,4,5));

/**
 * @brief Like @ref ddsrt_avl_walk_range, but the user defined function @ref ddsrt_avl_const_walk_t works on const nodes.
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] min The smallest value to start at
 * @param[in] max The largest value to finish at (inclusive)
 * @param[in] f is the user defined walk function
 * @param[in,out] a is an argument for the user defined function
*/
DDS_EXPORT void ddsrt_avl_const_walk_range (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *min, const void *max, ddsrt_avl_const_walk_t f, void *a) ddsrt_nonnull((1,2,3,4,5));

/**
 * @brief Like @ref ddsrt_avl_walk_range, but walks in the reverse direction.
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] min The smallest value to finish at (inclusive)
 * @param[in] max The largest value to start at
 * @param[in] f is the user defined walk function
 * @param[in,out] a is an argument for the user defined function
 */
DDS_EXPORT void ddsrt_avl_walk_range_reverse (const ddsrt_avl_treedef_t *td, ddsrt_avl_tree_t *tree, const void *min, const void *max, ddsrt_avl_walk_t f, void *a) ddsrt_nonnull((1,2,3));

/**
 * @brief Like @ref ddsrt_avl_walk_range_reverse, but the user defined function @ref ddsrt_avl_const_walk_t works on const nodes.
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[in] min The smallest value to finish at (inclusive)
 * @param[in] max The largest value to start at
 * @param[in] f is the user defined walk function
 * @param[in,out] a is an argument for the user defined function
 */
DDS_EXPORT void ddsrt_avl_const_walk_range_reverse (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, const void *min, const void *max, ddsrt_avl_const_walk_t f, void *a) ddsrt_nonnull((1,2,3));

/**
 * @brief Get the first node in in the ordered set, and initialize the iterator @ref ddsrt_avl_iter_t.
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[out] iter the iterator
 * @return pointer to the node
 * @see ddsrt_avl_iter_next
 * @see ddsrt_avl_find_min
 */
DDS_EXPORT void *ddsrt_avl_iter_first (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, ddsrt_avl_iter_t *iter) ddsrt_nonnull_all;

/**
 * @brief Similar to @ref ddsrt_avl_iter_first, but start at smallest node satisfying (node.key >= key)
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[out] iter the iterator
 * @param[in] key the key with which to do the lookup
 * @return pointer to the node
 */
DDS_EXPORT void *ddsrt_avl_iter_succ_eq (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, ddsrt_avl_iter_t *iter, const void *key) ddsrt_nonnull_all;

/**
 * @brief Similar to @ref ddsrt_avl_iter_first, but start at smallest node satisfying (node.key > key)
 * 
 * @param[in] td treedef of the tree
 * @param[in] tree the avl tree
 * @param[out] iter the iterator
 * @param[in] key the key with which to do the lookup
 * @return pointer to the node
 */
DDS_EXPORT void *ddsrt_avl_iter_succ (const ddsrt_avl_treedef_t *td, const ddsrt_avl_tree_t *tree, ddsrt_avl_iter_t *iter, const void *key) ddsrt_nonnull_all;

/**
 * @brief Get the next node in in the ordered set.
 * 
 * Modifying the tree structure is not allowed when using the iterator. If that is desired, use @ref ddsrt_avl_find_succ.
 * 
 * @param[in,out] iter the iterator
 * @return pointer to the node
 * @see ddsrt_avl_iter_first
 * @see ddsrt_avl_find_succ
 */
DDS_EXPORT void *ddsrt_avl_iter_next (ddsrt_avl_iter_t *iter) ddsrt_nonnull_all;

/* Maintaining # nodes */

#define DDSRT_AVL_CTREEDEF_INITIALIZER(avlnodeoffset, keyoffset, comparekk, augment) { DDSRT_AVL_TREEDEF_INITIALIZER (avlnodeoffset, keyoffset, comparekk, augment) }
#define DDSRT_AVL_CTREEDEF_INITIALIZER_INDKEY(avlnodeoffset, keyoffset, comparekk, augment) { DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY (avlnodeoffset, keyoffset, comparekk, augment) }
#define DDSRT_AVL_CTREEDEF_INITIALIZER_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, augment) { DDSRT_AVL_TREEDEF_INITIALIZER_ALLOWDUPS (avlnodeoffset, keyoffset, comparekk, augment) }
#define DDSRT_AVL_CTREEDEF_INITIALIZER_INDKEY_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, augment) { DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY_ALLOWDUPS (avlnodeoffset, keyoffset, comparekk, augment) }
#define DDSRT_AVL_CTREEDEF_INITIALIZER_R(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { DDSRT_AVL_TREEDEF_INITIALIZER_R (avlnodeoffset, keyoffset, comparekk, cmparg, augment) }
#define DDSRT_AVL_CTREEDEF_INITIALIZER_INDKEY_R(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY_R (avlnodeoffset, keyoffset, comparekk, cmparg, augment) }
#define DDSRT_AVL_CTREEDEF_INITIALIZER_R_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { DDSRT_AVL_TREEDEF_INITIALIZER_R_ALLOWDUPS (avlnodeoffset, keyoffset, comparekk, cmparg, augment) }
#define DDSRT_AVL_CTREEDEF_INITIALIZER_INDKEY_R_ALLOWDUPS(avlnodeoffset, keyoffset, comparekk, cmparg, augment) { DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY_R_ALLOWDUPS (avlnodeoffset, keyoffset, comparekk, cmparg, augment) }

/** @brief Counted version of @ref ddsrt_avl_treedef_init*/
DDS_EXPORT void ddsrt_avl_ctreedef_init (ddsrt_avl_ctreedef_t *td, size_t avlnodeoffset, size_t keyoffset, ddsrt_avl_compare_t comparekk, ddsrt_avl_augment_t augment, uint32_t flags) ddsrt_nonnull((1,4));

/** @brief Counted version of @ref ddsrt_avl_treedef_init_r*/
DDS_EXPORT void ddsrt_avl_ctreedef_init_r (ddsrt_avl_ctreedef_t *td, size_t avlnodeoffset, size_t keyoffset, ddsrt_avl_compare_r_t comparekk_r, void *cmp_arg, ddsrt_avl_augment_t augment, uint32_t flags) ddsrt_nonnull((1,4));

/** @brief Counted version of @ref ddsrt_avl_init*/
DDS_EXPORT void ddsrt_avl_cinit (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_free*/
DDS_EXPORT void ddsrt_avl_cfree (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, void (*freefun) (void *vnode)) ddsrt_nonnull((1,2));

/** @brief Counted version of @ref ddsrt_avl_free_arg*/
DDS_EXPORT void ddsrt_avl_cfree_arg (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, void (*freefun) (void *vnode, void *arg), void *arg) ddsrt_nonnull((1,2));

/** @brief Counted version of @ref ddsrt_avl_root*/
DDS_EXPORT void *ddsrt_avl_croot (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_root_non_empty*/
DDS_EXPORT void *ddsrt_avl_croot_non_empty (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_lookup*/
DDS_EXPORT void *ddsrt_avl_clookup (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *key) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_lookup_ipath*/
DDS_EXPORT void *ddsrt_avl_clookup_ipath (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *key, ddsrt_avl_ipath_t *path) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_lookup_dpath*/
DDS_EXPORT void *ddsrt_avl_clookup_dpath (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *key, ddsrt_avl_dpath_t *path) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_lookup_pred_eq*/
DDS_EXPORT void *ddsrt_avl_clookup_pred_eq (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *key) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_lookup_succ_eq*/
DDS_EXPORT void *ddsrt_avl_clookup_succ_eq (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *key) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_lookup_pred*/
DDS_EXPORT void *ddsrt_avl_clookup_pred (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *key) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_lookup_succ*/
DDS_EXPORT void *ddsrt_avl_clookup_succ (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *key) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_insert*/
DDS_EXPORT void ddsrt_avl_cinsert (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, void *vnode) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_delete*/
DDS_EXPORT void ddsrt_avl_cdelete (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, void *vnode) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_insert_ipath*/
DDS_EXPORT void ddsrt_avl_cinsert_ipath (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, void *vnode, ddsrt_avl_ipath_t *path) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_delete_dpath*/
DDS_EXPORT void ddsrt_avl_cdelete_dpath (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, void *vnode, ddsrt_avl_dpath_t *path) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_swap_node*/
DDS_EXPORT void ddsrt_avl_cswap_node (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, void *voldn, void *vnewn) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_augment_update*/
DDS_EXPORT void ddsrt_avl_caugment_update (const ddsrt_avl_ctreedef_t *td, void *vnode) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_is_empty*/
DDS_EXPORT int ddsrt_avl_cis_empty (const ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_is_singleton*/
DDS_EXPORT int ddsrt_avl_cis_singleton (const ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_count*/
DDS_EXPORT size_t ddsrt_avl_ccount (const ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_find_min*/
DDS_EXPORT void *ddsrt_avl_cfind_min (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_find_max*/
DDS_EXPORT void *ddsrt_avl_cfind_max (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_find_pred*/
DDS_EXPORT void *ddsrt_avl_cfind_pred (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *vnode) ddsrt_nonnull((1,2));

/** @brief Counted version of @ref ddsrt_avl_find_succ*/
DDS_EXPORT void *ddsrt_avl_cfind_succ (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *vnode) ddsrt_nonnull((1,2));

/** @brief Counted version of @ref ddsrt_avl_walk*/
DDS_EXPORT void ddsrt_avl_cwalk (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, ddsrt_avl_walk_t f, void *a) ddsrt_nonnull((1,2,3));

/** @brief Counted version of @ref ddsrt_avl_const_walk*/
DDS_EXPORT void ddsrt_avl_cconst_walk (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, ddsrt_avl_const_walk_t f, void *a) ddsrt_nonnull((1,2,3));

/** @brief Counted version of @ref ddsrt_avl_walk_range*/
DDS_EXPORT void ddsrt_avl_cwalk_range (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, const void *min, const void *max, ddsrt_avl_walk_t f, void *a) ddsrt_nonnull((1,2,3,4,5));

/** @brief Counted version of @ref ddsrt_avl_const_walk_range*/
DDS_EXPORT void ddsrt_avl_cconst_walk_range (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *min, const void *max, ddsrt_avl_const_walk_t f, void *a) ddsrt_nonnull((1,2,3,4,5));

/** @brief Counted version of @ref ddsrt_avl_walk_range_reverse*/
DDS_EXPORT void ddsrt_avl_cwalk_range_reverse (const ddsrt_avl_ctreedef_t *td, ddsrt_avl_ctree_t *tree, const void *min, const void *max, ddsrt_avl_walk_t f, void *a) ddsrt_nonnull((1,2,3,4,5));

/** @brief Counted version of @ref ddsrt_avl_const_walk_range_reverse*/
DDS_EXPORT void ddsrt_avl_cconst_walk_range_reverse (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, const void *min, const void *max, ddsrt_avl_const_walk_t f, void *a) ddsrt_nonnull((1,2,3,4,5));

/** @brief Counted version of @ref ddsrt_avl_iter_first*/
DDS_EXPORT void *ddsrt_avl_citer_first (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, ddsrt_avl_citer_t *iter) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_iter_succ_eq*/
DDS_EXPORT void *ddsrt_avl_citer_succ_eq (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, ddsrt_avl_citer_t *iter, const void *key) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_iter_succ*/
DDS_EXPORT void *ddsrt_avl_citer_succ (const ddsrt_avl_ctreedef_t *td, const ddsrt_avl_ctree_t *tree, ddsrt_avl_citer_t *iter, const void *key) ddsrt_nonnull_all;

/** @brief Counted version of @ref ddsrt_avl_iter_next*/
DDS_EXPORT void *ddsrt_avl_citer_next (ddsrt_avl_citer_t *iter) ddsrt_nonnull_all;

#if defined (__cplusplus)
}
#endif

#endif /* DDSRT_AVL_H */
