/*
 * readcon-core C API.
 *
 * Memory ownership
 *   - Opaque handles (RKRConFrame, RKRConFrameWriter, RKRConFrameBuilder,
 *     CConFrameIterator) are caller-owned. Free each with its dedicated
 *     destructor: free_rkr_frame, free_rkr_frame_writer,
 *     free_rkr_frame_builder, free_con_frame_iterator.
 *   - rkr_read_all_frames returns a heap-allocated array of RKRConFrame*
 *     handles together with the frame count via the *num_frames out-param.
 *     The array's capacity equals the count (shrunk to fit). Free with
 *     free_rkr_frame_array(frames, *num_frames). Each individual frame is
 *     released by free_rkr_frame_array; do NOT also call free_rkr_frame.
 *   - char* values returned by rkr_frame_metadata_json,
 *     rkr_frame_potential_type, and rkr_frame_get_header_line_cpp are heap
 *     allocated; free them with rkr_free_string. rkr_free_string is safe
 *     to call with NULL (no-op).
 *   - const char* returned by rkr_library_version is process-static; do
 *     NOT free it.
 *   - rkr_frame_to_c_frame returns a CFrame whose `atoms` array is owned
 *     by the caller; release with free_c_frame.
 *
 * Sentinel values for absent metadata
 *   - Floating-point getters (rkr_frame_energy, rkr_frame_time,
 *     rkr_frame_timestep, etc.) return NaN when the metadata key is
 *     absent. Test with isnan() from <math.h>.
 *   - Unsigned-integer getters (rkr_frame_frame_index, rkr_frame_neb_bead,
 *     rkr_frame_neb_band) return UINT64_MAX when absent.
 *   - String getters return NULL when absent.
 *
 * Thread safety
 *   - Opaque handles are NOT Sync. Do not share a single
 *     RKRConFrame/RKRConFrameWriter/RKRConFrameBuilder across threads
 *     without external synchronization. Distinct handles are independent.
 *   - rkr_read_all_frames internally uses sequential parsing; the
 *     `parallel` Cargo feature is exposed only through the Rust API and
 *     is not surfaced via this C header.
 */

/* Forward-declare the DLPack-managed tensor type for the tier-3
 * builder export FFI. Consumers that want to dereference fields
 * (data, shape, strides, dtype, deleter) must include
 * <dlpack/dlpack.h> (or equivalent) themselves; the readcon-core C
 * ABI only passes the pointer through. */
struct DLManagedTensorVersioned;
typedef struct DLManagedTensorVersioned RKRDLManagedTensorVersioned;


#ifndef READCON_H
#define READCON_H

#pragma once

/* Generated with cbindgen:0.29.4 */

/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */

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

