diff --git a/.gitignore b/.gitignore
index e6907a0..d6c8d60 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@
!/test/fnft__poly/
!/test/fnft_version_test/
!/test/fnft_version_test.c/
+!/test/fnft_kdvv_inverse/
# Ignore MacOS hidden files in subdirectories
/**/*.DS_Store
diff --git a/examples/fnft_kdvv_inverse_example_1.c b/examples/fnft_kdvv_inverse_example_1.c
new file mode 100644
index 0000000..52e1d26
--- /dev/null
+++ b/examples/fnft_kdvv_inverse_example_1.c
@@ -0,0 +1,75 @@
+/*
+* This file is part of FNFT.
+*
+* FNFT is free software; you can redistribute it and/or
+* modify it under the terms of the version 2 of the GNU General
+* Public License as published by the Free Software Foundation.
+*
+* FNFT is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+* Contributors:
+* Fabian Fischer (Hiwi KIT) 2026
+*/
+
+// This is a simple example for the usage of the inverse kdvv transform in C
+
+#define FNFT_ENABLE_SHORT_NAMES
+
+#include
+
+#include "fnft_kdvv_inverse.h"
+#include "fnft__errwarn.h"
+#include "fnft__misc.h"
+
+#define K 5 // number of bound states to add
+
+INT main()
+{
+ INT ret_code = SUCCESS;
+
+ COMPLEX * q = NULL;
+ COMPLEX * contspec = NULL;
+
+ // five desired bound states and norming constants
+ // - bound states have to be positive, purely imaginary numbers
+ // - bound states have to be in descending order
+ // - every norming constants belong to the bound state at the same index
+ // - the signs of the norming constants have to alternate regarding the order
+ // of bound states. The norming constant of the biggest bound state has to be positive.
+ COMPLEX bound_states[K] = { I*5.0, I*4.0, I*3.0, I*2.0, I*1.0 };
+ COMPLEX norming_constants[K] = { 1e5, -1e-2, 1e0, -1e3, 1e1 };
+
+ // General parameters
+ UINT D = 256; // Number of samples of the computed output
+ REAL T[2] = {-10.0, 10.0}; // area for which the output should be computed
+
+ // allocation of memory for the computed output
+ q = malloc(D * sizeof(COMPLEX));
+ CHECK_NOMEM(q, ret_code, leave_fun);
+
+ // Define continuous spectrum
+ // continuous spectrum is out of function, but needs to be defined/allocated for using
+ // the inverse kdvv (state 04/2026)
+ UINT M = 10;
+ REAL XI[2] = {-2.0, 2.0};
+
+ contspec = malloc(M * sizeof(COMPLEX));
+ CHECK_NOMEM(contspec, ret_code, leave_fun);
+
+ // call of the inverse kdvv
+ ret_code = fnft_kdvv_inverse(M, contspec, XI, K, bound_states, norming_constants, D, q, T, NULL);
+ CHECK_RETCODE(ret_code, leave_fun);
+
+ // prints the results in the console
+ misc_print_buf(D, q, "output");
+
+leave_fun:
+ free(q);
+ free(contspec);
+}
diff --git a/examples/fnft_kdvv_inverse_example_2.c b/examples/fnft_kdvv_inverse_example_2.c
new file mode 100644
index 0000000..04de867
--- /dev/null
+++ b/examples/fnft_kdvv_inverse_example_2.c
@@ -0,0 +1,88 @@
+/*
+* This file is part of FNFT.
+*
+* FNFT is free software; you can redistribute it and/or
+* modify it under the terms of the version 2 of the GNU General
+* Public License as published by the Free Software Foundation.
+*
+* FNFT is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+* Contributors:
+* Fabian Fischer (Hiwi KIT) 2026
+*/
+
+// This is an example for the usage of the inverse kdvv transform with many
+// bound states
+
+#define FNFT_ENABLE_SHORT_NAMES
+
+#include
+
+#include "fnft_kdvv_inverse.h"
+#include "fnft__errwarn.h"
+#include "fnft__misc.h"
+
+#define K 19 // Number of bound states to add
+
+INT main()
+{
+ INT ret_code = SUCCESS;
+
+ COMPLEX * bound_states = NULL;
+ COMPLEX * q = NULL;
+ COMPLEX * contspec = NULL;
+
+ COMPLEX desired_solitions_height[K] = { 40.0, 39.0, 38.0, 37.0, 36.0,
+ 30.0, 29.0, 28.0, 27.0, 26.0,
+ 20.0, 19.0, 18.0, 17.0, 16.0,
+ 10.0, 9.0, 8.0, 7.0};
+
+ COMPLEX normconsts[K] = { 1e20, -1e-7, 1e5, -1e3, 1e1,
+ -1e0, 1e2, -1e4, 1e-6, -1e8,
+ 1e2, -1e4, 1e6, -1e8, 1e-10,
+ -1e7, 1e-6, -1e5, 1e-9};
+
+ // resulting bound states out of desired solitions height
+ bound_states = malloc(K * sizeof(COMPLEX));
+ CHECK_NOMEM(bound_states, ret_code, leave_fun);
+
+ for (UINT i = 0; i.
+%
+% Contributors:
+% Sander Wahls (KIT) 2026.
+% Fabian Fischer (Hiwi KIT) 2026.
+
+% This example should illustrate how to use the inverse kdvv transform
+
+clear all;
+close all;
+
+% desired height of solitions:
+desired_solitions = [4, 3, 2, 1];
+
+% resulting bound states out of desired heights of solitions:
+% - bound states have to be positive, purely imaginary numbers
+% - the bound states have to be in descendent order
+bound_states = 1i*sqrt(desired_solitions ./2);
+
+% desired norming constants
+% defines how much the solitions are shifted towards each other
+% - signs have to alternate regards to the order of the bound states
+% - sign of the normconst for the biggest eigenvalue has to be positive
+norming_constants = complex([1, -1, 1, -1].*[0.00001, 0.1, 1, 10]);
+
+% Number of samples of the output of the inverse kdvv
+D = 1001;
+
+% Area, for which the output has to be computed
+% Can be asymmetric
+T = [-10 10];
+
+% defining the continuous spectrum:
+% out of function, just for seek of completeness (state 04/2026)
+contspec = [];
+XI = [1e-6 1];
+
+% calls function of c-library FNFT
+q = mex_fnft_kdvv_inverse(contspec, XI, bound_states, norming_constants, D, T);
+% the result consists of solitions with desired height (if the solitions are far
+% away enough to each other)
+
+% --- Plot the results ---
+t = linspace(T(1), T(2), D);
+
+% the output of the inverse kdvv is defined as a complex number (state 04/2026),
+% however only the imaginary part should be zero and can be neglected
+q = double(real(q(:)'));
+
+figure;
+plot(t, q);
+title('output of inverse kdvv: time-domain');
+xlabel('t');
+ylabel('q(t)');
+legend('q(t)');
+
+figure;
+stem(imag(bound_states),real(norming_constants), 'x');
+title('Bound states and norming constants');
+xlabel('bound states');
+ylabel('norming constants');
\ No newline at end of file
diff --git a/examples/mex_fnft_kdvv_inverse_example_2.m b/examples/mex_fnft_kdvv_inverse_example_2.m
new file mode 100644
index 0000000..fcc8811
--- /dev/null
+++ b/examples/mex_fnft_kdvv_inverse_example_2.m
@@ -0,0 +1,85 @@
+% This file is part of FNFT.
+%
+% FNFT is free software; you can redistribute it and/or
+% modify it under the terms of the version 2 of the GNU General
+% Public License as published by the Free Software Foundation.
+%
+% FNFT is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with this program. If not, see .
+%
+% Contributors:
+% Sander Wahls (KIT) 2026.
+% Fabian Fischer (Hiwi KIT) 2026.
+
+% This example should illustrate, how to use the inverse kdvv in combination
+% with the forward kdvv transform. For basic usage of the inverse kdvv, please
+% please have a look at mex_fnft_kdvv_inverse_example_1.m (state 04/2026).
+
+clear all;
+close all;
+
+contspec_initial = [];
+XI = [1e-6 10];
+
+bound_states_initial = 1i*sqrt([4, 3, 2, 1] ./2);
+norming_constants_initial = complex([1, -1, 1, -1].*[10000, 0.1, 1, 0.001]);
+
+D = 1001;
+T = [-15 10];
+
+% inverse kdvv transform
+q = mex_fnft_kdvv_inverse(contspec_initial, XI, bound_states_initial, norming_constants_initial, D, T);
+
+q = double(real(q(:)'));
+
+% compute the nonlinear Fourier transform of the output of the inverse kdvv
+[contspec_computed, bound_states_computed, norming_constants_computed] = mex_fnft_kdvv(q, T, XI);
+
+
+% --- Plot the results ---
+t = linspace(T(1), T(2), D);
+
+figure;
+plot(t, q);
+title('output of inverse kdvv: time-domain');
+xlabel('t');
+ylabel('q(t)');
+legend('q(t)');
+
+% plotting the initial and the computed discrete spectrum
+% the difference between the initial and the computed spectrum should be very small
+
+figure;
+stem(imag(bound_states_initial),real(norming_constants_initial), 'x', 'color', 'r');
+hold on;
+stem(imag(bound_states_computed),real(norming_constants_computed), 'x', 'color', 'g');
+hold off;
+title('initial and computed bound states and norming constants');
+xlabel('bound states');
+ylabel('norming constants');
+legend('initial', 'computed');
+
+% plotting of continuous spectrum
+% values are small because initial continuous spectrum was assumed as zero
+% and the computed continuous spectrum is only the result of numerical errors
+
+ep_xi = (XI(2) - XI(1)) / (D - 1);
+xi = XI(1):ep_xi:XI(2);
+
+figure;
+hmag=subplot(2,1,1);
+semilogy(xi, abs(contspec_computed));
+title('computed continuous spectrum');
+xlabel('\xi');
+ylabel('|r(\xi)|');
+hang=subplot(2,1,2);
+plot(xi, angle(contspec_computed));
+xlabel('\xi');
+ylabel('\angle r(\xi)');
+linkaxes([hmag,hang],'x');
+
diff --git a/examples/mex_fnft_kdvv_inverse_example_3.m b/examples/mex_fnft_kdvv_inverse_example_3.m
new file mode 100644
index 0000000..9334d3a
--- /dev/null
+++ b/examples/mex_fnft_kdvv_inverse_example_3.m
@@ -0,0 +1,78 @@
+% This file is part of FNFT.
+%
+% FNFT is free software; you can redistribute it and/or
+% modify it under the terms of the version 2 of the GNU General
+% Public License as published by the Free Software Foundation.
+%
+% FNFT is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with this program. If not, see .
+%
+% Contributors:
+% Fabian Fischer (Hiwi KIT) 2026.
+
+% This example shows a fast way to try different values (bound states, norming constants)
+% for the inverse kdvv transform, because this script sorts and considers the alternating
+% signs of the norming constants automatically.
+
+clear all;
+close all;
+
+% === modify here ====================================================================================
+% a solition in the output of inverse kdvv is defined by the elements of both arrays at the same index
+desired_solitions = [9, 2, 4, 3, 7];
+desired_shifting = [10000, 0.1, 1, 0.00001, 2];
+% ====================================================================================================
+
+
+% Check if both arrays contains the same number of elements
+if length(desired_shifting) ~= length(desired_solitions)
+ error('Number of desired solitions is not equal to number of values given for shifting!');
+end
+
+% --- determining sorted bound states and norming constants out of desired values ---
+
+number_values = length(desired_solitions);
+
+tmp_bound_states = 1i*sqrt(desired_solitions ./2);
+
+% sorting
+[bound_states, indices] = sort(tmp_bound_states, "descend");
+tmp_norming_constants = desired_shifting(indices);
+
+% alternating sequence 1 and -1 as elements and with 1 as first element
+tmp_seq = (-1).^( 0:(number_values-1) );
+
+norming_constants = complex(tmp_seq.*tmp_norming_constants);
+
+% inverse kdvv
+contspec = [];
+XI = [1e-6 1];
+D = 1001;
+T = [-12 8];
+q = mex_fnft_kdvv_inverse(contspec, XI, bound_states, norming_constants, D, T);
+
+
+% --- Plot the results ---
+t = linspace(T(1), T(2), D);
+
+% the output of the inverse kdvv is defined as a complex number (state 04/2026),
+% however only the imaginary part should be zero and can be neglected
+q = double(real(q(:)'));
+
+figure;
+plot(t, q);
+title('output of inverse kdvv: time-domain');
+xlabel('t');
+ylabel('q(t)');
+legend('q(t)');
+
+figure;
+stem(imag(bound_states),real(norming_constants), 'x');
+title('Bound states and norming constants');
+xlabel('bound states');
+ylabel('norming constants');
\ No newline at end of file
diff --git a/examples/mex_fnft_kdvv_inverse_example_4.m b/examples/mex_fnft_kdvv_inverse_example_4.m
new file mode 100644
index 0000000..0ca29a4
--- /dev/null
+++ b/examples/mex_fnft_kdvv_inverse_example_4.m
@@ -0,0 +1,37 @@
+%%
+% This file is part of FNFT.
+%
+% FNFT is free software; you can redistribute it and/or
+% modify it under the terms of the version 2 of the GNU General
+% Public License as published by the Free Software Foundation.
+%
+% FNFT is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with this program. If not, see .
+%
+% Contributors:
+% Sander Wahls (KIT) 2026.
+
+% This is a simple example of how to use the inverse kdvv.
+
+close all;
+
+contspec = [];
+XI = [0 1];
+
+bound_states = 1i*sqrt( [5, 4, 3, 2, 1] /2);
+
+normconsts = complex([1, -1, 1, -1, 1].*[10000000, 0.1, 1, 0.001, 10]);
+
+D = 1001;
+T = [-10 10];
+q = mex_fnft_kdvv_inverse(contspec, XI, bound_states, normconsts, D, T);
+
+t = linspace(T(1), T(2), D);
+plot(t, q)
+xlabel('t')
+ylabel('q(t)')
diff --git a/include/fnft_kdvv.h b/include/fnft_kdvv.h
index 542ac97..cedb0f2 100644
--- a/include/fnft_kdvv.h
+++ b/include/fnft_kdvv.h
@@ -270,7 +270,7 @@ fnft_kdvv_opts_t fnft_kdvv_default_opts();
* - fnft_kdv_discretization_TES4(_VANILLA)
*
* The accuray of the computed quantities for a given signal depends primarily on the number of samples \f$ D\f$ and the numerical method. When the exact spectrum is
- * is know, the accuracy can be quantified by defining a suitable error. The error usually decreases with increasing \f$ D\f$ assuming everthing else remains the same.
+ * known, the accuracy can be quantified by defining a suitable error. The error usually decreases with increasing \f$ D\f$ assuming everthing else remains the same.
* The rate at which the error decreases with increase in \f$ D\f$ is known as the order of the method. The orders of the various discretizations can be found at \link fnft_kdv_discretization_t \endlink.
* The orders of the discretizations which use exponential splitting schemes should be the same as their base methods but can deviate when accuracy of the splitting scheme is low.
*
diff --git a/include/fnft_kdvv_inverse.h b/include/fnft_kdvv_inverse.h
new file mode 100644
index 0000000..b566788
--- /dev/null
+++ b/include/fnft_kdvv_inverse.h
@@ -0,0 +1,106 @@
+/*
+ * This file is part of FNFT.
+ *
+ * FNFT is free software; you can redistribute it and/or
+ * modify it under the terms of the version 2 of the GNU General
+ * Public License as published by the Free Software Foundation.
+ *
+ * FNFT is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Contributors:
+ * Sander Wahls (KIT) 2026
+ * Fabian Fischer (Hiwi KIT) 2026
+ */
+
+/**
+ * @file fnft_kdvv_inverse.h
+ * @brief Fast inverse nonlinear Fourier transform for the vanishing
+ * Korteweg-de Vries equation.
+ * @ingroup fnft_inverse
+ */
+
+#ifndef FNFT_KDVV_INVERSE_H
+#define FNFT_KDVV_INVERSE_H
+
+#include "fnft__errwarn.h"
+#include "fnft__misc.h"
+#include "fnft_numtypes.h"
+
+/**
+ * @brief Fast inverse nonlinear Fourier transform for the
+ * Korteweg-de Vries equation with vanishing boundary conditions.
+ *
+ * This routine computes the inverse nonlinear Fourier transform for the
+ * Korteweg-de Vries equation
+ * \f[ q_x + 6qq_{t} + q_{ttt}=0, \quad q=q(x,t), \f]
+ * of Gardner et al. (
+ * Phys. Rev. Lett., 1967)
+ *
+ * The Fast inverse nonlinear Fourier transform for the Korteweg-de Vries equation
+ * uses the crum transformation. The main references are:
+ * - Prins and Wahls, "An accurate O(N^2) floating point algorithm for the Crum transform of the KdV equation," Communications in Nonlinear Science and Numerical Simulation 102, Article 105782, 2021.
+ * - The matlab project of P. Prins to the Crum Transformation
+ *
+ * @param[in] M Number of samples of the continuous spectrum.
+ * @param[in,out] contspec Array of length M, contains samples
+ * \f$ \hat{q}(\xi_n) \f$, where \f$ \xi_n = XI[0] + n(XI[1]-XI[0])/(M-1) \f$
+ * and \f$n=0,1,\dots,M-1\f$, of the to-be-inverted continuous spectrum in
+ * ascending order (i.e.,
+ * \f$ \hat{q}(\xi_0), \hat{q}(\xi_1), \dots, \hat{q}(\xi_{M-1}) \f$).
+ * Note: contspec functionality currently out of function (state 04/2026)!
+ * It will be implicitly assumed to \f$ 0 \f$ for all \f$ \xi \f$.
+ * @param[in] XI Array of length 2, contains the position of the first and the last
+ * sample of the continuous spectrum.
+ * Note: contspec functionality currently out of function (state 04/2026)! It does not
+ * matter which values are chosen at the moment.
+ * @param[in] K Number of discrete spectrum points.
+ * @param[in] bound_states Complex array of length K. Bound states have to be positive,
+ * purely imaginary numbers (lie on the upper half of the imaginary axis). The bound
+ * states have to be in descending order. To add a solition with height \f$ h_i \f$
+ * the bound state have to be \f$ \gamma_i = \sqrt{ h_i/2} \f$.
+ * @param[in] norming_constants Complex array of length K. Values of
+ * either the norming constants \f$ b(\xi) \f$ or the residues
+ * \f$ \frac{b(\xi)}{\partial{a(\xi)}/\partial{\xi}}\f$ at the values bound_states.
+ * The signs of the norming constants have to alternate regards to the order of the
+ * bound states. The sign of the normconst for the biggest eigenvalue has to be positive
+ * Note: currently this array is always interpreted as norming constants (state 04/2026)!
+ * Residues functionality is not implemented yet!
+ * @param[in] D Number of samples of the to be generated signal q.
+ * @param[out] q Array of length D. Is filled with samples
+ * \f$ q(t_n) \f$, where \f$ t_n = T[0] + n(T[1]-T[0])/(D-1) \f$
+ * and \f$n=0,1,\dots,D-1\f$, of the to-be-generated signal in ascending order
+ * (i.e., \f$ q(t_0), q(t_1), \dots, q(t_{D-1}) \f$).
+ * Has to be preallocated by the user.
+ * @param[in] T Array of length 2, contains the position in time of the first and
+ * of the last sample of q. It should be \f$ T[0].
+*
+* Contributors:
+* Sander Wahls (TU Delft) 2018.
+*/
+
+#ifndef FNFT__KDVV_INVERSE_TESTCASES_H
+#define FNFT__KDVV_INVERSE_TESTCASES_H
+
+#include "fnft_kdvv_inverse.h"
+#include "fnft_kdvv.h"
+
+/**
+ * @struct fnft_kdvv_params
+ */
+typedef struct {
+ UINT D;
+ REAL T[2];
+ UINT K;
+ COMPLEX * bound_states;
+ COMPLEX * normconsts;
+ UINT M;
+ REAL XI[2];
+ COMPLEX * contspec;
+} fnft_kdvv_params;
+
+
+/**
+ * @brief Routine to run tests for \link fnft_kdvv_inverse \endlink.
+ *
+ * This routine is used by the tests for \link fnft_kdvv_inverse \endlink.
+ *
+ * @param[in] params_i \link fnft_kdvv_params \endlink
+ * @param[in] err_bnd_bound_states
+ * @param[in] err_bnd_spurious_bound_states
+ * @param[in] err_bnd_normconst
+ * @param[in] err_bnd_contspec
+ * @return If all errors stay below bounds the routine
+ * \link FNFT_SUCCESS \endlink. Otherwise, it returns an error code
+ * (normally, \link FNFT_EC_TEST_FAILED \endlink).
+ *
+ * @ingroup kdv
+ */
+FNFT_INT fnft__kdvv_inverse_testcases_get_spectrum_of_inverse(
+ const fnft_kdvv_params params_i,
+ const FNFT_REAL err_bnd_bound_states,
+ const FNFT_REAL err_bnd_spurious_bound_states,
+ const FNFT_REAL err_bnd_normconst,
+ const FNFT_REAL err_bnd_contspec);
+
+
+/**
+ * @brief Routine to print the continuous spectrum and discrete spectrum, consisting of
+ * bound states and norming constants, as a result of \link fnft_kdvv \endlink.
+ *
+ * This routine is used by the tests for \link fnft_kdvv_inverse \endlink.
+ *
+ * @param[in] bound_states
+ * @param[in] normconsts
+ * @param[in] contspec
+ * @param[in] XI Array of length 2, contains the position of the first and the last
+ * sample of the continuous spectrum.
+ * @param[in] M Number of points at which the continuous spectrum is computed.
+ * @param[in] D Number of samples of the potential.
+ * @param[in] K Number of bound states (same than number of norming constants)
+ * @return void
+ *
+ * @ingroup kdv
+ */
+void fnft__kdvv_print_spectrum( FNFT_COMPLEX const * const bound_states,
+ FNFT_COMPLEX const * const normconsts,
+ FNFT_COMPLEX const * const contspec,
+ FNFT_REAL const * const XI,
+ const UINT M,
+ const UINT D,
+ const UINT K);
+
+
+#ifdef FNFT_ENABLE_SHORT_NAMES
+#define kdvv_testcases_get_spectrum_of_inverse(...) fnft__kdvv_inverse_testcases_get_spectrum_of_inverse(__VA_ARGS__)
+#define kdvv_print_spectrum(...) fnft__kdvv_print_spectrum(__VA_ARGS__)
+#endif
+
+#endif
diff --git a/include/private/fnft__misc.h b/include/private/fnft__misc.h
index 5dc9b2a..7a3ae9f 100644
--- a/include/private/fnft__misc.h
+++ b/include/private/fnft__misc.h
@@ -98,6 +98,25 @@ FNFT_REAL fnft__misc_hausdorff_dist(const FNFT_UINT lenA,
FNFT_COMPLEX const * const vecA, const FNFT_UINT lenB,
FNFT_COMPLEX const * const vecB);
+
+/**
+ * @brief Hausdorff distance between two vectors, normed by the considered element.
+ *
+ * @ingroup misc
+ * This function computes the Hausdorff distance between two vectors vecA and vecB.
+ * By calculating the maximum distance, the distance is divided by the absolute of the
+ * according element.
+ * @param[in] lenA Length of vector vecA.
+ * @param[in] vecA Complex vector of length lenA.
+ * @param[in] lenB length of vector vecB.
+ * @param[in] vecB Complex vector of length lenB.
+ * @return Returns the real valued Hausdorff distance between the vectors vecA and vecB.
+ */
+FNFT_REAL fnft__misc_hausdorff_dist_normed(const FNFT_UINT lenA,
+ FNFT_COMPLEX const * const vecA, const FNFT_UINT lenB,
+ FNFT_COMPLEX const * const vecB);
+
+
/**
* @brief Hyperbolic secant.
*
@@ -453,6 +472,7 @@ static inline FNFT_INT fnft__misc_normalize_vector(const FNFT_UINT len, FNFT_COM
#define misc_rel_err(...) fnft__misc_rel_err(__VA_ARGS__)
#define misc_rel_err_real(...) fnft__misc_rel_err_real(__VA_ARGS__)
#define misc_hausdorff_dist(...) fnft__misc_hausdorff_dist(__VA_ARGS__)
+#define misc_hausdorff_dist_normed(...) fnft__misc_hausdorff_dist_normed(__VA_ARGS__)
#define misc_sech(...) fnft__misc_sech(__VA_ARGS__)
#define misc_l2norm2(...) fnft__misc_l2norm2(__VA_ARGS__)
#define misc_filter(...) fnft__misc_filter(__VA_ARGS__)
diff --git a/matlab/mex_fnft_kdvv_inverse.c b/matlab/mex_fnft_kdvv_inverse.c
new file mode 100644
index 0000000..c52187e
--- /dev/null
+++ b/matlab/mex_fnft_kdvv_inverse.c
@@ -0,0 +1,177 @@
+/*
+* This file is part of FNFT.
+*
+* FNFT is free software; you can redistribute it and/or
+* modify it under the terms of the version 2 of the GNU General
+* Public License as published by the Free Software Foundation.
+*
+* FNFT is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+* Contributors:
+* Sander Wahls (KIT) 2026.
+*/
+
+#include
+#include
+#include "mex.h"
+#ifndef SKIP_MATRIX_H
+#include "matrix.h"
+#endif
+#include "fnft_kdvv_inverse.h"
+
+void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
+{
+ FNFT_UINT D;
+ FNFT_COMPLEX * q = NULL;
+ FNFT_REAL * T;
+ FNFT_UINT M, K;
+ FNFT_COMPLEX * contspec = NULL;
+ FNFT_COMPLEX * bound_states = NULL;
+ FNFT_COMPLEX * norming_constants = NULL;
+ FNFT_REAL * XI;
+ FNFT_UINT i;
+ double *re, *im;
+ char msg[128]; // buffer for error messages
+ FNFT_INT ret_code;
+ FNFT_UINT k;
+
+ if (nlhs < 1)
+ return;
+
+ /* Check types and dimensions of the first seven inputs: contspec, XI,
+ bound_states, norming_constants, D, T */
+
+ if ( nrhs < 6 )
+ mexErrMsgTxt("At least seven inputs expected.");
+ if ( !mxIsEmpty(prhs[0]) && (!mxIsDouble(prhs[0]) || !mxIsComplex(prhs[0]) || mxGetM(prhs[0]) != 1) )
+ mexErrMsgTxt("First input contspec should be a complex row vector (double precision) or []. Try passing complex(double(contspec(:)')).");
+ if ( mxIsComplex(prhs[1]) || !mxIsDouble(prhs[1]) || mxGetM(prhs[1]) != 1 || mxGetN(prhs[1]) != 2 )
+ mexErrMsgTxt("Second input XI should be a real 1x2 vector (double precision).");
+ if ( !mxIsEmpty(prhs[2]) && (!mxIsDouble(prhs[2]) || !mxIsComplex(prhs[2]) || mxGetM(prhs[2]) != 1) )
+ mexErrMsgTxt("Third input bound_states should be a complex row vector (double precision) or []. Try passing complex(double(bound_states(:)')).");
+ if ( !mxIsEmpty(prhs[3]) && (!mxIsDouble(prhs[3]) || !mxIsComplex(prhs[3]) || mxGetM(prhs[3]) != 1) )
+ mexErrMsgTxt("Fourth input norming_constants should be a complex row vector (double precision) or []. Try passing complex(double(norming_constants(:)')).");
+ if ( mxIsComplex(prhs[4]) || !mxIsDouble(prhs[4]) || mxGetNumberOfElements(prhs[4]) != 1 )
+ mexErrMsgTxt("Fifth input D should be a real scalar (double precision).");
+ if ( mxIsComplex(prhs[5]) || !mxIsDouble(prhs[5]) || mxGetM(prhs[5]) != 1 || mxGetN(prhs[5]) != 2 )
+ mexErrMsgTxt("Sixth input T should be a real 1x2 vector (double precision).");
+
+ M = mxGetNumberOfElements(prhs[0]);
+ K = mxGetNumberOfElements(prhs[2]);
+ T = mxGetPr(prhs[5]);
+ D = (unsigned int)mxGetScalar(prhs[4]);
+ XI = mxGetPr(prhs[1]);
+
+ /* Check values of first four inputs */
+
+ if ( K != mxGetNumberOfElements(prhs[3]) )
+ mexErrMsgTxt("bound_states and norming_constants should have the same lengths.");
+ if ( T[0] >= T[1] )
+ mexErrMsgTxt("T(1) >= T(2).");
+ if ( XI[0] >= XI[1] )
+ mexErrMsgTxt("XI(1) >= XI(2).");
+ if ( D<2 )
+ mexErrMsgTxt("D < 2.");
+
+ /* Redirect FNFT error messages and warnings to Matlabs command window */
+
+ fnft_errwarn_setprintf(mexPrintf);
+
+ /* Check remaining inputs, if any */
+
+ for (k=7; k<(FNFT_UINT)nrhs; k++) {
+
+ /* Check if current input is a string as desired and convert it */
+ if ( !mxIsChar(prhs[k]) ) {
+ snprintf(msg, sizeof msg, "%uth input should be a string.",
+ (unsigned int)(k+1));
+ goto on_error;
+ }
+ char *str = mxArrayToString(prhs[k]);
+ if ( str == NULL ) {
+ snprintf(msg, sizeof msg, "Out of memory.");
+ goto on_error;
+ }
+
+ /* Try to interpret value of string input */
+ if ( strcmp(str, "quiet") == 0 ) {
+
+ fnft_errwarn_setprintf(NULL);
+
+ } else {
+ snprintf(msg, sizeof msg, "%uth input has invalid value.",
+ (unsigned int)(k+1));
+ goto on_error;
+ }
+ }
+
+ /* Allocate memory */
+
+ q = mxMalloc(D * sizeof(FNFT_COMPLEX));
+ if (M>0)
+ contspec = mxMalloc(M * sizeof(FNFT_COMPLEX));
+ if (K>0) {
+ bound_states = mxMalloc(K * sizeof(FNFT_COMPLEX));
+ norming_constants = mxMalloc(K * sizeof(FNFT_COMPLEX));
+ }
+ if ( q == NULL || (M>0 && contspec == NULL) || (K>0 && bound_states == NULL)
+ || (K>0 && norming_constants == NULL) ) {
+ snprintf(msg, sizeof msg, "Out of memory.");
+ goto on_error;
+ }
+
+ /* Convert inputs */
+
+ re = mxGetPr(prhs[0]);
+ im = mxGetPi(prhs[0]);
+ for (i=0; i=D, contains the samples
+% of the reflection coefficient, the b-scattering
+% coefficient or the inverse Fourier transform of the
+% b-scattering coefficient on an equidistant grid.
+% Pass [] if the continuous spectrum is zero
+% (i.e., a multi-soliton is desired)
+% Note: contspec functionality currently out of function
+% (state 04/2026)!
+% It will be implicitly assumed to \f$ 0 \f$ for all \f$ \xi \f$.
+% XI Real 1x2 vector, contains the position of the first and the last
+% sample of the continuous spectrum.
+% Note: contspec functionality currently out of function
+% (state 04/2026)!
+% It does not matter which values are chosen at the moment.
+% bound_states Complex row vector, contains the desired bound states.
+% norming_constants Complex row vector, same length as bound_states.
+% Contains the corresponding norming constants.
+% D Real scalar, number of time domain samples.
+% T Real 1x2 vector, contains the location of the first and
+% the last sample in q
+%
+% OUTPUTS
+% q Complex row vector of length D
+
+% This file is part of FNFT.
+%
+% FNFT is free software; you can redistribute it and/or
+% modify it under the terms of the version 2 of the GNU General
+% Public License as published by the Free Software Foundation.
+%
+% FNFT is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with this program. If not, see .
+%
+% Contributors:
+% Sander Wahls (KIT) 2026.
+% Fabian Fischer (Hiwi KIT) 2026.
\ No newline at end of file
diff --git a/src/fnft_kdvv_inverse.c b/src/fnft_kdvv_inverse.c
new file mode 100644
index 0000000..7c18024
--- /dev/null
+++ b/src/fnft_kdvv_inverse.c
@@ -0,0 +1,774 @@
+/*
+ * This file is part of FNFT.
+ *
+ * FNFT is free software; you can redistribute it and/or
+ * modify it under the terms of the version 2 of the GNU General
+ * Public License as published by the Free Software Foundation.
+ *
+ * FNFT is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Contributors:
+ * Sander Wahls (KIT) 2026
+ * Fabian Fischer (Hiwi KIT) 2026
+ *
+ * Following Algorithms are based on Crum Transformation Matlab project
+ * of P.J. Prins 2020
+ */
+
+
+#define FNFT_ENABLE_SHORT_NAMES
+
+#define THRESHOLD_EQUALITY_BOUND_STATES 1e-15
+
+#include "fnft_kdvv_inverse.h"
+
+static INT find_first_positive_value(
+ COMPLEX const * const array,
+ const UINT length,
+ UINT * const i_pos_ptr)
+{
+ INT ret_code = SUCCESS;
+ UINT is_positive_value_found = 0;
+
+ for (UINT i = 0; i < length; i++){
+ if (CREAL(array[i]) > 0){
+ *i_pos_ptr = i;
+ is_positive_value_found = 1;
+ break;
+ }
+ }
+
+ if (is_positive_value_found == 0){
+ ret_code = FNFT_EC_INVALID_ARGUMENT;
+ }
+ else {
+ ret_code = SUCCESS;
+ }
+
+ return ret_code;
+}
+
+static INT one_solition_crum_transformation(
+ COMPLEX const k1,
+ COMPLEX const * const th1,
+ COMPLEX const * const th2,
+ const UINT D,
+ const UINT i_pos,
+ COMPLEX const * const t_grid,
+ COMPLEX * const w_inv,
+ COMPLEX * const prefactor,
+ COMPLEX * const M_min1_11)
+{
+ INT ret_code = SUCCESS;
+
+ // Calculation for positive x
+ for (UINT i = i_pos; i0
+ UINT i_pos;
+ ret_code = find_first_positive_value(t_grid, D, &i_pos);
+ CHECK_RETCODE(ret_code, leave_fun);
+
+ // -- Crum Transform --
+ COMPLEX bound_state_added_here = bound_states[0];
+ ret_code = one_solition_crum_transformation( bound_state_added_here,
+ &theta_E1[0], // thetas belonging to the bound state to add
+ &theta_E2[0], // thetas belonging to the bound state to add
+ D,
+ i_pos,
+ t_grid,
+ w_inv,
+ prefactor,
+ M_min1_11);
+ CHECK_RETCODE(ret_code, leave_fun);
+
+ // Update output
+ for (UINT i=0; i0
+ UINT i_pos;
+ ret_code = find_first_positive_value(t_grid, D, &i_pos);
+ CHECK_RETCODE(ret_code, leave_fun);
+
+ // -- Crum-Transformation --
+ ret_code = two_solitions_crum_transformation( k1,
+ k2,
+ N_pm0_jZ,
+ pm0_jZ,
+ theta_E1,
+ theta_E2,
+ temp_t1,
+ temp_t2,
+ D,
+ i_pos,
+ t_grid,
+ w_inv,
+ prefactor,
+ dq,
+ s );
+ CHECK_RETCODE(ret_code, leave_fun);
+
+
+ // Update output
+ for (UINT i=0; i 0){
+ return E_INVALID_ARGUMENT_MSG(norming_constants,The signs of the norming constants does not alternate!);
+ }
+
+ for (UINT j=i+1; j 0){
+ // Select indices for this step and for the remaining eigenvalues to add
+ // If the number of eigenvalues left is odd, then only one solition should be added
+ if (N_rest%2==1){ N_step = 1; }
+ else { N_step = 2; }
+
+ // Scale trajectories (magnitudes of th1 and th2 symmetric around 0)
+ COMPLEX tmp_scaling_factor;
+
+ for (UINT i=0; i.
+*
+* Contributors:
+* Fabian Fischer (Hiwi KIT) 2026
+*/
+
+#define FNFT_ENABLE_SHORT_NAMES
+
+#include
+
+#include "fnft__kdvv_inverse_testcases.h"
+#include "fnft__errwarn.h"
+#include "fnft__misc.h"
+
+void kdvv_print_spectrum( COMPLEX const * const bound_states,
+ COMPLEX const * const normconsts,
+ COMPLEX const * const contspec,
+ REAL const * const XI,
+ const UINT M,
+ const UINT D,
+ const UINT K)
+{
+ printf("Number of samples:\n D = %u\n", (unsigned int)D);
+
+ FNFT_REAL eps_xi = (XI[1] - XI[0]) / (M - 1);
+ printf("Continuous spectrum:\n");
+ for (FNFT_UINT i=0; i 0) && is_bsr_pure_imaginary;
+
+ UINT is_ncr_real = FABS(CIMAG(ncr)) < 1e-8;
+
+ if (is_bsr_pure_imaginary &&
+ is_bsr_positive_imaginary &&
+ is_ncr_real)
+ {
+ ret_code = SUCCESS;
+ }
+ else {
+ ret_code = FNFT_EC_TEST_FAILED;
+ break;
+ }
+ }
+ CHECK_RETCODE(ret_code, leave_fun);
+
+ // Check matching of the computed bound states
+ // bound_states_r is sorted in ascending order, K_r is the number of found bound states by fnft_kdvv
+ // take only the last params_i.K bound states, because they are the bigger ones and more likely the ones
+ // which corresponds to the initial given bound states
+ COMPLEX * const candidate_bound_states_r_ptr = &bound_states_r[K_r-params_i.K];
+ REAL hausdorff_dist_bound_states = misc_hausdorff_dist_normed( params_i.K, params_i.bound_states, params_i.K,
+ candidate_bound_states_r_ptr);
+ UINT is_bsr_in_tolerance = hausdorff_dist_bound_states < err_bnd_bound_states;
+
+ COMPLEX * const candidate_normconsts_r_ptr = &normconsts_r[K_r-params_i.K];
+ REAL hausdorff_dist_normconsts = misc_hausdorff_dist_normed(params_i.K, params_i.normconsts, params_i.K,
+ candidate_normconsts_r_ptr);
+ UINT is_ncr_in_tolerance = hausdorff_dist_normconsts < err_bnd_normconst;
+
+ #ifdef DEBUG
+ printf("Number of bound_states:\n K_r = %u\n", (unsigned int)K_r);
+ printf("Hausdorff dist bound states:\n dist = %f\n", hausdorff_dist_bound_states);
+ printf("Hausdorff dist normconsts:\n dist = %f\n", hausdorff_dist_normconsts);
+ #endif
+
+ UINT is_contspec_small = 1;
+ for (UINT i=0; i err_bnd_contspec){
+ is_contspec_small = 0;
+ }
+ }
+
+ UINT are_spurious_bound_states_small = 1;
+ for (UINT i=0; i<(K_r-params_i.K); i++){
+ if (CABS(bound_states_r[i]) > err_bnd_spurious_bound_states){
+ are_spurious_bound_states_small = 0;
+ }
+ }
+
+ if (is_bsr_in_tolerance &&
+ is_ncr_in_tolerance &&
+ is_contspec_small &&
+ are_spurious_bound_states_small)
+ {
+ ret_code = SUCCESS;
+ }
+ else {
+ ret_code = FNFT_EC_TEST_FAILED;
+ }
+
+
+leave_fun:
+ free(q);
+ free(contspec_r);
+ free(bound_states_r);
+ free(normconsts_r);
+
+ if (ret_code != SUCCESS)
+ return EXIT_FAILURE;
+ else
+ return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/src/private/fnft__misc.c b/src/private/fnft__misc.c
index 1dc90b6..d2b31ad 100644
--- a/src/private/fnft__misc.c
+++ b/src/private/fnft__misc.c
@@ -17,6 +17,7 @@
* Sander Wahls (TU Delft) 2017.
* Peter J Prins (TU Delft) 2018-2020.
* Shrinivas Chimmalgi (TU Delft) 2019-2020.
+* Fabian Fischer (Hiwi KIT) 2026
*/
#define FNFT_ENABLE_SHORT_NAMES
@@ -28,27 +29,31 @@
void misc_print_buf(const UINT len, COMPLEX const * const buf,
char const * const varname)
{
+ fnft_printf_ptr_t printf_ptr = fnft_errwarn_getprintf();
+
UINT i;
- printf("%s = [", varname);
+ printf_ptr("%s = [", varname);
for (i = 0; i < len; i++) {
- printf("%1.12e+%1.12ej", CREAL(buf[i]), CIMAG(buf[i]));
+ printf_ptr("%1.12e+%1.12ej", CREAL(buf[i]), CIMAG(buf[i]));
if (i != len-1)
- printf(", ");
+ printf_ptr(", ");
}
- printf("];\n");
+ printf_ptr("];\n");
}
void misc_print_buf_real(const UINT len, REAL const * const buf,
char const * const varname)
{
+ fnft_printf_ptr_t printf_ptr = fnft_errwarn_getprintf();
+
UINT i;
- printf("%s = [", varname);
+ printf_ptr("%s = [", varname);
for (i = 0; i < len; i++) {
- printf("%1.12e", buf[i]);
+ printf_ptr("%1.12e", buf[i]);
if (i != len-1)
- printf(", ");
+ printf_ptr(", ");
}
- printf("];\n");
+ printf_ptr("];\n");
}
REAL misc_rel_err(const UINT len, COMPLEX const * const vec_numer,
@@ -107,6 +112,38 @@ REAL misc_hausdorff_dist(const UINT lenA,
return max_dist;
}
+REAL misc_hausdorff_dist_normed(const UINT lenA,
+ COMPLEX const * const vecA, const UINT lenB,
+ COMPLEX const * const vecB)
+{
+ UINT i, j;
+ double tmp, dist, max_dist = -1.0;
+
+ for (i=0; i max_dist)
+ max_dist = dist;
+ }
+
+ for (j=0; j max_dist)
+ max_dist = dist;
+ }
+
+ return max_dist;
+}
+
COMPLEX misc_sech(COMPLEX Z)
{
return 2.0 / (CEXP(Z) + CEXP(-Z));
diff --git a/test/fnft_kdvv_inverse/fnft_kdvv_inverse_analytic_test.c b/test/fnft_kdvv_inverse/fnft_kdvv_inverse_analytic_test.c
new file mode 100644
index 0000000..b8ae38a
--- /dev/null
+++ b/test/fnft_kdvv_inverse/fnft_kdvv_inverse_analytic_test.c
@@ -0,0 +1,187 @@
+/*
+* This file is part of FNFT.
+*
+* FNFT is free software; you can redistribute it and/or
+* modify it under the terms of the version 2 of the GNU General
+* Public License as published by the Free Software Foundation.
+*
+* FNFT is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+* Contributors:
+* Fabian Fischer (Hiwi KIT) 2026
+*/
+
+#define FNFT_ENABLE_SHORT_NAMES
+
+#include
+
+#include "fnft__kdvv_inverse_testcases.h"
+#include "fnft__errwarn.h"
+#include "fnft__misc.h"
+
+
+/* This is a testcase with analytic reference
+ *
+ * This test case is based on the example discussed on p. 74-78 in [1]. Similar to this example
+ * is the example discussed in section 5.2 (p. 13) in [2]. Note that we use different notation and
+ * the definition of the norming constants in [2]. Thus we have to do the following adaptions of the
+ * formulas given in [1]:
+ *
+ * - substitute x with t and t with x
+ * - the analytic signal needs to be multiplied by -1
+ * - norming constants:
+ * b(2i, x) = exp(8i*(2i)^3*x) --> b(2i, 0) = 1
+ * b(1i, x) = -exp(8i*(1i)^3*x) --> b(1i, 0) = -1
+ *
+ * [1] P. G. Drazin, R. S. Johnson (1989). Solitons - an introduction. Cambridge University Press
+ * [2] Prins, P. J., & Wahls, S. (2021). An accurate O(N^2) floating point algorithm for the Crum
+ * transform of the KdV equation. Communications in Nonlinear Science and Numerical Simulation,
+ * 102, Article 105782. https://doi.org/10.1016/j.cnsns.2021.105782
+ */
+
+
+#define K 2
+
+#define QUADRATIC_ERROR_SUM_TOLERANCE 1e-27
+#define MAX_QUADRATIC_ERROR 1e-28
+
+static REAL analytic_signal(REAL t, REAL x){
+ return 12.0*(3.0 + 4.0 * COSH(2.0*t-8.0*x) + COSH(4.0*t-64.0*x))/POW((3.0*COSH(t-28.0*x) + COSH(3.0*t-36.0*x)), 2);
+}
+
+
+INT main()
+{
+ INT ret_code = SUCCESS;
+
+ COMPLEX * t_grid = NULL;
+ COMPLEX * q_1 = NULL;
+ COMPLEX * q_2 = NULL;
+ COMPLEX * analytic_q_1 = NULL;
+ COMPLEX * analytic_q_2 = NULL;
+
+ UINT D = 256;
+ REAL const T[2] = {-8.0, 12.0};
+
+ t_grid = malloc(D * sizeof(COMPLEX));
+ CHECK_NOMEM(t_grid, ret_code, leave_fun);
+
+ COMPLEX const eps_t = (T[1] - T[0])/(D - 1);
+
+ for (UINT n=0; n max_quadratic_error_1) {max_quadratic_error_1 = error_1;}
+ }
+
+ #ifdef DEBUG
+ // misc_print_buf(K, bound_states, "bs1");
+ // misc_print_buf(D, q_1, "q_1");
+ printf("resulting max quadratic error: %e \n", max_quadratic_error_1);
+ printf("resulting quadratic error sum: %e \n", quadratic_error_sum_1);
+ #endif
+
+
+ // Value #2 for x
+ q_2 = malloc(D * sizeof(COMPLEX));
+ CHECK_NOMEM(q_2, ret_code, leave_fun);
+
+ analytic_q_2 = malloc(D * sizeof(COMPLEX));
+ CHECK_NOMEM(analytic_q_2, ret_code, leave_fun);
+
+ ret_code = fnft_kdvv_inverse(0, NULL, NULL, K, bound_states, normconsts_2, D, q_2, T, NULL);
+ CHECK_RETCODE(ret_code, leave_fun);
+
+ for (UINT i=0; i < D; i++) {
+ analytic_q_2[i] = analytic_signal(t_grid[i], x_2);
+ }
+
+ REAL error_2 = 0.0;
+ REAL max_quadratic_error_2 = 0.0;
+ REAL quadratic_error_sum_2 = 0.0;
+
+ for (UINT i=0; i < D; i++) {
+ error_2 = CABS(CPOW((analytic_q_2[i] - q_2[i]), 2));
+ quadratic_error_sum_2 += error_2;
+ if (error_2 > max_quadratic_error_2) {max_quadratic_error_2 = error_2;}
+ }
+
+ #ifdef DEBUG
+ // misc_print_buf(D, q_2, "q_2");
+ printf("resulting max quadratic error: %e \n", max_quadratic_error_2);
+ printf("resulting quadratic error sum: %e \n", quadratic_error_sum_2);
+ #endif
+
+ UINT is_quadratic_error_sum_in_tolerance = (quadratic_error_sum_1 < QUADRATIC_ERROR_SUM_TOLERANCE) &&
+ (quadratic_error_sum_2 < QUADRATIC_ERROR_SUM_TOLERANCE);
+ UINT is_max_quadratic_error_in_tolerance = (max_quadratic_error_1 < MAX_QUADRATIC_ERROR) &&
+ (max_quadratic_error_2 < MAX_QUADRATIC_ERROR);
+
+
+ if (is_max_quadratic_error_in_tolerance &&
+ is_quadratic_error_sum_in_tolerance)
+ {
+ ret_code = SUCCESS;
+ }
+ else {
+ ret_code = FNFT_EC_TEST_FAILED;
+ }
+
+
+leave_fun:
+ free(t_grid);
+ free(q_1);
+ free(q_2);
+ free(analytic_q_1);
+ free(analytic_q_2);
+
+ if (ret_code != SUCCESS)
+ return EXIT_FAILURE;
+ else
+ return EXIT_SUCCESS;
+}
diff --git a/test/fnft_kdvv_inverse/fnft_kdvv_inverse_test_1.c b/test/fnft_kdvv_inverse/fnft_kdvv_inverse_test_1.c
new file mode 100644
index 0000000..bc831b1
--- /dev/null
+++ b/test/fnft_kdvv_inverse/fnft_kdvv_inverse_test_1.c
@@ -0,0 +1,101 @@
+/*
+* This file is part of FNFT.
+*
+* FNFT is free software; you can redistribute it and/or
+* modify it under the terms of the version 2 of the GNU General
+* Public License as published by the Free Software Foundation.
+*
+* FNFT is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+* Contributors:
+* Fabian Fischer (Hiwi KIT) 2026
+*/
+
+#define FNFT_ENABLE_SHORT_NAMES
+
+#include
+
+#include "fnft__kdvv_inverse_testcases.h"
+#include "fnft__errwarn.h"
+#include "fnft__misc.h"
+
+/* This is a simple test case */
+
+INT main()
+{
+ INT ret_code = SUCCESS;
+
+ COMPLEX bound_states_i[5] = {I*SQRT(5.0/2.0), I*SQRT(4.0/2.0), I*SQRT(3.0/2.0), I*SQRT(2.0/2.0), I*SQRT(1.0/2.0)};
+ COMPLEX normconsts_i[5] = {1*1e7, -1*1e-5, 1*1, -1*0.1, 1*10};
+
+ fnft_kdvv_params kdvv_parameters = {
+ .D = 256,
+ .T = {-20.0, 20.0},
+ .K = 5,
+ .bound_states = bound_states_i,
+ .normconsts = normconsts_i,
+ .M = 10,
+ .XI = {-2.0, 2.0},
+ .contspec = NULL,
+ };
+
+ kdvv_parameters.contspec = malloc(kdvv_parameters.M*sizeof(COMPLEX));
+ CHECK_NOMEM(kdvv_parameters.contspec, ret_code, leave_fun);
+
+ REAL err_bnd_bound_states = 1.4e-3;
+ REAL err_bnd_spurious_bound_states = 1e-2;
+ REAL err_bnd_normconst = 4.5e-2;
+ REAL err_bnd_contspec = 1e-1;
+
+
+ ret_code = kdvv_testcases_get_spectrum_of_inverse( kdvv_parameters, err_bnd_bound_states,
+ err_bnd_spurious_bound_states,
+ err_bnd_normconst, err_bnd_contspec);
+
+ CHECK_RETCODE(ret_code, leave_fun);
+
+
+ // Check quadratic convergence
+ kdvv_parameters.D *= 2;
+ err_bnd_bound_states /= 4;
+ err_bnd_spurious_bound_states /= 4;
+ err_bnd_normconst /= 4;
+ err_bnd_contspec /= 4;
+
+ ret_code = kdvv_testcases_get_spectrum_of_inverse( kdvv_parameters, err_bnd_bound_states,
+ err_bnd_spurious_bound_states,
+ err_bnd_normconst, err_bnd_contspec);
+
+ CHECK_RETCODE(ret_code, leave_fun);
+
+ kdvv_parameters.D *= 2;
+ err_bnd_bound_states /= 4;
+ err_bnd_spurious_bound_states /= 4;
+ err_bnd_normconst /= 4;
+ err_bnd_contspec /= 4;
+
+ ret_code = kdvv_testcases_get_spectrum_of_inverse( kdvv_parameters, err_bnd_bound_states,
+ err_bnd_spurious_bound_states,
+ err_bnd_normconst, err_bnd_contspec);
+
+ CHECK_RETCODE(ret_code, leave_fun);
+
+
+leave_fun:
+ free(kdvv_parameters.contspec);
+
+ if (ret_code != SUCCESS)
+ return EXIT_FAILURE;
+ else
+ return EXIT_SUCCESS;
+}
+
+
+
+
diff --git a/test/fnft_kdvv_inverse/fnft_kdvv_inverse_test_2.c b/test/fnft_kdvv_inverse/fnft_kdvv_inverse_test_2.c
new file mode 100644
index 0000000..ef22dfe
--- /dev/null
+++ b/test/fnft_kdvv_inverse/fnft_kdvv_inverse_test_2.c
@@ -0,0 +1,111 @@
+/*
+* This file is part of FNFT.
+*
+* FNFT is free software; you can redistribute it and/or
+* modify it under the terms of the version 2 of the GNU General
+* Public License as published by the Free Software Foundation.
+*
+* FNFT is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+* Contributors:
+* Fabian Fischer (Hiwi KIT) 2026
+*/
+
+#define FNFT_ENABLE_SHORT_NAMES
+
+#include
+
+#include "fnft__kdvv_inverse_testcases.h"
+#include "fnft__errwarn.h"
+#include "fnft__misc.h"
+
+/* This is a testcase with many eigenvalues */
+
+#define K_I 19
+
+INT main()
+{
+ INT ret_code = SUCCESS;
+
+ COMPLEX bound_states_i[K_I] = { 40.0, 39.0, 38.0, 37.0, 36.0,
+ 30.0, 29.0, 28.0, 27.0, 26.0,
+ 20.0, 19.0, 18.0, 17.0, 16.0,
+ 10.0, 9.0, 8.0, 7.0};
+
+ COMPLEX normconsts_i[K_I] = { 1e20, -1e-7, 1e5, -1e3, 1e1,
+ -1e0, 1e2, -1e4, 1e-6, -1e8,
+ 1e2, -1e4, 1e6, -1e8, 1e-10,
+ -1e7, 1e-6, -1e5, 1e-9};
+
+ for (UINT i = 0; i.
+*
+* Contributors:
+* Fabian Fischer (Hiwi KIT) 2026
+*/
+
+#define FNFT_ENABLE_SHORT_NAMES
+
+#include
+
+#include "fnft__kdvv_inverse_testcases.h"
+#include "fnft__errwarn.h"
+#include "fnft__misc.h"
+
+/* This is a testcase with an assymetric window */
+
+#define K_I 8
+
+INT main()
+{
+ INT ret_code = SUCCESS;
+
+ COMPLEX bound_states_i[K_I] = { 40.0, 35.0, 28.0, 23.0, 19.0,
+ 16.0, 12.0, 4.0};
+
+ COMPLEX normconsts_i[K_I] = { 1e-3, -1e-17, 1e-15, -1e-20, 1e-1,
+ -1e-5, 1e-12, -1e-14};
+
+ for (UINT i = 0; i