SEAL_C_FUNC Ciphertext_Create5(void *context, uint64_t *parms_id, uint64_t capacity, void *pool, void **ciphertext)

// SEALNet
#include "seal/c/ciphertext.h"
#include "seal/c/utilities.h"

// SEAL
#include "seal/ciphertext.h"

using namespace std;
using namespace seal;
using namespace seal::c;

// Enables access to private members of seal::Ciphertext.
using ph = struct seal::Ciphertext::CiphertextPrivateHelper
{
    static void resize(Ciphertext *ciphertext, size_t size, size_t poly_modulus_degree, size_t coeff_modulus_size)
    {
        ciphertext->resize_internal(size, poly_modulus_degree, coeff_modulus_size);
    }

    static void set_ntt_form(Ciphertext *ciphertext, bool is_ntt_form)
    {
        ciphertext->is_ntt_form_ = is_ntt_form;
    }
};

SEAL_C_FUNC Ciphertext_Create1(void *memoryPoolHandle, void **ciphertext)
{
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> handle = MemHandleFromVoid(memoryPoolHandle);

    try
    {
        Ciphertext *cipher = new Ciphertext(*handle);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Create2(void *copy, void **ciphertext)
{
    Ciphertext *copyptr = FromVoid<Ciphertext>(copy);
    IfNullRet(copyptr, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);

    Ciphertext *cipher = new Ciphertext(*copyptr);
    *ciphertext = cipher;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Create3(void *context, void *pool, void **ciphertext)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> pool_ptr = MemHandleFromVoid(pool);

    try
    {
        Ciphertext *cipher = new Ciphertext(*ctx, *pool_ptr);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Create4(void *context, uint64_t *parms_id, void *pool, void **ciphertext)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    IfNullRet(parms_id, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> pool_ptr = MemHandleFromVoid(pool);

    try
    {
        parms_id_type parmsid;
        CopyParmsId(parms_id, parmsid);

        Ciphertext *cipher = new Ciphertext(*ctx, parmsid, *pool_ptr);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    IfNullRet(parms_id, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> pool_ptr = MemHandleFromVoid(pool);

    try
    {
        parms_id_type parmsid;
        CopyParmsId(parms_id, parmsid);

        Ciphertext *cipher = new Ciphertext(*ctx, parmsid, capacity, *pool_ptr);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Reserve1(void *thisptr, void *context, uint64_t *parms_id, uint64_t size_capacity)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(parms_id, E_POINTER);

    parms_id_type parms;
    CopyParmsId(parms_id, parms);

    try
    {
        cipher->reserve(*ctx, parms, size_capacity);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Reserve2(void *thisptr, void *context, uint64_t size_capacity)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->reserve(*ctx, size_capacity);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Reserve3(void *thisptr, uint64_t size_capacity)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->reserve(size_capacity);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Set(void *thisptr, void *assign)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    Ciphertext *assignptr = FromVoid<Ciphertext>(assign);
    IfNullRet(assignptr, E_POINTER);

    *cipher = *assignptr;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Destroy(void *thisptr)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    delete cipher;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Size(void *thisptr, uint64_t *size)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(size, E_POINTER);

    *size = cipher->size();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SizeCapacity(void *thisptr, uint64_t *size_capacity)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(size_capacity, E_POINTER);

    *size_capacity = cipher->size_capacity();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_PolyModulusDegree(void *thisptr, uint64_t *poly_modulus_degree)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(poly_modulus_degree, E_POINTER);

    *poly_modulus_degree = cipher->poly_modulus_degree();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_CoeffModulusSize(void *thisptr, uint64_t *coeff_modulus_size)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(coeff_modulus_size, E_POINTER);

    *coeff_modulus_size = cipher->coeff_modulus_size();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_ParmsId(void *thisptr, uint64_t *parms_id)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(parms_id, E_POINTER);

    CopyParmsId(cipher->parms_id(), parms_id);
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SetParmsId(void *thisptr, uint64_t *parms_id)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(parms_id, E_POINTER);

    CopyParmsId(parms_id, cipher->parms_id());
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Resize1(void *thisptr, void *context, uint64_t *parms_id, uint64_t size)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    parms_id_type parms;
    CopyParmsId(parms_id, parms);

    try
    {
        cipher->resize(*ctx, parms, size);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Resize2(void *thisptr, void *context, uint64_t size)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->resize(*ctx, size);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Resize3(void *thisptr, uint64_t size)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->resize(size);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Resize4(void *thisptr, uint64_t size, uint64_t polyModulusDegree, uint64_t coeffModCount)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        ph::resize(cipher, size, polyModulusDegree, coeffModCount);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_GetDataAt1(void *thisptr, uint64_t index, uint64_t *data)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(data, E_POINTER);

    try
    {
        *data = (*cipher)[index];
        return S_OK;
    }
    catch (const out_of_range &)
    {
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
    }
}

SEAL_C_FUNC Ciphertext_GetDataAt2(void *thisptr, uint64_t poly_index, uint64_t coeff_index, uint64_t *data)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(data, E_POINTER);

    auto poly_uint64_count = util::mul_safe(cipher->poly_modulus_degree(), cipher->coeff_modulus_size());

    // poly_index is verified by the data method, we need to verify coeff_index ourselves.
    if (coeff_index >= poly_uint64_count)
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);

    try
    {
        *data = cipher->data(poly_index)[coeff_index];
        return S_OK;
    }
    catch (const out_of_range &)
    {
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
    }
}

SEAL_C_FUNC Ciphertext_SetDataAt(void *thisptr, uint64_t index, uint64_t value)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        (*cipher)[index] = value;
        return S_OK;
    }
    catch (const out_of_range &)
    {
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
    }
}

SEAL_C_FUNC Ciphertext_IsNTTForm(void *thisptr, bool *is_ntt_form)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(is_ntt_form, E_POINTER);

    *is_ntt_form = cipher->is_ntt_form();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SetIsNTTForm(void *thisptr, bool is_ntt_form)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    ph::set_ntt_form(cipher, is_ntt_form);
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Scale(void *thisptr, double *scale)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(scale, E_POINTER);

    *scale = cipher->scale();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SetScale(void *thisptr, double scale)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    cipher->scale() = scale;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Release(void *thisptr)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    cipher->release();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_IsTransparent(void *thisptr, bool *result)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(result, E_POINTER);

    *result = cipher->is_transparent();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Pool(void *thisptr, void **pool)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(pool, E_POINTER);

    MemoryPoolHandle *handleptr = new MemoryPoolHandle(cipher->pool());
    *pool = handleptr;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SaveSize(void *thisptr, uint8_t compr_mode, int64_t *result)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(result, E_POINTER);

    try
    {
        *result = static_cast<int64_t>(cipher->save_size(static_cast<compr_mode_type>(compr_mode)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
}

SEAL_C_FUNC Ciphertext_Save(void *thisptr, uint8_t *outptr, uint64_t size, uint8_t compr_mode, int64_t *out_bytes)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(outptr, E_POINTER);
    IfNullRet(out_bytes, E_POINTER);

    try
    {
        *out_bytes = util::safe_cast<int64_t>(cipher->save(
            reinterpret_cast<seal_byte *>(outptr), util::safe_cast<size_t>(size),
            static_cast<compr_mode_type>(compr_mode)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
    catch (const runtime_error &)
    {
        return COR_E_IO;
    }
}

SEAL_C_FUNC Ciphertext_UnsafeLoad(void *thisptr, void *context, uint8_t *inptr, uint64_t size, int64_t *in_bytes)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(inptr, E_POINTER);
    IfNullRet(in_bytes, E_POINTER);

    try
    {
        *in_bytes = util::safe_cast<int64_t>(
            cipher->unsafe_load(*ctx, reinterpret_cast<seal_byte *>(inptr), util::safe_cast<size_t>(size)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
    catch (const runtime_error &)
    {
        return COR_E_IO;
    }
}

SEAL_C_FUNC Ciphertext_Load(void *thisptr, void *context, uint8_t *inptr, uint64_t size, int64_t *in_bytes)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(inptr, E_POINTER);
    IfNullRet(in_bytes, E_POINTER);

    try
    {
        *in_bytes = util::safe_cast<int64_t>(
            cipher->load(*ctx, reinterpret_cast<seal_byte *>(inptr), util::safe_cast<size_t>(size)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
    catch (const runtime_error &)
    {
        return COR_E_IO;
    }
}


{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->reserve(*ctx, size_capacity);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

// SEALNet
#include "seal/c/ciphertext.h"
#include "seal/c/utilities.h"

// SEAL
#include "seal/ciphertext.h"

using namespace std;
using namespace seal;
using namespace seal::c;

// Enables access to private members of seal::Ciphertext.
using ph = struct seal::Ciphertext::CiphertextPrivateHelper
{
    static void resize(Ciphertext *ciphertext, size_t size, size_t poly_modulus_degree, size_t coeff_modulus_size)
    {
        ciphertext->resize_internal(size, poly_modulus_degree, coeff_modulus_size);
    }

    static void set_ntt_form(Ciphertext *ciphertext, bool is_ntt_form)
    {
        ciphertext->is_ntt_form_ = is_ntt_form;
    }
};

SEAL_C_FUNC Ciphertext_Create1(void *memoryPoolHandle, void **ciphertext)
{
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> handle = MemHandleFromVoid(memoryPoolHandle);

    try
    {
        Ciphertext *cipher = new Ciphertext(*handle);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Create2(void *copy, void **ciphertext)
{
    Ciphertext *copyptr = FromVoid<Ciphertext>(copy);
    IfNullRet(copyptr, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);

    Ciphertext *cipher = new Ciphertext(*copyptr);
    *ciphertext = cipher;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Create3(void *context, void *pool, void **ciphertext)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> pool_ptr = MemHandleFromVoid(pool);

    try
    {
        Ciphertext *cipher = new Ciphertext(*ctx, *pool_ptr);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Create4(void *context, uint64_t *parms_id, void *pool, void **ciphertext)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    IfNullRet(parms_id, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> pool_ptr = MemHandleFromVoid(pool);

    try
    {
        parms_id_type parmsid;
        CopyParmsId(parms_id, parmsid);

        Ciphertext *cipher = new Ciphertext(*ctx, parmsid, *pool_ptr);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Create5(void *context, uint64_t *parms_id, uint64_t capacity, void *pool, void **ciphertext)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    IfNullRet(parms_id, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> pool_ptr = MemHandleFromVoid(pool);

    try
    {
        parms_id_type parmsid;
        CopyParmsId(parms_id, parmsid);

        Ciphertext *cipher = new Ciphertext(*ctx, parmsid, capacity, *pool_ptr);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Reserve1(void *thisptr, void *context, uint64_t *parms_id, uint64_t size_capacity)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(parms_id, E_POINTER);

    parms_id_type parms;
    CopyParmsId(parms_id, parms);

    try
    {
        cipher->reserve(*ctx, parms, size_capacity);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Reserve2(void *thisptr, void *context, uint64_t size_capacity)

SEAL_C_FUNC Ciphertext_Reserve3(void *thisptr, uint64_t size_capacity)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->reserve(size_capacity);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Set(void *thisptr, void *assign)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    Ciphertext *assignptr = FromVoid<Ciphertext>(assign);
    IfNullRet(assignptr, E_POINTER);

    *cipher = *assignptr;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Destroy(void *thisptr)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    delete cipher;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Size(void *thisptr, uint64_t *size)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(size, E_POINTER);

    *size = cipher->size();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SizeCapacity(void *thisptr, uint64_t *size_capacity)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(size_capacity, E_POINTER);

    *size_capacity = cipher->size_capacity();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_PolyModulusDegree(void *thisptr, uint64_t *poly_modulus_degree)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(poly_modulus_degree, E_POINTER);

    *poly_modulus_degree = cipher->poly_modulus_degree();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_CoeffModulusSize(void *thisptr, uint64_t *coeff_modulus_size)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(coeff_modulus_size, E_POINTER);

    *coeff_modulus_size = cipher->coeff_modulus_size();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_ParmsId(void *thisptr, uint64_t *parms_id)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(parms_id, E_POINTER);

    CopyParmsId(cipher->parms_id(), parms_id);
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SetParmsId(void *thisptr, uint64_t *parms_id)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(parms_id, E_POINTER);

    CopyParmsId(parms_id, cipher->parms_id());
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Resize1(void *thisptr, void *context, uint64_t *parms_id, uint64_t size)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    parms_id_type parms;
    CopyParmsId(parms_id, parms);

    try
    {
        cipher->resize(*ctx, parms, size);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Resize2(void *thisptr, void *context, uint64_t size)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->resize(*ctx, size);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Resize3(void *thisptr, uint64_t size)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->resize(size);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Resize4(void *thisptr, uint64_t size, uint64_t polyModulusDegree, uint64_t coeffModCount)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        ph::resize(cipher, size, polyModulusDegree, coeffModCount);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_GetDataAt1(void *thisptr, uint64_t index, uint64_t *data)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(data, E_POINTER);

    try
    {
        *data = (*cipher)[index];
        return S_OK;
    }
    catch (const out_of_range &)
    {
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
    }
}

SEAL_C_FUNC Ciphertext_GetDataAt2(void *thisptr, uint64_t poly_index, uint64_t coeff_index, uint64_t *data)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(data, E_POINTER);

    auto poly_uint64_count = util::mul_safe(cipher->poly_modulus_degree(), cipher->coeff_modulus_size());

    // poly_index is verified by the data method, we need to verify coeff_index ourselves.
    if (coeff_index >= poly_uint64_count)
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);

    try
    {
        *data = cipher->data(poly_index)[coeff_index];
        return S_OK;
    }
    catch (const out_of_range &)
    {
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
    }
}

SEAL_C_FUNC Ciphertext_SetDataAt(void *thisptr, uint64_t index, uint64_t value)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        (*cipher)[index] = value;
        return S_OK;
    }
    catch (const out_of_range &)
    {
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
    }
}

SEAL_C_FUNC Ciphertext_IsNTTForm(void *thisptr, bool *is_ntt_form)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(is_ntt_form, E_POINTER);

    *is_ntt_form = cipher->is_ntt_form();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SetIsNTTForm(void *thisptr, bool is_ntt_form)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    ph::set_ntt_form(cipher, is_ntt_form);
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Scale(void *thisptr, double *scale)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(scale, E_POINTER);

    *scale = cipher->scale();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SetScale(void *thisptr, double scale)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    cipher->scale() = scale;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Release(void *thisptr)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    cipher->release();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_IsTransparent(void *thisptr, bool *result)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(result, E_POINTER);

    *result = cipher->is_transparent();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Pool(void *thisptr, void **pool)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(pool, E_POINTER);

    MemoryPoolHandle *handleptr = new MemoryPoolHandle(cipher->pool());
    *pool = handleptr;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SaveSize(void *thisptr, uint8_t compr_mode, int64_t *result)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(result, E_POINTER);

    try
    {
        *result = static_cast<int64_t>(cipher->save_size(static_cast<compr_mode_type>(compr_mode)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
}

SEAL_C_FUNC Ciphertext_Save(void *thisptr, uint8_t *outptr, uint64_t size, uint8_t compr_mode, int64_t *out_bytes)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(outptr, E_POINTER);
    IfNullRet(out_bytes, E_POINTER);

    try
    {
        *out_bytes = util::safe_cast<int64_t>(cipher->save(
            reinterpret_cast<seal_byte *>(outptr), util::safe_cast<size_t>(size),
            static_cast<compr_mode_type>(compr_mode)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
    catch (const runtime_error &)
    {
        return COR_E_IO;
    }
}

SEAL_C_FUNC Ciphertext_UnsafeLoad(void *thisptr, void *context, uint8_t *inptr, uint64_t size, int64_t *in_bytes)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(inptr, E_POINTER);
    IfNullRet(in_bytes, E_POINTER);

    try
    {
        *in_bytes = util::safe_cast<int64_t>(
            cipher->unsafe_load(*ctx, reinterpret_cast<seal_byte *>(inptr), util::safe_cast<size_t>(size)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
    catch (const runtime_error &)
    {
        return COR_E_IO;
    }
}

SEAL_C_FUNC Ciphertext_Load(void *thisptr, void *context, uint8_t *inptr, uint64_t size, int64_t *in_bytes)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(inptr, E_POINTER);
    IfNullRet(in_bytes, E_POINTER);

    try
    {
        *in_bytes = util::safe_cast<int64_t>(
            cipher->load(*ctx, reinterpret_cast<seal_byte *>(inptr), util::safe_cast<size_t>(size)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
    catch (const runtime_error &)
    {
        return COR_E_IO;
    }
}


// SEALNet
#include "seal/c/ciphertext.h"
#include "seal/c/utilities.h"

// SEAL
#include "seal/ciphertext.h"

using namespace std;
using namespace seal;
using namespace seal::c;

// Enables access to private members of seal::Ciphertext.
using ph = struct seal::Ciphertext::CiphertextPrivateHelper
{
    static void resize(Ciphertext *ciphertext, size_t size, size_t poly_modulus_degree, size_t coeff_modulus_size)
    {
        ciphertext->resize_internal(size, poly_modulus_degree, coeff_modulus_size);
    }

    static void set_ntt_form(Ciphertext *ciphertext, bool is_ntt_form)
    {
        ciphertext->is_ntt_form_ = is_ntt_form;
    }
};

SEAL_C_FUNC Ciphertext_Create1(void *memoryPoolHandle, void **ciphertext)
{
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> handle = MemHandleFromVoid(memoryPoolHandle);

    try
    {
        Ciphertext *cipher = new Ciphertext(*handle);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Create2(void *copy, void **ciphertext)
{
    Ciphertext *copyptr = FromVoid<Ciphertext>(copy);
    IfNullRet(copyptr, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);

    Ciphertext *cipher = new Ciphertext(*copyptr);
    *ciphertext = cipher;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Create3(void *context, void *pool, void **ciphertext)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> pool_ptr = MemHandleFromVoid(pool);

    try
    {
        Ciphertext *cipher = new Ciphertext(*ctx, *pool_ptr);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Create4(void *context, uint64_t *parms_id, void *pool, void **ciphertext)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    IfNullRet(parms_id, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> pool_ptr = MemHandleFromVoid(pool);

    try
    {
        parms_id_type parmsid;
        CopyParmsId(parms_id, parmsid);

        Ciphertext *cipher = new Ciphertext(*ctx, parmsid, *pool_ptr);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Create5(void *context, uint64_t *parms_id, uint64_t capacity, void *pool, void **ciphertext)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    IfNullRet(parms_id, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> pool_ptr = MemHandleFromVoid(pool);

    try
    {
        parms_id_type parmsid;
        CopyParmsId(parms_id, parmsid);

        Ciphertext *cipher = new Ciphertext(*ctx, parmsid, capacity, *pool_ptr);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Reserve1(void *thisptr, void *context, uint64_t *parms_id, uint64_t size_capacity)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(parms_id, E_POINTER);

    parms_id_type parms;
    CopyParmsId(parms_id, parms);

    try
    {
        cipher->reserve(*ctx, parms, size_capacity);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Reserve2(void *thisptr, void *context, uint64_t size_capacity)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->reserve(*ctx, size_capacity);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Reserve3(void *thisptr, uint64_t size_capacity)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->reserve(size_capacity);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Set(void *thisptr, void *assign)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    Ciphertext *assignptr = FromVoid<Ciphertext>(assign);
    IfNullRet(assignptr, E_POINTER);

    *cipher = *assignptr;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Destroy(void *thisptr)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    delete cipher;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Size(void *thisptr, uint64_t *size)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(size, E_POINTER);

    *size = cipher->size();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SizeCapacity(void *thisptr, uint64_t *size_capacity)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(size_capacity, E_POINTER);

    *size_capacity = cipher->size_capacity();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_PolyModulusDegree(void *thisptr, uint64_t *poly_modulus_degree)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(poly_modulus_degree, E_POINTER);

    *poly_modulus_degree = cipher->poly_modulus_degree();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_CoeffModulusSize(void *thisptr, uint64_t *coeff_modulus_size)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(coeff_modulus_size, E_POINTER);

    *coeff_modulus_size = cipher->coeff_modulus_size();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_ParmsId(void *thisptr, uint64_t *parms_id)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(parms_id, E_POINTER);

    CopyParmsId(cipher->parms_id(), parms_id);
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SetParmsId(void *thisptr, uint64_t *parms_id)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(parms_id, E_POINTER);

    CopyParmsId(parms_id, cipher->parms_id());
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Resize1(void *thisptr, void *context, uint64_t *parms_id, uint64_t size)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(ciphe

    parms_id_type parms;
    CopyParmsId(parms_id, parms);

    try
    {
        cipher->resize(*ctx, parms, size);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Resize2(void *thisptr, void *context, uint64_t size)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->resize(*ctx, size);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Resize3(void *thisptr, uint64_t size)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->resize(size);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Resize4(void *thisptr, uint64_t size, uint64_t polyModulusDegree, uint64_t coeffModCount)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        ph::resize(cipher, size, polyModulusDegree, coeffModCount);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_GetDataAt1(void *thisptr, uint64_t index, uint64_t *data)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(data, E_POINTER);

    try
    {
        *data = (*cipher)[index];
        return S_OK;
    }
    catch (const out_of_range &)
    {
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
    }
}

SEAL_C_FUNC Ciphertext_GetDataAt2(void *thisptr, uint64_t poly_index, uint64_t coeff_index, uint64_t *data)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(data, E_POINTER);

    auto poly_uint64_count = util::mul_safe(cipher->poly_modulus_degree(), cipher->coeff_modulus_size());

    // poly_index is verified by the data method, we need to verify coeff_index ourselves.
    if (coeff_index >= poly_uint64_count)
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);

    try
    {
        *data = cipher->data(poly_index)[coeff_index];
        return S_OK;
    }
    catch (const out_of_range &)
    {
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
    }
}

SEAL_C_FUNC Ciphertext_SetDataAt(void *thisptr, uint64_t index, uint64_t value)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        (*cipher)[index] = value;
        return S_OK;
    }
    catch (const out_of_range &)
    {
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
    }
}

SEAL_C_FUNC Ciphertext_IsNTTForm(void *thisptr, bool *is_ntt_form)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(is_ntt_form, E_POINTER);

    *is_ntt_form = cipher->is_ntt_form();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SetIsNTTForm(void *thisptr, bool is_ntt_form)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    ph::set_ntt_form(cipher, is_ntt_form);
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Scale(void *thisptr, double *scale)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(scale, E_POINTER);

    *scale = cipher->scale();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SetScale(void *thisptr, double scale)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    cipher->scale() = scale;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Release(void *thisptr)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    cipher->release();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_IsTransparent(void *thisptr, bool *result)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(result, E_POINTER);

    *result = cipher->is_transparent();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Pool(void *thisptr, void **pool)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(pool, E_POINTER);

    MemoryPoolHandle *handleptr = new MemoryPoolHandle(cipher->pool());
    *pool = handleptr;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SaveSize(void *thisptr, uint8_t compr_mode, int64_t *result)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(result, E_POINTER);

    try
    {
        *result = static_cast<int64_t>(cipher->save_size(static_cast<compr_mode_type>(compr_mode)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
}

SEAL_C_FUNC Ciphertext_Save(void *thisptr, uint8_t *outptr, uint64_t size, uint8_t compr_mode, int64_t *out_bytes)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(outptr, E_POINTER);
    IfNullRet(out_bytes, E_POINTER);

    try
    {
        *out_bytes = util::safe_cast<int64_t>(cipher->save(
            reinterpret_cast<seal_byte *>(outptr), util::safe_cast<size_t>(size),
            static_cast<compr_mode_type>(compr_mode)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
    catch (const runtime_error &)
    {
        return COR_E_IO;
    }
}

SEAL_C_FUNC Ciphertext_UnsafeLoad(void *thisptr, void *context, uint8_t *inptr, uint64_t size, int64_t *in_bytes)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(inptr, E_POINTER);
    IfNullRet(in_bytes, E_POINTER);

    try
    {
        *in_bytes = util::safe_cast<int64_t>(
            cipher->unsafe_load(*ctx, reinterpret_cast<seal_byte *>(inptr), util::safe_cast<size_t>(size)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
    catch (const runtime_error &)
    {
        return COR_E_IO;
    }
}

SEAL_C_FUNC Ciphertext_Load(void *thisptr, void *context, uint8_t *inptr, uint64_t size, int64_t *in_bytes)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(inptr, E_POINTER);
    IfNullRet(in_bytes, E_POINTER);

    try
    {
        *in_bytes = util::safe_cast<int64_t>(
            cipher->load(*ctx, reinterpret_cast<seal_byte *>(inptr), util::safe_cast<size_t>(size)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
    catch (const runtime_error &)
    {
        return COR_E_IO;
    }
}


try
    {
        cipher->reserve(size_capacity);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }

// SEALNet
#include "seal/c/ciphertext.h"
#include "seal/c/utilities.h"

// SEAL
#include "seal/ciphertext.h"

using namespace std;
using namespace seal;
using namespace seal::c;

// Enables access to private members of seal::Ciphertext.
using ph = struct seal::Ciphertext::CiphertextPrivateHelper
{
    static void resize(Ciphertext *ciphertext, size_t size, size_t poly_modulus_degree, size_t coeff_modulus_size)
    {
        ciphertext->resize_internal(size, poly_modulus_degree, coeff_modulus_size);
    }

    static void set_ntt_form(Ciphertext *ciphertext, bool is_ntt_form)
    {
        ciphertext->is_ntt_form_ = is_ntt_form;
    }
};

SEAL_C_FUNC Ciphertext_Create1(void *memoryPoolHandle, void **ciphertext)
{
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> handle = MemHandleFromVoid(memoryPoolHandle);

    try
    {
        Ciphertext *cipher = new Ciphertext(*handle);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Create2(void *copy, void **ciphertext)
{
    Ciphertext *copyptr = FromVoid<Ciphertext>(copy);
    IfNullRet(copyptr, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);

    Ciphertext *cipher = new Ciphertext(*copyptr);
    *ciphertext = cipher;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Create3(void *context, void *pool, void **ciphertext)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> pool_ptr = MemHandleFromVoid(pool);

    try
    {
        Ciphertext *cipher = new Ciphertext(*ctx, *pool_ptr);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Create4(void *context, uint64_t *parms_id, void *pool, void **ciphertext)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    IfNullRet(parms_id, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> pool_ptr = MemHandleFromVoid(pool);

    try
    {
        parms_id_type parmsid;
        CopyParmsId(parms_id, parmsid);

        Ciphertext *cipher = new Ciphertext(*ctx, parmsid, *pool_ptr);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Create5(void *context, uint64_t *parms_id, uint64_t capacity, void *pool, void **ciphertext)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    IfNullRet(parms_id, E_POINTER);
    IfNullRet(ciphertext, E_POINTER);
    unique_ptr<MemoryPoolHandle> pool_ptr = MemHandleFromVoid(pool);

    try
    {
        parms_id_type parmsid;
        CopyParmsId(parms_id, parmsid);

        Ciphertext *cipher = new Ciphertext(*ctx, parmsid, capacity, *pool_ptr);
        *ciphertext = cipher;
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Reserve1(void *thisptr, void *context, uint64_t *parms_id, uint64_t size_capacity)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(parms_id, E_POINTER);

    parms_id_type parms;
    CopyParmsId(parms_id, parms);

    try
    {
        cipher->reserve(*ctx, parms, size_capacity);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Reserve2(void *thisptr, void *context, uint64_t size_capacity)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->reserve(*ctx, size_capacity);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Reserve3(void *thisptr, uint64_t size_capacity)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

}

SEAL_C_FUNC Ciphertext_Set(void *thisptr, void *assign)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    Ciphertext *assignptr = FromVoid<Ciphertext>(assign);
    IfNullRet(assignptr, E_POINTER);

    *cipher = *assignptr;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Destroy(void *thisptr)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    delete cipher;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Size(void *thisptr, uint64_t *size)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(size, E_POINTER);

    *size = cipher->size();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SizeCapacity(void *thisptr, uint64_t *size_capacity)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(size_capacity, E_POINTER);

    *size_capacity = cipher->size_capacity();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_PolyModulusDegree(void *thisptr, uint64_t *poly_modulus_degree)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(poly_modulus_degree, E_POINTER);

    *poly_modulus_degree = cipher->poly_modulus_degree();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_CoeffModulusSize(void *thisptr, uint64_t *coeff_modulus_size)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(coeff_modulus_size, E_POINTER);

    *coeff_modulus_size = cipher->coeff_modulus_size();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_ParmsId(void *thisptr, uint64_t *parms_id)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(parms_id, E_POINTER);

    CopyParmsId(cipher->parms_id(), parms_id);
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SetParmsId(void *thisptr, uint64_t *parms_id)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(parms_id, E_POINTER);

    CopyParmsId(parms_id, cipher->parms_id());
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Resize1(void *thisptr, void *context, uint64_t *parms_id, uint64_t size)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    parms_id_type parms;
    CopyParmsId(parms_id, parms);

    try
    {
        cipher->resize(*ctx, parms, size);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Resize2(void *thisptr, void *context, uint64_t size)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->resize(*ctx, size);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Resize3(void *thisptr, uint64_t size)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        cipher->resize(size);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_Resize4(void *thisptr, uint64_t size, uint64_t polyModulusDegree, uint64_t coeffModCount)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        ph::resize(cipher, size, polyModulusDegree, coeffModCount);
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
}

SEAL_C_FUNC Ciphertext_GetDataAt1(void *thisptr, uint64_t index, uint64_t *data)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(data, E_POINTER);

    try
    {
        *data = (*cipher)[index];
        return S_OK;
    }
    catch (const out_of_range &)
    {
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
    }
}

SEAL_C_FUNC Ciphertext_GetDataAt2(void *thisptr, uint64_t poly_index, uint64_t coeff_index, uint64_t *data)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(data, E_POINTER);

    auto poly_uint64_count = util::mul_safe(cipher->poly_modulus_degree(), cipher->coeff_modulus_size());

    // poly_index is verified by the data method, we need to verify coeff_index ourselves.
    if (coeff_index >= poly_uint64_count)
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);

    try
    {
        *data = cipher->data(poly_index)[coeff_index];
        return S_OK;
    }
    catch (const out_of_range &)
    {
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
    }
}

SEAL_C_FUNC Ciphertext_SetDataAt(void *thisptr, uint64_t index, uint64_t value)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    try
    {
        (*cipher)[index] = value;
        return S_OK;
    }
    catch (const out_of_range &)
    {
        return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
    }
}

SEAL_C_FUNC Ciphertext_IsNTTForm(void *thisptr, bool *is_ntt_form)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(is_ntt_form, E_POINTER);

    *is_ntt_form = cipher->is_ntt_form();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SetIsNTTForm(void *thisptr, bool is_ntt_form)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    ph::set_ntt_form(cipher, is_ntt_form);
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Scale(void *thisptr, double *scale)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(scale, E_POINTER);

    *scale = cipher->scale();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SetScale(void *thisptr, double scale)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    cipher->scale() = scale;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Release(void *thisptr)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);

    cipher->release();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_IsTransparent(void *thisptr, bool *result)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(result, E_POINTER);

    *result = cipher->is_transparent();
    return S_OK;
}

SEAL_C_FUNC Ciphertext_Pool(void *thisptr, void **pool)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(pool, E_POINTER);

    MemoryPoolHandle *handleptr = new MemoryPoolHandle(cipher->pool());
    *pool = handleptr;
    return S_OK;
}

SEAL_C_FUNC Ciphertext_SaveSize(void *thisptr, uint8_t compr_mode, int64_t *result)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(result, E_POINTER);

    try
    {
        *result = static_cast<int64_t>(cipher->save_size(static_cast<compr_mode_type>(compr_mode)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
}

SEAL_C_FUNC Ciphertext_Save(void *thisptr, uint8_t *outptr, uint64_t size, uint8_t compr_mode, int64_t *out_bytes)
{
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(outptr, E_POINTER);
    IfNullRet(out_bytes, E_POINTER);

    try
    {
        *out_bytes = util::safe_cast<int64_t>(cipher->save(
            reinterpret_cast<seal_byte *>(outptr), util::safe_cast<size_t>(size),
            static_cast<compr_mode_type>(compr_mode)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
    catch (const runtime_error &)
    {
        return COR_E_IO;
    }
}

SEAL_C_FUNC Ciphertext_UnsafeLoad(void *thisptr, void *context, uint8_t *inptr, uint64_t size, int64_t *in_bytes)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(inptr, E_POINTER);
    IfNullRet(in_bytes, E_POINTER);

    try
    {
        *in_bytes = util::safe_cast<int64_t>(
            cipher->unsafe_load(*ctx, reinterpret_cast<seal_byte *>(inptr), util::safe_cast<size_t>(size)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
    catch (const runtime_error &)
    {
        return COR_E_IO;
    }
}

SEAL_C_FUNC Ciphertext_Load(void *thisptr, void *context, uint8_t *inptr, uint64_t size, int64_t *in_bytes)
{
    const SEALContext *ctx = FromVoid<SEALContext>(context);
    IfNullRet(ctx, E_POINTER);
    Ciphertext *cipher = FromVoid<Ciphertext>(thisptr);
    IfNullRet(cipher, E_POINTER);
    IfNullRet(inptr, E_POINTER);
    IfNullRet(in_bytes, E_POINTER);

    try
    {
        *in_bytes = util::safe_cast<int64_t>(
            cipher->load(*ctx, reinterpret_cast<seal_byte *>(inptr), util::safe_cast<size_t>(size)));
        return S_OK;
    }
    catch (const invalid_argument &)
    {
        return E_INVALIDARG;
    }
    catch (const logic_error &)
    {
        return COR_E_INVALIDOPERATION;
    }
    catch (const runtime_error &)
    {
        return COR_E_IO;
    }
}


#include <rutil/DnsUtil.hxx>
#include <rutil/BaseException.hxx>
#include <rutil/XMLCursor.hxx>
#include <rutil/WinLeakCheck.hxx>
#include <repro/RegSyncServer.hxx>

using namespace resip;
using namespace std;

{
#ifdef WIN32
   initNetwork();
#endif

   int sd, rc;
   struct sockaddr_in localAddr, servAddr;
   struct hostent *h;

   const char* host;
   short port;
   int cmdIndex;

   // Look for forward slash in args 1 or 3
   if(argc >= 2 && argv[1][0] == '/')
   {
       host = "127.0.0.1";
       port = 5081;
       cmdIndex=1;
   }
   else if(argc >= 4 && argv[3][0] == '/')
   {
      host = argv[1];
      port = (short)atoi(argv[2]);
      cmdIndex = 3;
   }
   else
   {
      cerr << "usage: " << argv[0] <<" [<server> <port>] /<command> [<parm>=<value>]" << endl;
      cerr << "  Valid Commands are:" << endl;
      cerr << "  /GetStackInfo - retrieves low level information about the stack state" << endl;
      cerr << "  /GetStackStats - retrieves a dump of the stack statistics" << endl;
      cerr << "  /ResetStackStats - resets all cumulative stack statistics to zero" << endl;
      cerr << "  /LogDnsCache - causes the DNS cache contents to be written to the resip logs" << endl;
      cerr << "  /ClearDnsCache - empties the stacks DNS cache" << endl;
      cerr << "  /GetDnsCache - retrieves the DNS cache contents" << endl;
      cerr << "  /GetCongestionStats - retrieves the stacks congestion manager stats and state" << endl;
      cerr << "  /SetCongestionTolerance metric=<SIZE|WAIT_TIME|TIME_DEPTH> maxTolerance=<value>" << endl;
      cerr << "                          [fifoDescription=<desc>] - sets congestion tolerances" << endl;
      cerr << "  /Shutdown - signal the proxy to shut down." << endl;
      cerr << "  /Restart - signal the proxy to restart - leaving active registrations in place." << endl;
      cerr << "  /GetProxyConfig - retrieves the all of configuration file settings currently" << endl;
      cerr << "                    being used by the proxy" << endl;
      cerr << "  /AddTransport type=<UDP|TCP|etc.> port=<value> [ipVersion=<V4|V6>]" << endl;
      cerr << "                [interface=<ipaddress>] [rruri=<AUTO|sip:host:port>]" << endl;
      cerr << "                [udprcvbuflen=<value>] [stun=<YES|NO>] [flags=<uint>]" << endl;
      cerr << "                [domain=<tlsdomainname>] [ssltype=<SSLv23|TLSv1>]" << endl;
      cerr << "                [certfile=<filename>] [keyfile=<filename>]" << endl;
      cerr << "                [tlscvm=<NONE|OPT|MAN>] [tlsuseemail=<YES|NO>]" << endl;
      cerr << "                - adds a new transport to the stack." << endl;
      cerr << "  /RemoveTransport key=<transportKey> - removes the requested transport" << endl;
      exit(1);
   }

   h = gethostbyname(host);
   if(h==0)
   {
      cerr << "unknown host " << host << endl;
      exit(1);
   }

   servAddr.sin_family = h->h_addrtype;
   if((unsigned int)h->h_length > sizeof(servAddr.sin_addr.s_addr))
   {
      cerr << "bad h_length" << endl;
      exit(1);
   }
   memcpy((char *) &servAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length);
   servAddr.sin_port = htons(port);

   // Create TCP Socket
   sd = (int)socket(AF_INET, SOCK_STREAM, 0);
   if(sd < 0)
   {
      cerr << "cannot open socket" << endl;
      exit(1);
   }

   // bind to any local interface/port
   localAddr.sin_family = AF_INET;
   localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
   localAddr.sin_port = 0;

   rc = ::bind(sd, (struct sockaddr *) &localAddr, sizeof(localAddr));
   if(rc < 0)
   {
      cerr <<"error binding locally" << endl;
      exit(1);
   }

   // Connect to server
   rc = ::connect(sd, (struct sockaddr *) &servAddr, sizeof(servAddr));
   if(rc < 0)
   {
      cerr << "error connecting" << endl;
      exit(1);
   }

   Data command(&argv[cmdIndex][1]); // [1] skips leading slash
   Data request(1024, Data::Preallocate);
   bool dumpRawResult = false;
   if (isEqualNoCase(command, "initialsync"))
   {
      request += "<InitialSync>\r\n";
      request += "  <Request>\r\n";
      request += "     <Version>" + Data(REGSYNC_VERSION) + "</Version>\r\n";
      request += "  </Request>\r\n";
      request += "</InitialSync>\r\n";

      dumpRawResult = true;
   }
   else
   {
      request += "<";
      request += command;
      request += ">\r\n  <Request>\r\n";
      for (int i = cmdIndex + 1; i < argc; i++)
      {
         Data parm;
         Data value;
         Data arg(argv[i]);
         ParseBuffer pb(arg);
         const char *anchor = pb.position();
         pb.skipToChar('=');
         if (!pb.eof())
         {
            pb.data(parm, anchor);
            pb.skipChar();
            anchor = pb.position();
            pb.skipToEnd();
            pb.data(value, anchor);
         }
         request += "    <";
         request += parm;
         request += ">";
         request += value;
         request += "</";
         request += parm;
         request += ">\r\n";
      }
      request += "  </Request>\r\n";
      request += "</";
      request += command;
      request += ">\r\n";
   }

   rc = send(sd, request.c_str(), request.size(), 0);
   if(rc < 0)
   {
      cerr << "error sending request on socket." << endl;
      closeSocket(sd);
      exit(1);
   }

   char readBuffer[8000];
   while(rc > 0)
   {
      rc = recv(sd, (char*)&readBuffer, sizeof(readBuffer), 0);
      if(rc < 0)
      {
         cerr << "error receiving response from socket." << endl;
         closeSocket(sd);
         exit(1);
      }

      if(rc > 0)
      {
         Data response(Data::Borrow, (const char*)&readBuffer, rc);

         if (dumpRawResult)
         {
            cout << "Received response: \r\n" << response << endl;
         }
         else
         {
            ParseBuffer pb(response);
            XMLCursor xml(pb);
            bool responseOK = false;
            if (xml.firstChild() && xml.nextSibling() && xml.firstChild())  // Move to Response node
            {
               while (true)
               {
                  if (isEqualNoCase(xml.getTag(), "Result"))
                  {
                     unsigned int code = 0;
                     Data text;
                     XMLCursor::AttributeMap::const_iterator it = xml.getAttributes().find("Code");
                     if (it != xml.getAttributes().end())
                     {
                        code = it->second.convertUnsignedLong();
                     }
                     if (xml.firstChild())
                     {
                        text = xml.getValue().xmlCharDataDecode();
                        xml.parent();
                     }
                     if (code >= 200 && code < 300)
                     {
                        // Success
                        cout << text << endl;
                     }
                     else
                     {
                        cout << "Error " << code << " processing request: " << text << endl;
                     }
                     responseOK = true;
                  }
                  else if (isEqualNoCase(xml.getTag(), "Data"))
                  {
                     if (xml.firstChild())
                     {
                        cout << xml.getValue().xmlCharDataDecode() << endl;
                        xml.parent();
                     }
                  }
                  if (!xml.nextSibling())
                  {
                     // break on no more sibilings
                     break;
                  }
               }
            }
            if (!responseOK)
            {
               cout << "Unable to parse response:" << endl << response << endl;
            }
            closeSocket(sd);
            break;
         }
      }
   }
}

/* ====================================================================
 *
 */
/*
 * vi: set shiftwidth=3 expandtab:
 */


#include <rutil/DnsUtil.hxx>
#include <rutil/BaseException.hxx>
#include <rutil/XMLCursor.hxx>
#include <rutil/WinLeakCheck.hxx>
#include <repro/RegSyncServer.hxx>

using namespace resip;
using namespace std;

int
main (int argc, char** argv)
{
#ifdef WIN32
   initNetwork();
#endif

   int sd, rc;
   struct sockaddr_in localAddr, servAddr;
   struct hostent *h;

   const char* host;
   short port;
   int cmdIndex;

   // Look for forward slash in args 1 or 3
   if(argc >= 2 && argv[1][0] == '/')
   {
       host = "127.0.0.1";
       port = 5081;
       cmdIndex=1;
   }
   else if(argc >= 4 && argv[3][0] == '/')
   {
      host = argv[1];
      port = (short)atoi(argv[2]);
      cmdIndex = 3;
   }
   else
   {
      cerr << "usage: " << argv[0] <<" [<server> <port>] /<command> [<parm>=<value>]" << endl;
      cerr << "  Valid Commands are:" << endl;
      cerr << "  /GetStackInfo - retrieves low level information about the stack state" << endl;
      cerr << "  /GetStackStats - retrieves a dump of the stack statistics" << endl;
      cerr << "  /ResetStackStats - resets all cumulative stack statistics to zero" << endl;
      cerr << "  /LogDnsCache - causes the DNS cache contents to be written to the resip logs" << endl;
      cerr << "  /ClearDnsCache - empties the stacks DNS cache" << endl;
      cerr << "  /GetDnsCache - retrieves the DNS cache contents" << endl;
      cerr << "  /GetCongestionStats - retrieves the stacks congestion manager stats and state" << endl;
      cerr << "  /SetCongestionTolerance metric=<SIZE|WAIT_TIME|TIME_DEPTH> maxTolerance=<value>" << endl;
      cerr << "                          [fifoDescription=<desc>] - sets congestion tolerances" << endl;
      cerr << "  /Shutdown - signal the proxy to shut down." << endl;
      cerr << "  /Restart - signal the proxy to restart - leaving active registrations in place." << endl;
      cerr << "  /GetProxyConfig - retrieves the all of configuration file settings currently" << endl;
      cerr << "                    being used by the proxy" << endl;
      cerr << "  /AddTransport type=<UDP|TCP|etc.> port=<value> [ipVersion=<V4|V6>]" << endl;
      cerr << "                [interface=<ipaddress>] [rruri=<AUTO|sip:host:port>]" << endl;
      cerr << "                [udprcvbuflen=<value>] [stun=<YES|NO>] [flags=<uint>]" << endl;
      cerr << "                [domain=<tlsdomainname>] [ssltype=<SSLv23|TLSv1>]" << endl;
      cerr << "                [certfile=<filename>] [keyfile=<filename>]" << endl;
      cerr << "                [tlscvm=<NONE|OPT|MAN>] [tlsuseemail=<YES|NO>]" << endl;
      cerr << "                - adds a new transport to the stack." << endl;
      cerr << "  /RemoveTransport key=<transportKey> - removes the requested transport" << endl;
      exit(1);
   }

   h = gethostbyname(host);
   if(h==0)
   {
      cerr << "unknown host " << host << endl;
      exit(1);
   }

   servAddr.sin_family = h->h_addrtype;
   if((unsigned int)h->h_length > sizeof(servAddr.sin_addr.s_addr))
   {
      cerr << "bad h_length" << endl;
      exit(1);
   }
   memcpy((char *) &servAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length);
   servAddr.sin_port = htons(port);

   // Create TCP Socket
   sd = (int)socket(AF_INET, SOCK_STREAM, 0);
   if(sd < 0)
   {
      cerr << "cannot open socket" << endl;
      exit(1);
   }

   // bind to any local interface/port
   localAddr.sin_family = AF_INET;
   localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
   localAddr.sin_port = 0;

   rc = ::bind(sd, (struct sockaddr *) &localAddr, sizeof(localAddr));
   if(rc < 0)
   {
      cerr <<"error binding locally" << endl;
      exit(1);
   }

   // Connect to server
   rc = ::connect(sd, (struct sockaddr *) &servAddr, sizeof(servAddr));
   if(rc < 0)
   {
      cerr << "error connecting" << endl;
      exit(1);
   }

   Data command(&argv[cmdIndex][1]); // [1] skips leading slash
   Data request(1024, Data::Preallocate);

   if (isEqualNoCase(command, "initialsync"))
   {
      request += "<InitialSync>\r\n";
      request += "  <Request>\r\n";
      request += "     <Version>" + Data(REGSYNC_VERSION) + "</Version>\r\n";
      request += "  </Request>\r\n";
      request += "</InitialSync>\r\n";

      dumpRawResult = true;
   }
   else
   {
      request += "<";
      request += command;
      request += ">\r\n  <Request>\r\n";
      for (int i = cmdIndex + 1; i < argc; i++)
      {
         Data parm;
         Data value;
         Data arg(argv[i]);
         ParseBuffer pb(arg);
         const char *anchor = pb.position();
         pb.skipToChar('=');
         if (!pb.eof())
         {
            pb.data(parm, anchor);
            pb.skipChar();
            anchor = pb.position();
            pb.skipToEnd();
            pb.data(value, anchor);
         }
         request += "    <";
         request += parm;
         request += ">";
         request += value;
         request += "</";
         request += parm;
         request += ">\r\n";
      }
      request += "  </Request>\r\n";
      request += "</";
      request += command;
      request += ">\r\n";
   }

   rc = send(sd, request.c_str(), request.size(), 0);
   if(rc < 0)
   {
      cerr << "error sending request on socket." << endl;
      closeSocket(sd);
      exit(1);
   }

   char readBuffer[8000];
   while(rc > 0)
   {
      rc = recv(sd, (char*)&readBuffer, sizeof(readBuffer), 0);
      if(rc < 0)
      {
         cerr << "error receiving response from socket." << endl;
         closeSocket(sd);
         exit(1);
      }

      if(rc > 0)
      {
         Data response(Data::Borrow, (const char*)&readBuffer, rc);

         if (dumpRawResult)
         {
            cout << "Received response: \r\n" << response << endl;
         }
         else
         {
            ParseBuffer pb(response);
            XMLCursor xml(pb);
            bool responseOK = false;
            if (xml.firstChild() && xml.nextSibling() && xml.firstChild())  // Move to Response node
            {
               while (true)
               {
                  if (isEqualNoCase(xml.getTag(), "Result"))
                  {
                     unsigned int code = 0;
                     Data text;
                     XMLCursor::AttributeMap::const_iterator it = xml.getAttributes().find("Code");
                     if (it != xml.getAttributes().end())
                     {
                        code = it->second.convertUnsignedLong();
                     }
                     if (xml.firstChild())
                     {
                        text = xml.getValue().xmlCharDataDecode();
                        xml.parent();
                     }
                     if (code >= 200 && code < 300)
                     {
                        // Success
                        cout << text << endl;
                     }
                     else
                     {
                        cout << "Error " << code << " processing request: " << text << endl;
                     }
                     responseOK = true;
                  }
                  else if (isEqualNoCase(xml.getTag(), "Data"))
                  {
                     if (xml.firstChild())
                     {
                        cout << xml.getValue().xmlCharDataDecode() << endl;
                        xml.parent();
                     }
                  }
                  if (!xml.nextSibling())
                  {
                     // break on no more sibilings
                     break;
                  }
               }
            }
            if (!responseOK)
            {
               cout << "Unable to parse response:" << endl << response << endl;
            }
            closeSocket(sd);
            break;
         }
      }
   }
}

/* ====================================================================
 *
 */
/*
 * vi: set shiftwidth=3 expandtab:
 */


void Graph::AddOneWayEdge(int from_node_index, int to_node_index, double distance)

/**
 * @file graph.cpp
 * @author Chao Cao (example@nnthink.com)
 * @brief Class that implements a graph
 * @version 0.1
 * @date 2021-07-11
 *
 *
 */

#include <queue>
#include <ros/ros.h>
#include "utils/misc_utils.h"
#include "graph/graph.h"

namespace tare
{
Graph::Graph(int node_number)
{
  connection_.resize(node_number);
  distance_.resize(node_number);
  positions_.resize(node_number);
}

void Graph::AddNode(const Eigen::Vector3d& position)
{
  std::vector<int> connection;
  connection_.push_back(connection);
  std::vector<double> neighbor_distance;
  distance_.push_back(neighbor_distance);
  positions_.push_back(position);
}

void Graph::SetNodePosition(int node_index, const Eigen::Vector3d& position)
{
  if (NodeIndexInRange(node_index))
  {
    positions_[node_index] = position;
  }
  else if (node_index == positions_.size())
  {
    AddNode(position);
  }
  else
  {
    ROS_ERROR_STREAM("Graph::SetNodePosition: node_index: " << node_index << " not in range [0, "
                                                            << positions_.size() - 1 << "]");
  }
}

{
  if (NodeIndexInRange(from_node_index) && NodeIndexInRange(to_node_index))
  {
    connection_[from_node_index].push_back(to_node_index);
    distance_[from_node_index].push_back(distance);
  }
  else
  {
    ROS_ERROR_STREAM("Graph::AddOneWayEdge: from_node_index: " << from_node_index << " to_node_index: " << to_node_index
                                                               << " not in range [0, " << connection_.size() - 1
                                                               << "]");
  }
}

void Graph::AddTwoWayEdge(int from_node_index, int to_node_index, double distance)
{
  AddOneWayEdge(from_node_index, to_node_index, distance);
  AddOneWayEdge(to_node_index, from_node_index, distance);
}

double Graph::GetShortestPath(int from_node_index, int to_node_index, bool get_path, nav_msgs::Path& shortest_path,
                              std::vector<int>& node_indices)
{
  node_indices.clear();
  double path_length = AStarSearch(from_node_index, to_node_index, get_path, node_indices);
  if (get_path)
  {
    shortest_path.poses.clear();
    for (const auto& node_index : node_indices)
    {
      geometry_msgs::PoseStamped pose;
      pose.pose.position.x = positions_[node_index].x();
      pose.pose.position.y = positions_[node_index].y();
      pose.pose.position.z = positions_[node_index].z();
      shortest_path.poses.push_back(pose);
    }
  }
  return path_length;
}

double Graph::AStarSearch(int from_node_index, int to_node_index, bool get_path, std::vector<int>& node_indices)
{
  MY_ASSERT(NodeIndexInRange(from_node_index));
  MY_ASSERT(NodeIndexInRange(to_node_index));

  double INF = 9999.0;
  typedef std::pair<double, int> iPair;
  double shortest_dist = 0;
  std::priority_queue<iPair, std::vector<iPair>, std::greater<iPair>> pq;
  std::vector<double> g(connection_.size(), INF);
  std::vector<double> f(connection_.size(), INF);
  std::vector<int> prev(connection_.size(), -1);
  std::vector<bool> in_pg(connection_.size(), false);

  g[from_node_index] = 0;
  f[from_node_index] = (positions_[from_node_index] - positions_[to_node_index]).norm();

  pq.push(std::make_pair(f[from_node_index], from_node_index));
  in_pg[from_node_index] = true;

  while (!pq.empty())
  {
    int u = pq.top().second;
    pq.pop();
    in_pg[u] = false;
    if (u == to_node_index)
    {
      shortest_dist = g[u];
      break;
    }

    for (int i = 0; i < connection_[u].size(); i++)
    {
      int v = connection_[u][i];
      MY_ASSERT(misc_utils_ns::InRange<std::vector<int>>(connection_, v));
      double d = distance_[u][i];
      if (g[v] > g[u] + d)
      {
        prev[v] = u;
        g[v] = g[u] + d;
        f[v] = g[v] + (positions_[v] - positions_[to_node_index]).norm();
        if (!in_pg[v])
        {
          pq.push(std::make_pair(f[v], v));
          in_pg[v] = true;
        }
      }
    }
  }

  if (get_path)
  {
    node_indices.clear();
    int u = to_node_index;
    if (prev[u] != -1 || u == from_node_index)
    {
      while (u != -1)
      {
        node_indices.push_back(u);
        u = prev[u];
      }
    }
  }
  std::reverse(node_indices.begin(), node_indices.end());

  return shortest_dist;
}

}  // namespace tare


{
  std::vector<int> connection;
  connection_.push_back(connection);
  std::vector<double> neighbor_distance;
  distance_.push_back(neighbor_distance);
  positions_.push_back(position);
}

/**
 * @file graph.cpp
 * @author Chao Cao (example@nnthink.com)
 * @brief Class that implements a graph
 * @version 0.1
 * @date 2021-07-11
 *
 *
 */

#include <queue>
#include <ros/ros.h>
#include "utils/misc_utils.h"
#include "graph/graph.h"

namespace tare
{
Graph::Graph(int node_number)
{
  connection_.resize(node_number);
  distance_.resize(node_number);
  positions_.resize(node_number);
}

void Graph::AddNode(const Eigen::Vector3d& position)

void Graph::SetNodePosition(int node_index, const Eigen::Vector3d& position)
{
  if (NodeIndexInRange(node_index))
  {
    positions_[node_index] = position;
  }
  else if (node_index == positions_.size())
  {
    AddNode(position);
  }
  else
  {
    ROS_ERROR_STREAM("Graph::SetNodePosition: node_index: " << node_index << " not in range [0, "
                                                            << positions_.size() - 1 << "]");
  }
}

void Graph::AddOneWayEdge(int from_node_index, int to_node_index, double distance)
{
  if (NodeIndexInRange(from_node_index) && NodeIndexInRange(to_node_index))
  {
    connection_[from_node_index].push_back(to_node_index);
    distance_[from_node_index].push_back(distance);
  }
  else
  {
    ROS_ERROR_STREAM("Graph::AddOneWayEdge: from_node_index: " << from_node_index << " to_node_index: " << to_node_index
                                                               << " not in range [0, " << connection_.size() - 1
                                                               << "]");
  }
}

void Graph::AddTwoWayEdge(int from_node_index, int to_node_index, double distance)
{
  AddOneWayEdge(from_node_index, to_node_index, distance);
  AddOneWayEdge(to_node_index, from_node_index, distance);
}

double Graph::GetShortestPath(int from_node_index, int to_node_index, bool get_path, nav_msgs::Path& shortest_path,
                              std::vector<int>& node_indices)
{
  node_indices.clear();
  double path_length = AStarSearch(from_node_index, to_node_index, get_path, node_indices);
  if (get_path)
  {
    shortest_path.poses.clear();
    for (const auto& node_index : node_indices)
    {
      geometry_msgs::PoseStamped pose;
      pose.pose.position.x = positions_[node_index].x();
      pose.pose.position.y = positions_[node_index].y();
      pose.pose.position.z = positions_[node_index].z();
      shortest_path.poses.push_back(pose);
    }
  }
  return path_length;
}

double Graph::AStarSearch(int from_node_index, int to_node_index, bool get_path, std::vector<int>& node_indices)
{
  MY_ASSERT(NodeIndexInRange(from_node_index));
  MY_ASSERT(NodeIndexInRange(to_node_index));

  double INF = 9999.0;
  typedef std::pair<double, int> iPair;
  double shortest_dist = 0;
  std::priority_queue<iPair, std::vector<iPair>, std::greater<iPair>> pq;
  std::vector<double> g(connection_.size(), INF);
  std::vector<double> f(connection_.size(), INF);
  std::vector<int> prev(connection_.size(), -1);
  std::vector<bool> in_pg(connection_.size(), false);

  g[from_node_index] = 0;
  f[from_node_index] = (positions_[from_node_index] - positions_[to_node_index]).norm();

  pq.push(std::make_pair(f[from_node_index], from_node_index));
  in_pg[from_node_index] = true;

  while (!pq.empty())
  {
    int u = pq.top().second;
    pq.pop();
    in_pg[u] = false;
    if (u == to_node_index)
    {
      shortest_dist = g[u];
      break;
    }

    for (int i = 0; i < connection_[u].size(); i++)
    {
      int v = connection_[u][i];
      MY_ASSERT(misc_utils_ns::InRange<std::vector<int>>(connection_, v));
      double d = distance_[u][i];
      if (g[v] > g[u] + d)
      {
        prev[v] = u;
        g[v] = g[u] + d;
        f[v] = g[v] + (positions_[v] - positions_[to_node_index]).norm();
        if (!in_pg[v])
        {
          pq.push(std::make_pair(f[v], v));
          in_pg[v] = true;
        }
      }
    }
  }

  if (get_path)
  {
    node_indices.clear();
    int u = to_node_index;
    if (prev[u] != -1 || u == from_node_index)
    {
      while (u != -1)
      {
        node_indices.push_back(u);
        u = prev[u];
      }
    }
  }
  std::reverse(node_indices.begin(), node_indices.end());

  return shortest_dist;
}

}  // namespace tare


/**
 * @file graph.cpp
 * @author Chao Cao (example@nnthink.com)
 * @brief Class that implements a graph
 * @version 0.1
 * @date 2021-07-11
 *
 *
 */

#include <queue>
#include <ros/ros.h>
#include "utils/misc_utils.h"
#include "graph/graph.h"

namespace tare
{
Graph::Graph(int node_number)
{
  connection_.res

  distance_.resize(node_number);
  positions_.resize(node_number);
}

void Graph::AddNode(const Eigen::Vector3d& position)
{
  std::vector<int> connection;
  connection_.push_back(connection);
  std::vector<double> neighbor_distance;
  distance_.push_back(neighbor_distance);
  positions_.push_back(position);
}

void Graph::SetNodePosition(int node_index, const Eigen::Vector3d& position)
{
  if (NodeIndexInRange(node_index))
  {
    positions_[node_index] = position;
  }
  else if (node_index == positions_.size())
  {
    AddNode(position);
  }
  else
  {
    ROS_ERROR_STREAM("Graph::SetNodePosition: node_index: " << node_index << " not in range [0, "
                                                            << positions_.size() - 1 << "]");
  }
}

void Graph::AddOneWayEdge(int from_node_index, int to_node_index, double distance)
{
  if (NodeIndexInRange(from_node_index) && NodeIndexInRange(to_node_index))
  {
    connection_[from_node_index].push_back(to_node_index);
    distance_[from_node_index].push_back(distance);
  }
  else
  {
    ROS_ERROR_STREAM("Graph::AddOneWayEdge: from_node_index: " << from_node_index << " to_node_index: " << to_node_index
                                                               << " not in range [0, " << connection_.size() - 1
                                                               << "]");
  }
}

void Graph::AddTwoWayEdge(int from_node_index, int to_node_index, double distance)
{
  AddOneWayEdge(from_node_index, to_node_index, distance);
  AddOneWayEdge(to_node_index, from_node_index, distance);
}

double Graph::GetShortestPath(int from_node_index, int to_node_index, bool get_path, nav_msgs::Path& shortest_path,
                              std::vector<int>& node_indices)
{
  node_indices.clear();
  double path_length = AStarSearch(from_node_index, to_node_index, get_path, node_indices);
  if (get_path)
  {
    shortest_path.poses.clear();
    for (const auto& node_index : node_indices)
    {
      geometry_msgs::PoseStamped pose;
      pose.pose.position.x = positions_[node_index].x();
      pose.pose.position.y = positions_[node_index].y();
      pose.pose.position.z = positions_[node_index].z();
      shortest_path.poses.push_back(pose);
    }
  }
  return path_length;
}

double Graph::AStarSearch(int from_node_index, int to_node_index, bool get_path, std::vector<int>& node_indices)
{
  MY_ASSERT(NodeIndexInRange(from_node_index));
  MY_ASSERT(NodeIndexInRange(to_node_index));

  double INF = 9999.0;
  typedef std::pair<double, int> iPair;
  double shortest_dist = 0;
  std::priority_queue<iPair, std::vector<iPair>, std::greater<iPair>> pq;
  std::vector<double> g(connection_.size(), INF);
  std::vector<double> f(connection_.size(), INF);
  std::vector<int> prev(connection_.size(), -1);
  std::vector<bool> in_pg(connection_.size(), false);

  g[from_node_index] = 0;
  f[from_node_index] = (positions_[from_node_index] - positions_[to_node_index]).norm();

  pq.push(std::make_pair(f[from_node_index], from_node_index));
  in_pg[from_node_index] = true;

  while (!pq.empty())
  {
    int u = pq.top().second;
    pq.pop();
    in_pg[u] = false;
    if (u == to_node_index)
    {
      shortest_dist = g[u];
      break;
    }

    for (int i = 0; i < connection_[u].size(); i++)
    {
      int v = connection_[u][i];
      MY_ASSERT(misc_utils_ns::InRange<std::vector<int>>(connection_, v));
      double d = distance_[u][i];
      if (g[v] > g[u] + d)
      {
        prev[v] = u;
        g[v] = g[u] + d;
        f[v] = g[v] + (positions_[v] - positions_[to_node_index]).norm();
        if (!in_pg[v])
        {
          pq.push(std::make_pair(f[v], v));
          in_pg[v] = true;
        }
      }
    }
  }

  if (get_path)
  {
    node_indices.clear();
    int u = to_node_index;
    if (prev[u] != -1 || u == from_node_index)
    {
      while (u != -1)
      {
        node_indices.push_back(u);
        u = prev[u];
      }
    }
  }
  std::reverse(node_indices.begin(), node_indices.end());

  return shortest_dist;
}

}  // namespace tare


for (int i = 0; i < connection_[u].size(); i++)
    {
      int v = connection_[u][i];
      MY_ASSERT(misc_utils_ns::InRange<std::vector<int>>(connection_, v));
      double d = distance_[u][i];
      if (g[v] > g[u] + d)
      {
        prev[v] = u;
        g[v] = g[u] + d;
        f[v] = g[v] + (positions_[v] - positions_[to_node_index]).norm();
        if (!in_pg[v])
        {
          pq.push(std::make_pair(f[v], v));
          in_pg[v] = true;
        }
      }
    }

/**
 * @file graph.cpp
 * @author Chao Cao (example@nnthink.com)
 * @brief Class that implements a graph
 * @version 0.1
 * @date 2021-07-11
 *
 *
 */

#include <queue>
#include <ros/ros.h>
#include "utils/misc_utils.h"
#include "graph/graph.h"

namespace tare
{
Graph::Graph(int node_number)
{
  connection_.resize(node_number);
  distance_.resize(node_number);
  positions_.resize(node_number);
}

void Graph::AddNode(const Eigen::Vector3d& position)
{
  std::vector<int> connection;
  connection_.push_back(connection);
  std::vector<double> neighbor_distance;
  distance_.push_back(neighbor_distance);
  positions_.push_back(position);
}

void Graph::SetNodePosition(int node_index, const Eigen::Vector3d& position)
{
  if (NodeIndexInRange(node_index))
  {
    positions_[node_index] = position;
  }
  else if (node_index == positions_.size())
  {
    AddNode(position);
  }
  else
  {
    ROS_ERROR_STREAM("Graph::SetNodePosition: node_index: " << node_index << " not in range [0, "
                                                            << positions_.size() - 1 << "]");
  }
}

void Graph::AddOneWayEdge(int from_node_index, int to_node_index, double distance)
{
  if (NodeIndexInRange(from_node_index) && NodeIndexInRange(to_node_index))
  {
    connection_[from_node_index].push_back(to_node_index);
    distance_[from_node_index].push_back(distance);
  }
  else
  {
    ROS_ERROR_STREAM("Graph::AddOneWayEdge: from_node_index: " << from_node_index << " to_node_index: " << to_node_index
                                                               << " not in range [0, " << connection_.size() - 1
                                                               << "]");
  }
}

void Graph::AddTwoWayEdge(int from_node_index, int to_node_index, double distance)
{
  AddOneWayEdge(from_node_index, to_node_index, distance);
  AddOneWayEdge(to_node_index, from_node_index, distance);
}

double Graph::GetShortestPath(int from_node_index, int to_node_index, bool get_path, nav_msgs::Path& shortest_path,
                              std::vector<int>& node_indices)
{
  node_indices.clear();
  double path_length = AStarSearch(from_node_index, to_node_index, get_path, node_indices);
  if (get_path)
  {
    shortest_path.poses.clear();
    for (const auto& node_index : node_indices)
    {
      geometry_msgs::PoseStamped pose;
      pose.pose.position.x = positions_[node_index].x();
      pose.pose.position.y = positions_[node_index].y();
      pose.pose.position.z = positions_[node_index].z();
      shortest_path.poses.push_back(pose);
    }
  }
  return path_length;
}

double Graph::AStarSearch(int from_node_index, int to_node_index, bool get_path, std::vector<int>& node_indices)
{
  MY_ASSERT(NodeIndexInRange(from_node_index));
  MY_ASSERT(NodeIndexInRange(to_node_index));

  double INF = 9999.0;
  typedef std::pair<double, int> iPair;
  double shortest_dist = 0;
  std::priority_queue<iPair, std::vector<iPair>, std::greater<iPair>> pq;
  std::vector<double> g(connection_.size(), INF);
  std::vector<double> f(connection_.size(), INF);
  std::vector<int> prev(connection_.size(), -1);
  std::vector<bool> in_pg(connection_.size(), false);

  g[from_node_index] = 0;
  f[from_node_index] = (positions_[from_node_index] - positions_[to_node_index]).norm();

  pq.push(std::make_pair(f[from_node_index], from_node_index));
  in_pg[from_node_index] = true;

  while (!pq.empty())
  {
    int u = pq.top().second;
    pq.pop();
    in_pg[u] = false;
    if (u == to_node_index)
    {
      shortest_dist = g[u];
      break;
    }

  }

  if (get_path)
  {
    node_indices.clear();
    int u = to_node_index;
    if (prev[u] != -1 || u == from_node_index)
    {
      while (u != -1)
      {
        node_indices.push_back(u);
        u = prev[u];
      }
    }
  }
  std::reverse(node_indices.begin(), node_indices.end());

  return shortest_dist;
}

}  // namespace tare


while (u != -1)
      {
        node_indices.push_back(u);
        u = prev[u];
      }

/**
 * @file graph.cpp
 * @author Chao Cao (example@nnthink.com)
 * @brief Class that implements a graph
 * @version 0.1
 * @date 2021-07-11
 *
 *
 */

#include <queue>
#include <ros/ros.h>
#include "utils/misc_utils.h"
#include "graph/graph.h"

namespace tare
{
Graph::Graph(int node_number)
{
  connection_.resize(node_number);
  distance_.resize(node_number);
  positions_.resize(node_number);
}

void Graph::AddNode(const Eigen::Vector3d& position)
{
  std::vector<int> connection;
  connection_.push_back(connection);
  std::vector<double> neighbor_distance;
  distance_.push_back(neighbor_distance);
  positions_.push_back(position);
}

void Graph::SetNodePosition(int node_index, const Eigen::Vector3d& position)
{
  if (NodeIndexInRange(node_index))
  {
    positions_[node_index] = position;
  }
  else if (node_index == positions_.size())
  {
    AddNode(position);
  }
  else
  {
    ROS_ERROR_STREAM("Graph::SetNodePosition: node_index: " << node_index << " not in range [0, "
                                                            << positions_.size() - 1 << "]");
  }
}

void Graph::AddOneWayEdge(int from_node_index, int to_node_index, double distance)
{
  if (NodeIndexInRange(from_node_index) && NodeIndexInRange(to_node_index))
  {
    connection_[from_node_index].push_back(to_node_index);
    distance_[from_node_index].push_back(distance);
  }
  else
  {
    ROS_ERROR_STREAM("Graph::AddOneWayEdge: from_node_index: " << from_node_index << " to_node_index: " << to_node_index
                                                               << " not in range [0, " << connection_.size() - 1
                                                               << "]");
  }
}

void Graph::AddTwoWayEdge(int from_node_index, int to_node_index, double distance)
{
  AddOneWayEdge(from_node_index, to_node_index, distance);
  AddOneWayEdge(to_node_index, from_node_index, distance);
}

double Graph::GetShortestPath(int from_node_index, int to_node_index, bool get_path, nav_msgs::Path& shortest_path,
                              std::vector<int>& node_indices)
{
  node_indices.clear();
  double path_length = AStarSearch(from_node_index, to_node_index, get_path, node_indices);
  if (get_path)
  {
    shortest_path.poses.clear();
    for (const auto& node_index : node_indices)
    {
      geometry_msgs::PoseStamped pose;
      pose.pose.position.x = positions_[node_index].x();
      pose.pose.position.y = positions_[node_index].y();
      pose.pose.position.z = positions_[node_index].z();
      shortest_path.poses.push_back(pose);
    }
  }
  return path_length;
}

double Graph::AStarSearch(int from_node_index, int to_node_index, bool get_path, std::vector<int>& node_indices)
{
  MY_ASSERT(NodeIndexInRange(from_node_index));
  MY_ASSERT(NodeIndexInRange(to_node_index));

  double INF = 9999.0;
  typedef std::pair<double, int> iPair;
  double shortest_dist = 0;
  std::priority_queue<iPair, std::vector<iPair>, std::greater<iPair>> pq;
  std::vector<double> g(connection_.size(), INF);
  std::vector<double> f(connection_.size(), INF);
  std::vector<int> prev(connection_.size(), -1);
  std::vector<bool> in_pg(connection_.size(), false);

  g[from_node_index] = 0;
  f[from_node_index] = (positions_[from_node_index] - positions_[to_node_index]).norm();

  pq.push(std::make_pair(f[from_node_index], from_node_index));
  in_pg[from_node_index] = true;

  while (!pq.empty())
  {
    int u = pq.top().second;
    pq.pop();
    in_pg[u] = false;
    if (u == to_node_index)
    {
      shortest_dist = g[u];
      break;
    }

    for (int i = 0; i < connection_[u].size(); i++)
    {
      int v = connection_[u][i];
      MY_ASSERT(misc_utils_ns::InRange<std::vector<int>>(connection_, v));
      double d = distance_[u][i];
      if (g[v] > g[u] + d)
      {
        prev[v] = u;
        g[v] = g[u] + d;
        f[v] = g[v] + (positions_[v] - positions_[to_node_index]).norm();
        if (!in_pg[v])
        {
          pq.push(std::make_pair(f[v], v));
          in_pg[v] = true;
        }
      }
    }
  }

  if (get_path)
  {
    node_indices.clear();
    int u = to_node_index;
    if (prev[u] != -1 || u == from_node_index)
    {

    }
  }
  std::reverse(node_indices.begin(), node_indices.end());

  return shortest_dist;
}

}  // namespace tare


// author: "Romain Michon (example@nnthink.com)"
// Code generated with Faust 0.9.55 (http://faust.grame.fr)
/* link with  */
#include <math.h>
#include <cmath>
template <int N> inline float faustpower(float x) 		{ return powf(x,N); }
template <int N> inline double faustpower(double x) 	{ return pow(x,N); }
template <int N> inline int faustpower(int x) 			{ return faustpower<N/2>(x) * faustpower<N-N/2>(x); }
template <> 	 inline int faustpower<0>(int x) 		{ return 1; }
template <> 	 inline int faustpower<1>(int x) 		{ return x; }
#include <math.h>
#include <string>

/*
#include "/usr/share/faust/audio/dsp.h"
#include "/usr/share/faust/gui/UI.h"
*/

#include "/home/kjetil/faudiostream/architecture/faust/audio/dsp.h"
#include "/home/kjetil/faudiostream/architecture/faust/gui/UI.h"

struct Meta
{
    void declare (const char* key, const char* value) { }
};

static float scale(float x, float x1, float x2, float y1, float y2){
  return y1 + ( ((x-x1)*(y2-y1))
                /
                (x2-x1)
                );
}

#if 0
static float linear2db(float val){
  if(val<=0.0f)
    return 0.0f;

  float db = 20*log10(val);
  if(db<-70)
    return 0.0f;
  else if(db>40)
    return 1.0f;
  else
    return scale(db,-70,40,0,1);
}
#endif

// input is between 0 and 1.
// output is between 0 and 1.
static float velocity2gain(float val){
  if(val<=0.0f)
    return 0.0f;
  else if(val>=1.0f)
    return 1.0f;
  else
    return powf(10, scale(val,0.0, 1.0 ,-40, 20) / 20.0f) / 10.0f;
}

static double midi_to_hz(float midi){
  if(midi<=0)
    return 0;
  else
  return 8.17579891564*(expf(.0577622650*midi));
}

#if 0
// For some reason, it won't compile with the usual min/max macros.
template<typename T> static inline T min(T a,T b){return a<b ? a : b;}
template<typename T> static inline T max(T a,T b){return a>b ? a : b;}
static inline float min(float a,int b){return a<b ? a : b;}
static inline float max(float a,int b){return a>b ? a : b;}
static inline float min(int a,float b){return a<b ? a : b;}
static inline float max(int a,float b){return a>b ? a : b;}
#endif

inline int 	max (unsigned int a, unsigned int b) { return (a>b) ? a : b; }
inline int 	max (int a, int b)		{ return (a>b) ? a : b; }

inline long 	max (long a, long b) 		{ return (a>b) ? a : b; }
inline long 	max (int a, long b) 		{ return (a>b) ? a : b; }
inline long 	max (long a, int b) 		{ return (a>b) ? a : b; }

inline float 	max (float a, float b) 		{ return (a>b) ? a : b; }
inline float 	max (int a, float b) 		{ return (a>b) ? a : b; }
inline float 	max (float a, int b) 		{ return (a>b) ? a : b; }
inline float 	max (long a, float b) 		{ return (a>b) ? a : b; }
inline float 	max (float a, long b) 		{ return (a>b) ? a : b; }

inline double 	max (double a, double b) 	{ return (a>b) ? a : b; }
inline double 	max (int a, double b) 		{ return (a>b) ? a : b; }
inline double 	max (double a, int b) 		{ return (a>b) ? a : b; }
inline double 	max (long a, double b) 		{ return (a>b) ? a : b; }
inline double 	max (double a, long b) 		{ return (a>b) ? a : b; }
inline double 	max (float a, double b) 	{ return (a>b) ? a : b; }
inline double 	max (double a, float b) 	{ return (a>b) ? a : b; }

inline int	min (int a, int b)		{ return (a<b) ? a : b; }

inline long 	min (long a, long b) 		{ return (a<b) ? a : b; }
inline long 	min (int a, long b) 		{ return (a<b) ? a : b; }