#ifdef __cplusplus
namespace readcon {
#endif  // __cplusplus

/**
 * CON/convel format spec version implemented by this build.
 *
 * - Version 1: column 5 present but semantics undefined. Readers MAY
 *   ignore it. No JSON metadata line.
 * - Version 2: column 5 is the original atom index before type-based
 *   grouping. Readers MUST parse and preserve it. Writers MUST write
 *   the stored value. Line 2 of the header carries a JSON object
 *   with at least `{"con_spec_version": 2}`.
 *
 * See `docs/orgmode/spec.org` for the full specification.
 */
#define CON_SPEC_VERSION 2

/**
 * CON/convel format spec version. Use `#if RKR_CON_SPEC_VERSION >= 2` in C/C++
 * to gate code that depends on atom_index semantics.
 *
 * Tracks `crate::CON_SPEC_VERSION` (which the Rust API exposes as
 * `CON_SPEC_VERSION`). Both macros are emitted into the C header for
 * the convenience of either naming convention; they always carry the
 * same value.
 */
#define RKR_CON_SPEC_VERSION 2

/**
 * Error codes for RKR functions.
 */
typedef enum RKRStatus {
    /**
     * Function completed successfully.
     */
    RKR_STATUS_SUCCESS = 0,
    /**
     * A null pointer was passed for a required argument.
     */
    RKR_STATUS_NULL_POINTER = -1,
    /**
     * An input string was not valid UTF-8.
     */
    RKR_STATUS_INVALID_UTF8 = -2,
    /**
     * JSON parsing or serialization failed.
     */
    RKR_STATUS_INVALID_JSON = -3,
    /**
     * File I/O error.
     */
    RKR_STATUS_IO_ERROR = -4,
    /**
     * Index out of bounds.
     */
    RKR_STATUS_INDEX_OUT_OF_BOUNDS = -5,
    /**
     * The destination buffer cannot hold a null-terminated string.
     */
    RKR_STATUS_BUFFER_TOO_SMALL = -6,
    /**
     * An internal logic error or unhandled state.
     */
    RKR_STATUS_INTERNAL_ERROR = -7,
    /**
     * An optional section (velocities, forces, atom_energies) was
     * requested but is not declared on the builder.
     */
    RKR_STATUS_SECTION_ABSENT = -8,
    /**
     * DLPack export or another validation step failed.
     */
    RKR_STATUS_VALIDATION_ERROR = -9,
    /**
     * Chemfiles selection parse/evaluate failed (requires chemfiles-enabled build).
     */
    RKR_STATUS_SELECTION_ERROR = -10,
} RKRStatus;

/**
 * An iterator that lazily parses simulation frames from a `.con` or `.convel`
 * file's contents.
 *
 * This struct wraps an iterator over the lines of a string and, upon each iteration,
 * attempts to parse a complete `ConFrame`. Velocity sections are detected
 * automatically: if a blank line follows the coordinate blocks, the velocity
 * data is parsed into the atoms.
 *
 * The iterator yields items of type `Result<ConFrame, ParseError>`, allowing for
 * robust error handling for each frame.
 */
typedef struct ConFrameIterator ConFrameIterator;

#if defined(READCON_CORE_HAS_CHEMFILES)
/**
 * Opaque handle for a cached selection evaluation result (chemfiles builds only).
 */
typedef struct RKRSelectionResult RKRSelectionResult;
#endif

typedef struct String String;

/**
 * An opaque handle to a full, lossless Rust `ConFrame` object.
 * The C/C++ side needs to treat this as a void pointer
 */
typedef struct RKRConFrame {
    uint8_t _private[0];
} RKRConFrame;

typedef struct CConFrameIterator {
    struct ConFrameIterator *iterator;
    struct String *file_contents;
} CConFrameIterator;

/**
 * Transparent atom record extracted via [`rkr_frame_to_c_frame`].
 *
 * `is_fixed` is the OR of `fixed_x`, `fixed_y`, `fixed_z`; it is kept
 * for source compatibility with pre-spec-v2 callers that did not have
 * per-axis flags. New code should use the per-axis fields.
 *
 * `vx`/`vy`/`vz`, `fx`/`fy`/`fz`, and `energy` carry meaningful values
 * only when `has_velocity`, `has_forces`, or `has_energy` is true
 * respectively; the values are zeroed otherwise.
 */
typedef struct CAtom {
    uint64_t atomic_number;
    double x;
    double y;
    double z;
    uint64_t atom_id;
    double mass;
    /**
     * True when any of `fixed_x`, `fixed_y`, `fixed_z` is true.
     * Kept for source compatibility; prefer the per-axis fields.
     */
    bool is_fixed;
    bool fixed_x;
    bool fixed_y;
    bool fixed_z;
    double vx;
    double vy;
    double vz;
    bool has_velocity;
    double fx;
    double fy;
    double fz;
    bool has_forces;
    /**
     * Per-atom energy contribution; meaningful only when
     * `has_energy` is true. See [`crate::types::SECTION_ENERGIES`].
     */
    double energy;
    bool has_energy;
} CAtom;

/**
 * A transparent, "lossy" C-struct containing only the core atomic data.
 * This can be extracted from an `RKRConFrame` handle for direct data access.
 * The caller is responsible for freeing the `atoms` array using `free_c_frame`.
 */
typedef struct CFrame {
    struct CAtom *atoms;
    uintptr_t num_atoms;
    double cell[3];
    double angles[3];
    bool has_velocities;
    bool has_forces;
    bool has_energies;
} CFrame;

/**
 * An opaque handle to a Rust `ConFrameWriter` object.
 * The C/C++ side needs to treat this as a void pointer
 */
typedef struct RKRConFrameWriter {
    uint8_t _private[0];
} RKRConFrameWriter;

/**
 * An opaque handle to a Rust `ConFrameBuilder` object.
 */
typedef struct RKRConFrameBuilder {
    uint8_t _private[0];
} RKRConFrameBuilder;

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

/**
 * Returns the spec version at runtime (for dynamically linked consumers).
 */
uint32_t rkr_con_spec_version(void);

/**
 * Returns a pointer to a static, null-terminated library version string.
 * The returned pointer is valid for the lifetime of the process. Do NOT free it.
 */
const char *rkr_library_version(void);

/**
 * Returns the position of an atom inside the frame's `atom_data` array
 * matching the given `atom_id`. Returns `UINT64_MAX` if no atom with
 * that id exists or `frame_handle` is NULL.
 *
 * O(N) per call. C/C++ consumers performing many lookups should cache
 * a `std::unordered_map<uint64_t, size_t>` from a single sweep over
 * the frame.
 *
 * # Safety
 *
 * `frame_handle` must point to a valid `RKRConFrame` allocation.
 */
uint64_t rkr_frame_atom_index_by_id(const struct RKRConFrame *frame_handle,
                                    uint64_t atom_id);

/**
 * Returns the atomic number for a chemical symbol, or 0 if the symbol
 * is unknown or `symbol` is NULL. Lookup covers H..U (Z = 1..=92) and
 * is case-sensitive: "Fe" works, "fe" does not.
 *
 * # Safety
 *
 * `symbol` must be either NULL or a pointer to a NUL-terminated UTF-8
 * C string valid for reads up to the terminating NUL byte.
 */
uint64_t rkr_symbol_to_z(const char *symbol);

/**
 * Returns a pointer to a static, NUL-terminated chemical symbol for an
 * atomic number, or "X" for unknown values. Coverage is H..U
 * (Z = 1..=92). The returned pointer is valid for the lifetime of the
 * process; do NOT free it.
 */
const char *rkr_z_to_symbol(uint64_t z);

/**
 * Returns the spec version stored in a parsed frame's header.
 * Returns 0 on error (null handle).
 */
uint32_t rkr_frame_spec_version(const struct RKRConFrame *frame_handle);

/**
 * Returns the JSON metadata line from a parsed frame as a heap-allocated
 * null-terminated C string. The caller MUST free with `rkr_free_string`.
 * Returns NULL on error.
 *
 * # Safety
 * frame_handle must be valid. The caller takes ownership of the returned string.
 */
char *rkr_frame_metadata_json(const struct RKRConFrame *frame_handle);

/**
 * Returns the per-frame energy from metadata, or NaN if absent.
 */
double rkr_frame_energy(const struct RKRConFrame *frame_handle);

/**
 * Returns the potential type string from metadata as a heap-allocated
 * null-terminated C string. The caller MUST free with `rkr_free_string`.
 * Returns NULL if absent or on error.
 *
 * # Safety
 * frame_handle must be valid. The caller takes ownership of the returned string.
 */
char *rkr_frame_potential_type(const struct RKRConFrame *frame_handle);

/**
 * Returns the zero-based frame index from metadata, or UINT64_MAX if absent.
 */
uint64_t rkr_frame_frame_index(const struct RKRConFrame *frame_handle);

/**
 * Returns the simulation time from metadata, or NaN if absent.
 */
double rkr_frame_time(const struct RKRConFrame *frame_handle);

/**
 * Returns the integration timestep from metadata, or NaN if absent.
 */
double rkr_frame_timestep(const struct RKRConFrame *frame_handle);

/**
 * Returns the NEB bead index from metadata, or UINT64_MAX if absent.
 */
uint64_t rkr_frame_neb_bead(const struct RKRConFrame *frame_handle);

/**
 * Returns the NEB band index from metadata, or UINT64_MAX if absent.
 */
uint64_t rkr_frame_neb_band(const struct RKRConFrame *frame_handle);

/**
 * Number of optional frame topology bonds (`metadata["bonds"]`), or 0 if absent.
 *
 * # Safety
 * `frame_handle` must be a valid handle or NULL.
 */
uint64_t rkr_frame_bond_count(const struct RKRConFrame *frame_handle);

/**
 * Read one bond at `index` (0-based into the `bonds` metadata array).
 *
 * Writes 0-based `atom_data` indices into `out_i` / `out_j`. When the bond
 * has an explicit order, sets `out_has_order` to 1 and `out_order` to that
 * integer; otherwise `out_has_order` is 0.
 *
 * # Safety
 * `frame_handle` must be valid. Output pointers must be non-null.
 */
enum RKRStatus rkr_frame_bond_at(const struct RKRConFrame *frame_handle,
                                 uint64_t index,
                                 uint32_t *out_i,
                                 uint32_t *out_j,
                                 uint8_t *out_has_order,
                                 int32_t *out_order);

/**
 * Returns a stable, static message for a status code.
 * The returned pointer is valid for the lifetime of the process. Do NOT free it.
 */
const char *rkr_status_message(enum RKRStatus status);

/**
 * Creates a new iterator for a .con or .convel file.
 *
 * Returns NULL if the file cannot be read (missing, unreadable, or
 * not valid UTF-8). A successfully-opened file with zero frames
 * returns a non-NULL iterator that yields NULL on the first call to
 * [`con_frame_iterator_next`]. The caller OWNS the returned pointer
 * and MUST call [`free_con_frame_iterator`].
 *
 * # Safety
 * filename_c must be a valid null-terminated string. The caller takes
 * ownership of the returned iterator.
 */
struct CConFrameIterator *read_con_file_iterator(const char *filename_c);

/**
 * Reads the next frame from the iterator, returning an opaque handle.
 * The caller OWNS the returned handle and must free it with `free_rkr_frame`.
 *
 * # Safety
 * iterator must be valid. The caller takes ownership of the returned frame.
 */
struct RKRConFrame *con_frame_iterator_next(struct CConFrameIterator *iterator);

/**
 * Frees the memory for an opaque `RKRConFrame` handle.
 *
 * # Safety
 * frame_handle must be valid or null.
 */
void free_rkr_frame(struct RKRConFrame *frame_handle);

/**
 * Frees the memory for a `CConFrameIterator`.
 *
 * # Safety
 * iterator must be valid or null.
 */
void free_con_frame_iterator(struct CConFrameIterator *iterator);

/**
 * Extracts the core atomic data into a transparent `CFrame` struct.
 * The caller OWNS the returned pointer and MUST call `free_c_frame` on it.
 *
 * # Safety
 * frame_handle must be valid. The caller takes ownership of the returned CFrame.
 */
struct CFrame *rkr_frame_to_c_frame(const struct RKRConFrame *frame_handle);

/**
 * Frees the memory of a `CFrame` struct, including its internal atoms array.
 *
 * # Safety
 * frame must be valid or null.
 */
void free_c_frame(struct CFrame *frame);

/**
 * Copies a header string line into a caller-provided buffer.
 *
 * `is_prebox=true` selects from the two prebox lines (line 0 = user
 * text, line 1 = JSON metadata); `false` selects from the two postbox
 * lines. Strings longer than `buffer_len - 1` bytes are truncated; the
 * final byte is always set to NUL.
 *
 * Returns `RKR_STATUS_SUCCESS` on success,
 * `RKR_STATUS_INDEX_OUT_OF_BOUNDS` if `line_index >= 2`,
 * `RKR_STATUS_NULL_POINTER` if `frame_handle` or `buffer` is NULL,
 * `RKR_STATUS_BUFFER_TOO_SMALL` if `buffer_len == 0`.
 *
 * Pair with [`rkr_frame_get_header_line_cpp`] when the caller prefers
 * an allocated string with no fixed length cap; that variant returns
 * NULL for the same out-of-bounds condition.
 *
 * # Safety
 * frame_handle must be valid. buffer must be at least buffer_len bytes.
 */
enum RKRStatus rkr_frame_get_header_line(const struct RKRConFrame *frame_handle,
                                         bool is_prebox,
                                         uintptr_t line_index,
                                         char *buffer,
                                         uintptr_t buffer_len);

/**
 * Gets a header string line as a newly allocated, null-terminated C string.
 *
 * The caller OWNS the returned pointer and MUST call `rkr_free_string`
 * on it to prevent a memory leak. Returns NULL on error or if the
 * index is invalid (use [`rkr_frame_get_header_line`] when a status
 * code is preferred to NULL-vs-success disambiguation).
 *
 * The `_cpp` suffix is historical; the function is callable from both
 * C and C++.
 *
 * # Safety
 * frame_handle must be valid. The caller takes ownership of the returned string.
 */
char *rkr_frame_get_header_line_cpp(const struct RKRConFrame *frame_handle,
                                    bool is_prebox,
                                    uintptr_t line_index);

/**
 * Frees a C string that was allocated by Rust (e.g., from
 * `rkr_frame_metadata_json`, `rkr_frame_potential_type`, or
 * `rkr_frame_get_header_line_cpp`). Safe to call with NULL (no-op).
 *
 * # Safety
 * s must be either NULL or a pointer previously returned by an
 * allocating Rust FFI function in this crate.
 */
void rkr_free_string(char *s);

/**
 * Creates a new frame writer for the specified file.
 * The caller OWNS the returned pointer and MUST call `free_rkr_writer`.
 *
 * # Safety
 * filename_c must be valid. The caller takes ownership of the returned writer.
 */
struct RKRConFrameWriter *create_writer_from_path_c(const char *filename_c);

/**
 * Frees the memory for an `RKRConFrameWriter`, closing the associated file.
 *
 * # Safety
 * writer_handle must be valid or null.
 */
void free_rkr_writer(struct RKRConFrameWriter *writer_handle);

/**
 * Writes multiple frames from an array of handles to the file managed by the writer.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * writer_handle and frame_handles must be valid.
 */
enum RKRStatus rkr_writer_extend(struct RKRConFrameWriter *writer_handle,
                                 const struct RKRConFrame *const *frame_handles,
                                 uintptr_t num_frames);

/**
 * Creates a new frame writer with custom floating-point precision.
 * The caller OWNS the returned pointer and MUST call `free_rkr_writer`.
 *
 * # Safety
 * filename_c must be valid. The caller takes ownership of the returned writer.
 */
struct RKRConFrameWriter *create_writer_from_path_with_precision_c(const char *filename_c,
                                                                   uint8_t precision);

/**
 * Attaches a velocity vector to the most recently added atom on a builder.
 * No-op if no atom has been added yet.
 *
 * # Safety
 * builder_handle must be valid. velocity must point to 3 contiguous f64 values.
 */
enum RKRStatus rkr_frame_builder_set_last_velocity(struct RKRConFrameBuilder *builder_handle,
                                                   const double *velocity);

/**
 * Attaches a force vector to the most recently added atom on a builder.
 * No-op if no atom has been added yet.
 *
 * # Safety
 * builder_handle must be valid. force must point to 3 contiguous f64 values.
 */
enum RKRStatus rkr_frame_builder_set_last_force(struct RKRConFrameBuilder *builder_handle,
                                                const double *force);

/**
 * Attaches a per-atom energy to the most recently added atom on a
 * builder. No-op if no atom has been added yet.
 *
 * Use this together with the per-frame `energy` metadata key when a
 * caller wants to round-trip an "Energies of Component" decomposition
 * alongside the total.
 *
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_set_last_energy(struct RKRConFrameBuilder *builder_handle,
                                                 double energy);

/**
 * Returns the number of atoms currently held in the builder.
 *
 * # Safety
 * builder_handle must be a valid pointer returned by rkr_frame_new and
 * not yet consumed by rkr_frame_builder_build / freed.
 * Returns 0 on NULL handle.
 */
uintptr_t rkr_frame_builder_atom_count(const struct RKRConFrameBuilder *builder_handle);

/**
 * Updates the Cartesian position of an existing atom.
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_set_atom_position(struct RKRConFrameBuilder *builder_handle,
                                                   uintptr_t index,
                                                   double x,
                                                   double y,
                                                   double z);

/**
 * Sets the velocity vector of an existing atom from 3 contiguous f64 values.
 * # Safety
 * builder_handle must be valid; velocity must point to 3 contiguous f64.
 */
enum RKRStatus rkr_frame_builder_set_atom_velocity(struct RKRConFrameBuilder *builder_handle,
                                                   uintptr_t index,
                                                   const double *velocity);

/**
 * Sets the force vector of an existing atom from 3 contiguous f64 values.
 * # Safety
 * builder_handle must be valid; force must point to 3 contiguous f64.
 */
enum RKRStatus rkr_frame_builder_set_atom_force(struct RKRConFrameBuilder *builder_handle,
                                                uintptr_t index,
                                                const double *force);

/**
 * Sets the per-atom energy contribution of an existing atom.
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_set_atom_energy(struct RKRConFrameBuilder *builder_handle,
                                                 uintptr_t index,
                                                 double energy);

/**
 * Updates per-direction fixed flags `[fixed_x, fixed_y, fixed_z]`.
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_set_atom_fixed(struct RKRConFrameBuilder *builder_handle,
                                                uintptr_t index,
                                                bool fixed_x,
                                                bool fixed_y,
                                                bool fixed_z);

/**
 * Updates the mass of an existing atom.
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_set_atom_mass(struct RKRConFrameBuilder *builder_handle,
                                               uintptr_t index,
                                               double mass);

/**
 * Updates the atom_id (pre-grouping index from .con column 5) of an
 * existing atom. The underlying `Array1<u64>` buffer pointer stays
 * stable; callers that hold a raw `*const u64` via
 * `rkr_frame_builder_atom_ids_data` do not need to refresh after this.
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_set_atom_id(struct RKRConFrameBuilder *builder_handle,
                                             uintptr_t index,
                                             uint64_t atom_id);

/**
 * Removes velocity / force / energy data from an existing atom.
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_clear_atom_velocity(struct RKRConFrameBuilder *builder_handle,
                                                     uintptr_t index);

/**
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_clear_atom_force(struct RKRConFrameBuilder *builder_handle,
                                                  uintptr_t index);

/**
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_clear_atom_energy(struct RKRConFrameBuilder *builder_handle,
                                                   uintptr_t index);

/**
 * Bulk-update positions for every atom from a flat row-major
 * `[x0,y0,z0,x1,y1,z1,...]` buffer of length `3 * atom_count()`.
 * # Safety
 * builder_handle must be valid; positions must point to `3 * len` f64.
 */
enum RKRStatus rkr_frame_builder_set_positions_from_flat(struct RKRConFrameBuilder *builder_handle,
                                                         const double *positions,
                                                         uintptr_t len);

/**
 * Bulk-update forces for every atom.
 * # Safety
 * builder_handle must be valid; forces must point to `3 * len` f64.
 */
enum RKRStatus rkr_frame_builder_set_forces_from_flat(struct RKRConFrameBuilder *builder_handle,
                                                      const double *forces,
                                                      uintptr_t len);

/**
 * Bulk-update per-atom energies (one f64 per atom).
 * # Safety
 * builder_handle must be valid; energies must point to `len` f64.
 */
enum RKRStatus rkr_frame_builder_set_atom_energies_from_flat(struct RKRConFrameBuilder *builder_handle,
                                                             const double *energies,
                                                             uintptr_t len);

/**
 * Reads the position of an existing atom into 3 contiguous f64 out values.
 * # Safety
 * builder_handle must be valid; out_xyz must point to 3 writable f64.
 */
enum RKRStatus rkr_frame_builder_get_atom_position(const struct RKRConFrameBuilder *builder_handle,
                                                   uintptr_t index,
                                                   double *out_xyz);

/**
 * Reads the velocity / force vector of an atom (if any) into 3 contiguous
 * f64. `*has_value` is set to `true` if the atom carries that vector,
 * `false` if it does not (in which case `out_xyz` is left untouched).
 *
 * # Safety
 * builder_handle, out_xyz, has_value must all be valid pointers.
 */
enum RKRStatus rkr_frame_builder_get_atom_velocity(const struct RKRConFrameBuilder *builder_handle,
                                                   uintptr_t index,
                                                   double *out_xyz,
                                                   bool *has_value);

/**
 * # Safety
 * builder_handle, out_xyz, has_value must all be valid pointers.
 */
enum RKRStatus rkr_frame_builder_get_atom_force(const struct RKRConFrameBuilder *builder_handle,
                                                uintptr_t index,
                                                double *out_xyz,
                                                bool *has_value);

/**
 * Reads the per-atom energy of an atom (if any). `*has_value` is set to
 * `true` if the atom carries an energy contribution, else `false` and
 * `*out_value` is left untouched.
 * # Safety
 * builder_handle, out_value, has_value must all be valid pointers.
 */
enum RKRStatus rkr_frame_builder_get_atom_energy(const struct RKRConFrameBuilder *builder_handle,
                                                 uintptr_t index,
                                                 double *out_value,
                                                 bool *has_value);

/**
 * Reads the mass of an existing atom.
 * # Safety
 * builder_handle and out_mass must be valid pointers.
 */
enum RKRStatus rkr_frame_builder_get_atom_mass(const struct RKRConFrameBuilder *builder_handle,
                                               uintptr_t index,
                                               double *out_mass);

/**
 * Export builder positions as a DLPack-managed tensor.
 *
 * On success the caller-supplied `*out_tensor` is set to a newly-
 * allocated `DLManagedTensorVersioned*` that owns a clone of the
 * builder's `(N, 3) f64` row-major positions buffer. The caller MUST
 * invoke `(*out_tensor)->deleter(*out_tensor)` to release it.
 *
 * # Safety
 * `builder_handle` must be a valid builder handle; `out_tensor` must
 * be a valid pointer to a writable `*mut DLManagedTensorVersioned`.
 */
enum RKRStatus rkr_frame_builder_positions_dlpack(const struct RKRConFrameBuilder *builder_handle,
                                                  RKRDLManagedTensorVersioned **out_tensor);

/**
 * Export builder velocities as a DLPack-managed tensor.
 *
 * Returns `RKR_STATUS_SECTION_ABSENT` if the velocities section is not
 * declared; otherwise `(N, 3) f64`. See positions_dlpack for ownership
 * semantics.
 *
 * # Safety
 * `builder_handle` must be a valid builder handle; `out_tensor` must
 * be a valid pointer to a writable `*mut DLManagedTensorVersioned`.
 */
enum RKRStatus rkr_frame_builder_velocities_dlpack(const struct RKRConFrameBuilder *builder_handle,
                                                   RKRDLManagedTensorVersioned **out_tensor);

/**
 * Export builder forces as a DLPack-managed tensor.
 *
 * Returns `RKR_STATUS_SECTION_ABSENT` if the forces section is not
 * declared.
 *
 * # Safety
 * `builder_handle` must be a valid builder handle; `out_tensor` must
 * be a valid pointer to a writable `*mut DLManagedTensorVersioned`.
 */
enum RKRStatus rkr_frame_builder_forces_dlpack(const struct RKRConFrameBuilder *builder_handle,
                                               RKRDLManagedTensorVersioned **out_tensor);

/**
 * Export builder per-atom energies as a DLPack-managed tensor.
 *
 * Returns `RKR_STATUS_SECTION_ABSENT` if the energies section is not
 * declared; otherwise `(N,) f64`.
 *
 * # Safety
 * `builder_handle` must be a valid builder handle; `out_tensor` must
 * be a valid pointer to a writable `*mut DLManagedTensorVersioned`.
 */
enum RKRStatus rkr_frame_builder_atom_energies_dlpack(const struct RKRConFrameBuilder *builder_handle,
                                                      RKRDLManagedTensorVersioned **out_tensor);

/**
 * Export builder per-atom masses as a DLPack-managed tensor `(N,) f64`.
 *
 * # Safety
 * `builder_handle` must be a valid builder handle; `out_tensor` must
 * be a valid pointer to a writable `*mut DLManagedTensorVersioned`.
 */
enum RKRStatus rkr_frame_builder_masses_dlpack(const struct RKRConFrameBuilder *builder_handle,
                                               RKRDLManagedTensorVersioned **out_tensor);

/**
 * Export builder per-atom ids as a DLPack-managed tensor `(N,) u64`.
 *
 * # Safety
 * `builder_handle` must be a valid builder handle; `out_tensor` must
 * be a valid pointer to a writable `*mut DLManagedTensorVersioned`.
 */
enum RKRStatus rkr_frame_builder_atom_ids_dlpack(const struct RKRConFrameBuilder *builder_handle,
                                                 RKRDLManagedTensorVersioned **out_tensor);

/**
 * Borrow the positions buffer as a raw `(N, 3) f64` row-major pointer.
 * Returns NULL on invalid handle. Pointer is valid until the builder
 * is dropped or `add_atom` reallocates.
 *
 * # Safety
 * builder_handle must be valid; the returned pointer must not be
 * dereferenced after a call to add_atom on the same builder.
 */
double *rkr_frame_builder_positions_data(struct RKRConFrameBuilder *builder_handle);

/**
 * Borrow the velocities buffer as a raw `(N, 3) f64` row-major pointer.
 * Returns NULL if the velocities section is absent or the handle is
 * invalid.
 *
 * # Safety
 * Same contract as rkr_frame_builder_positions_data.
 */
double *rkr_frame_builder_velocities_data(struct RKRConFrameBuilder *builder_handle);

/**
 * Borrow the forces buffer as a raw `(N, 3) f64` row-major pointer.
 * Returns NULL if the forces section is absent.
 *
 * # Safety
 * Same contract as rkr_frame_builder_positions_data.
 */
double *rkr_frame_builder_forces_data(struct RKRConFrameBuilder *builder_handle);

/**
 * Borrow the per-atom energies buffer as a raw `(N,) f64` pointer.
 * Returns NULL if the energies section is absent.
 *
 * # Safety
 * Same contract as rkr_frame_builder_positions_data.
 */
double *rkr_frame_builder_atom_energies_data(struct RKRConFrameBuilder *builder_handle);

/**
 * Borrow the per-atom masses buffer as a raw `(N,) f64` pointer.
 *
 * # Safety
 * Same contract as rkr_frame_builder_positions_data.
 */
double *rkr_frame_builder_masses_data(struct RKRConFrameBuilder *builder_handle);

/**
 * Borrow the per-atom atom_ids buffer as a raw `(N,) u64` pointer.
 *
 * # Safety
 * Same contract as rkr_frame_builder_positions_data.
 */
const uint64_t *rkr_frame_builder_atom_ids_data(const struct RKRConFrameBuilder *builder_handle);

/**
 * Adds an atom with optional per-axis fixed mask, velocity, and force vectors.
 *
 * `velocity` and `force` are pointers to 3 contiguous f64 values, or NULL if
 * absent. This is the unified entry point that replaces the eight
 * `rkr_frame_add_atom_*` convenience functions; callers may continue using
 * those for source compatibility.
 *
 * # Safety
 * builder_handle and symbol must be valid. velocity (if non-null) must point
 * to 3 contiguous f64 values, and force (if non-null) likewise.
 */
enum RKRStatus rkr_frame_add_atom_full(struct RKRConFrameBuilder *builder_handle,
                                       const char *symbol,
                                       double x,
                                       double y,
                                       double z,
                                       bool fixed_x,
                                       bool fixed_y,
                                       bool fixed_z,
                                       uint64_t atom_id,
                                       double mass,
                                       const double *velocity,
                                       const double *force);

/**
 * Creates a new frame builder with the given cell dimensions, angles,
 * and header lines.
 *
 * `prebox1` is accepted for source compatibility but ignored: the
 * JSON metadata line is regenerated by the writer from the builder's
 * `spec_version`, `metadata`, and `sections`. Pass NULL or any string.
 * The caller OWNS the returned pointer and MUST call
 * `free_rkr_frame_builder` or consume it via `rkr_frame_builder_build`.
 * Returns NULL on error.
 *
 * # Safety
 * cell and angles must point to 3 doubles. prebox0, postbox0, and
 * postbox1 must be NULL or valid null-terminated strings; prebox1 is
 * not dereferenced. The caller takes ownership of the returned
 * builder.
 */
struct RKRConFrameBuilder *rkr_frame_new(const double *cell,
                                         const double *angles,
                                         const char *prebox0,
                                         const char *prebox1,
                                         const char *postbox0,
                                         const char *postbox1);

/**
 * Parses and sets JSON metadata on an existing frame builder.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle and metadata_json must be valid.
 */
enum RKRStatus rkr_frame_builder_set_metadata_json(struct RKRConFrameBuilder *builder_handle,
                                                   const char *metadata_json);

/**
 * Sets a numeric metadata key on an existing frame builder.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle and key must be valid.
 */
enum RKRStatus rkr_frame_builder_set_scalar_metadata(struct RKRConFrameBuilder *builder_handle,
                                                     const char *key,
                                                     double value);

/**
 * Sets a string metadata key on an existing frame builder.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle, key, and value must be valid.
 */
enum RKRStatus rkr_frame_builder_set_string_metadata(struct RKRConFrameBuilder *builder_handle,
                                                     const char *key,
                                                     const char *value);

/**
 * Sets the per-frame total energy metadata on an existing frame builder.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_set_energy(struct RKRConFrameBuilder *builder_handle,
                                            double energy);

/**
 * Sets the zero-based frame index metadata on an existing frame builder.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_set_frame_index(struct RKRConFrameBuilder *builder_handle,
                                                 uint64_t idx);

/**
 * Sets the simulation time metadata on an existing frame builder.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_set_time(struct RKRConFrameBuilder *builder_handle,
                                          double time);

/**
 * Sets the timestep metadata on an existing frame builder.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_set_timestep(struct RKRConFrameBuilder *builder_handle,
                                              double dt);

/**
 * Sets the NEB bead index metadata on an existing frame builder.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_set_neb_bead(struct RKRConFrameBuilder *builder_handle,
                                              uint64_t bead);

/**
 * Sets the NEB band index metadata on an existing frame builder.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle must be valid.
 */
enum RKRStatus rkr_frame_builder_set_neb_band(struct RKRConFrameBuilder *builder_handle,
                                              uint64_t band);

/**
 * **Deprecated**: prefer `rkr_frame_add_atom_full` with NULL velocity
 * and force pointers. Adds an atom (no velocity, no forces) to the
 * builder using a single uniform fixed flag.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle and symbol must be valid.
 */
enum RKRStatus rkr_frame_add_atom(struct RKRConFrameBuilder *builder_handle,
                                  const char *symbol,
                                  double x,
                                  double y,
                                  double z,
                                  bool is_fixed,
                                  uint64_t atom_id,
                                  double mass);

/**
 * **Deprecated**: prefer `rkr_frame_add_atom_full`. Adds an atom (no
 * velocity, no forces) using per-axis fixed flags.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle and symbol must be valid.
 */
enum RKRStatus rkr_frame_add_atom_with_fixed_mask(struct RKRConFrameBuilder *builder_handle,
                                                  const char *symbol,
                                                  double x,
                                                  double y,
                                                  double z,
                                                  bool fixed_x,
                                                  bool fixed_y,
                                                  bool fixed_z,
                                                  uint64_t atom_id,
                                                  double mass);

/**
 * **Deprecated**: prefer `rkr_frame_add_atom_full`. Adds an atom with
 * a velocity vector and a single uniform fixed flag.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle and symbol must be valid.
 */
enum RKRStatus rkr_frame_add_atom_with_velocity(struct RKRConFrameBuilder *builder_handle,
                                                const char *symbol,
                                                double x,
                                                double y,
                                                double z,
                                                bool is_fixed,
                                                uint64_t atom_id,
                                                double mass,
                                                double vx,
                                                double vy,
                                                double vz);

/**
 * **Deprecated**: prefer `rkr_frame_add_atom_full`. Adds an atom with
 * a velocity vector and per-axis fixed flags.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle and symbol must be valid.
 */
enum RKRStatus rkr_frame_add_atom_with_velocity_fixed_mask(struct RKRConFrameBuilder *builder_handle,
                                                           const char *symbol,
                                                           double x,
                                                           double y,
                                                           double z,
                                                           bool fixed_x,
                                                           bool fixed_y,
                                                           bool fixed_z,
                                                           uint64_t atom_id,
                                                           double mass,
                                                           double vx,
                                                           double vy,
                                                           double vz);

/**
 * **Deprecated**: prefer `rkr_frame_add_atom_full`. Adds an atom with
 * a force vector and a single uniform fixed flag.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle and symbol must be valid.
 */
enum RKRStatus rkr_frame_add_atom_with_forces(struct RKRConFrameBuilder *builder_handle,
                                              const char *symbol,
                                              double x,
                                              double y,
                                              double z,
                                              bool is_fixed,
                                              uint64_t atom_id,
                                              double mass,
                                              double fx,
                                              double fy,
                                              double fz);

/**
 * **Deprecated**: prefer `rkr_frame_add_atom_full`. Adds an atom with
 * a force vector and per-axis fixed flags.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle and symbol must be valid.
 */
enum RKRStatus rkr_frame_add_atom_with_forces_fixed_mask(struct RKRConFrameBuilder *builder_handle,
                                                         const char *symbol,
                                                         double x,
                                                         double y,
                                                         double z,
                                                         bool fixed_x,
                                                         bool fixed_y,
                                                         bool fixed_z,
                                                         uint64_t atom_id,
                                                         double mass,
                                                         double fx,
                                                         double fy,
                                                         double fz);

/**
 * **Deprecated**: prefer `rkr_frame_add_atom_full`. Adds an atom with
 * both velocity and force vectors and a single uniform fixed flag.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle and symbol must be valid.
 */
enum RKRStatus rkr_frame_add_atom_with_velocity_and_forces(struct RKRConFrameBuilder *builder_handle,
                                                           const char *symbol,
                                                           double x,
                                                           double y,
                                                           double z,
                                                           bool is_fixed,
                                                           uint64_t atom_id,
                                                           double mass,
                                                           double vx,
                                                           double vy,
                                                           double vz,
                                                           double fx,
                                                           double fy,
                                                           double fz);

/**
 * **Deprecated**: prefer `rkr_frame_add_atom_full`. Adds an atom with
 * both velocity and force vectors and per-axis fixed flags.
 * Returns `RKR_STATUS_SUCCESS` on success, or an error code.
 *
 * # Safety
 * builder_handle and symbol must be valid.
 */
enum RKRStatus rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(struct RKRConFrameBuilder *builder_handle,
                                                                      const char *symbol,
                                                                      double x,
                                                                      double y,
                                                                      double z,
                                                                      bool fixed_x,
                                                                      bool fixed_y,
                                                                      bool fixed_z,
                                                                      uint64_t atom_id,
                                                                      double mass,
                                                                      double vx,
                                                                      double vy,
                                                                      double vz,
                                                                      double fx,
                                                                      double fy,
                                                                      double fz);

/**
 * Consumes the builder and returns a finalized RKRConFrame handle.
 * The builder handle is invalidated after this call.
 * The caller OWNS the returned frame and MUST call `free_rkr_frame`.
 * Returns NULL on error.
 *
 * # Safety
 * builder_handle must be valid. The caller takes ownership of the returned frame.
 */
struct RKRConFrame *rkr_frame_builder_build(struct RKRConFrameBuilder *builder_handle);

/**
 * Frees a frame builder without building.
 *
 * # Safety
 * builder_handle must be valid or null.
 */
void free_rkr_frame_builder(struct RKRConFrameBuilder *builder_handle);

/**
 * Cheap, copy-on-write clone of a frame builder. Returned handle owns
 * a new `ConFrameBuilder` whose per-atom buffers share storage with
 * the source via ArcArray; any subsequent mutation triggers a
 * per-buffer copy-on-write so writes do not leak across clones.
 *
 * Intended for downstream consumers (NEB image bulk allocation,
 * trajectory snapshots) that need many builders carrying the same
 * per-atom data without paying N copies up-front. Returns NULL on
 * NULL input.
 *
 * The caller OWNS the returned handle and MUST call
 * `free_rkr_frame_builder` (or consume via `rkr_frame_builder_build`).
 *
 * # Safety
 * `builder_handle` must be a valid pointer returned by `rkr_frame_new`
 * (or by an earlier `rkr_frame_builder_clone`) and not yet freed.
 */
struct RKRConFrameBuilder *rkr_frame_builder_clone(const struct RKRConFrameBuilder *builder_handle);

/**
 * Creates a new gzip-compressed frame writer for the specified file.
 * The caller OWNS the returned pointer and MUST call `free_rkr_writer`.
 *
 * # Safety
 * filename_c must be valid. The caller takes ownership of the returned writer.
 */
struct RKRConFrameWriter *create_writer_gzip_c(const char *filename_c);

/**
 * Creates a gzip-compressed frame writer with a custom floating-point
 * precision. The caller OWNS the returned pointer and MUST call
 * `free_rkr_writer`.
 *
 * # Safety
 * filename_c must be valid. The caller takes ownership of the returned writer.
 */
struct RKRConFrameWriter *create_writer_gzip_with_precision_c(const char *filename_c,
                                                              uint8_t precision);

#if defined(READCON_CORE_HAS_ZSTD)
/**
 * Creates a new zstd-compressed frame writer for the specified file.
 * The caller OWNS the returned pointer and MUST call `free_rkr_writer`.
 *
 * Only present when readcon-core is built with the `zstd` Cargo
 * feature; the C header guards the declaration with
 * `READCON_CORE_HAS_ZSTD`.
 *
 * # Safety
 * filename_c must be valid. The caller takes ownership of the returned writer.
 */
struct RKRConFrameWriter *create_writer_zstd_c(const char *filename_c);
#endif

#if defined(READCON_CORE_HAS_ZSTD)
/**
 * Creates a zstd-compressed frame writer with a custom floating-point
 * precision. The caller OWNS the returned pointer and MUST call
 * `free_rkr_writer`.
 *
 * Only present when readcon-core is built with the `zstd` Cargo
 * feature; the C header guards the declaration with
 * `READCON_CORE_HAS_ZSTD`.
 *
 * # Safety
 * filename_c must be valid. The caller takes ownership of the returned writer.
 */
struct RKRConFrameWriter *create_writer_zstd_with_precision_c(const char *filename_c,
                                                              uint8_t precision);
#endif

/**
 * Reads the first frame from a .con file.
 * Uses `read_to_string` for small files (< 64 KiB) and mmap for larger ones.
 * Stops after the first frame rather than parsing the entire file.
 * The caller OWNS the returned handle and MUST call `free_rkr_frame`.
 * Returns NULL on error.
 *
 * # Safety
 * filename_c must be valid. The caller takes ownership of the returned frame.
 */
struct RKRConFrame *rkr_read_first_frame(const char *filename_c);

/**
 * Reads all frames from a .con file using mmap.
 * Returns an array of frame handles and sets `num_frames` to the count.
 * The caller OWNS both the array and each frame handle.
 * Free frames with `free_rkr_frame` and the array with `free_rkr_frame_array`.
 * Returns NULL on error.
 *
 * # Safety
 * filename_c and num_frames must be valid. The caller takes ownership of the returned handles and array.
 */
struct RKRConFrame **rkr_read_all_frames(const char *filename_c,
                                         uintptr_t *num_frames);

/**
 * Frees an array of frame handles returned by `rkr_read_all_frames`.
 * Each frame is freed individually, then the array itself.
 *
 * # Safety
 * frames must be valid or null.
 */
void free_rkr_frame_array(struct RKRConFrame **frames, uintptr_t num_frames);

#if defined(READCON_CORE_HAS_CHEMFILES)
/**
 * Evaluate a chemfiles selection-language string on an `RKRConFrame`.
 *
 * On success writes a heap-allocated result handle to `*out_result` (caller
 * frees with [`rkr_selection_result_free`]). Returns
 * `RKR_STATUS_SELECTION_ERROR` for invalid grammar or evaluation failure.
 *
 * Only available when the library is built with the `chemfiles` feature.
 *
 * # Safety
 * `frame_handle`, `selection`, and `out_result` must be non-null; `selection`
 * must point to a valid UTF-8 C string.
 */
enum RKRStatus rkr_frame_select(const struct RKRConFrame *frame_handle,
                                const char *selection,
                                struct RKRSelectionResult **out_result);
#endif

#if defined(READCON_CORE_HAS_CHEMFILES)
/**
 * Number of matches in a selection result.
 *
 * # Safety
 * `result_handle` must be a valid handle from [`rkr_frame_select`] or NULL.
 */
uint64_t rkr_selection_result_match_count(const struct RKRSelectionResult *result_handle);
#endif

#if defined(READCON_CORE_HAS_CHEMFILES)
/**
 * Selection context size (1=atom, 2=pair, 3=angle, 4=dihedral).
 *
 * # Safety
 * `result_handle` must be valid or NULL (returns 0).
 */
uint32_t rkr_selection_result_context_size(const struct RKRSelectionResult *result_handle);
#endif

#if defined(READCON_CORE_HAS_CHEMFILES)
/**
 * Copy match `match_index` atom indices into `out_atoms` (up to 4 slots).
 * Writes actual arity to `*out_size` when non-null.
 *
 * # Safety
 * Handles and `out_atoms` must be valid; `out_atoms` needs space for 4 `uint64_t`.
 */
enum RKRStatus rkr_selection_result_match_at(const struct RKRSelectionResult *result_handle,
                                             uint64_t match_index,
                                             uint64_t *out_atoms,
                                             uint32_t *out_size);
#endif

#if defined(READCON_CORE_HAS_CHEMFILES)
/**
 * Fill `out_indices` with primary atom indices for each match (length =
 * match count). Returns `RKR_STATUS_BUFFER_TOO_SMALL` if `capacity` is too small.
 *
 * # Safety
 * `result_handle` and `out_indices` must be valid when capacity > 0.
 */
enum RKRStatus rkr_selection_result_primary_indices(const struct RKRSelectionResult *result_handle,
                                                    uint64_t *out_indices,
                                                    uint64_t capacity,
                                                    uint64_t *out_written);
#endif

#if defined(READCON_CORE_HAS_CHEMFILES)
/**
 * Free a selection result from [`rkr_frame_select`]. Safe with NULL.
 *
 * # Safety
 * `result_handle` must be from `rkr_frame_select` or NULL.
 */
void rkr_selection_result_free(struct RKRSelectionResult *result_handle);
#endif

/**
 * Returns 1 when this library build includes chemfiles selection support.
 */
uint8_t rkr_has_chemfiles_support(void);

#ifdef __cplusplus
}  // extern "C"
#endif  // __cplusplus

#ifdef __cplusplus
}  // namespace readcon
#endif  // __cplusplus

#endif  /* READCON_H */
