diff --git a/src/interpolation/interpolation.hpp b/src/interpolation/interpolation.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7af561b114dd793a49c4fa09f81cabd456de8766
--- /dev/null
+++ b/src/interpolation/interpolation.hpp
@@ -0,0 +1,367 @@
+/*
+ * mp4_interpolation.hpp
+ *
+ *  Created on: May 4, 2017
+ *      Author: i-bird
+ */
+
+#ifndef OPENFPM_NUMERICS_SRC_INTERPOLATION_INTERPOLATION_HPP_
+#define OPENFPM_NUMERICS_SRC_INTERPOLATION_INTERPOLATION_HPP_
+
+#include "NN/CellList/MemFast.hpp"
+#include "NN/CellList/CellList.hpp"
+
+#define INTERPOLATION_ERROR_OBJECT std::runtime_error("Runtime interpolation error");
+
+constexpr int inte_m2p = 0;
+constexpr int inte_p2m = 1;
+
+template<typename T>
+struct mul_inte
+{
+	inline static void value(T & result, const T & coeff, const T & src)
+	{
+		result += coeff * src;
+	}
+};
+
+template<typename T, unsigned int N1>
+struct mul_inte<T[N1]>
+{
+	inline static void value(T (& result)[N1], const T & coeff, const T (& src)[N1])
+	{
+		for (size_t i = 0 ; i < N1 ; i++)
+			result[i] += coeff * src[i];
+	}
+};
+
+template<typename T, unsigned int N1, unsigned int N2>
+struct mul_inte<T[N1][N2]>
+{
+	inline static void value(T (& result)[N1][N2], const T & coeff, const T (& src)[N1][N2])
+	{
+		for (size_t i = 0 ; i < N1 ; i++)
+			for (size_t j = 0 ; j < N2 ; j++)
+				result[i][j] += coeff * src[i][j];
+	}
+};
+
+template<unsigned int prp_g, unsigned int prp_v,unsigned int m2p_or_p2m>
+struct inte_template
+{
+	template<typename grid, typename vector, typename iterator, typename key_type> inline static void value(grid & gd,
+			                                                          vector & vd,
+																	  const grid_dist_key_dx<vector::dims> & k_dist,
+																	  iterator & key_p,
+																	  const grid_cpu<vector::dims,aggregate<typename vector::stype>> & a_int,
+																	  const key_type & key)
+	{
+		mul_inte<typename std::remove_reference<decltype(gd.template get<prp_g>(k_dist))>::type>::value(gd.template get<prp_g>(k_dist),a_int.template get<0>(key),vd.template getProp<prp_v>(key_p));
+	}
+};
+
+template<unsigned int prp_g, unsigned int prp_v>
+struct inte_template<prp_g,prp_v,inte_m2p>
+{
+	template<typename grid, typename vector, typename iterator, typename key_type> inline static void value(grid & gd,
+			                                                          vector & vd,
+																	  const grid_dist_key_dx<vector::dims> & k_dist,
+																	  iterator & key_p,
+																	  const grid_cpu<vector::dims,aggregate<typename vector::stype>> & a_int,
+																	  const key_type & key)
+	{
+		mul_inte<typename std::remove_reference<decltype(gd.template get<prp_g>(k_dist))>::type>::value(vd.template getProp<prp_v>(key_p),a_int.template get<0>(key),gd.template get<prp_g>(k_dist));
+	}
+};
+
+
+/*! \brief return the sub-domain where this particle must be interpolated
+ *
+ * \param p particle position
+ *
+ * \return the sub-domain id
+ *
+ */
+template<typename vector, typename grid>
+inline size_t getSub(Point<vector::dims,typename vector::stype> & p,
+		             const CellList<vector::dims,typename vector::stype,Mem_fast<vector::dims,typename vector::stype>,shift<vector::dims,typename vector::stype>> & geo_cell,
+					 grid & gd)
+{
+	size_t cell = geo_cell.getCell(p);
+
+	for (size_t i = 0 ; i < geo_cell.getNelements(cell) ; i++)
+	{
+		size_t ns = geo_cell.get(cell,i);
+
+		if (gd.getDecomposition().getSubDomain(ns).isInside(p))
+			return ns;
+	}
+
+#ifdef SE_CLASS1
+	std::cerr << __FILE__ << ":" << __LINE__ << " Error: " << std::endl;
+	ACTION_ON_ERROR(INTERPOLATION_ERROR_OBJECT)
+#endif
+
+	return (size_t)-1;
+}
+
+
+template<unsigned int prp_g, unsigned int prp_v, unsigned int m2p_or_p2m, typename kernel, typename iterator, typename vector, typename grid, typename grid_inte>
+																 inline void inte_calc(iterator & it,
+		                     	 	 	 	 	 	 	 	 	 const vector & vd,
+																 const Box<vector::dims,typename vector::stype> & domain,
+																 int (& ip)[vector::dims][kernel::np],
+																 grid & gd,
+																 const typename vector::stype (& dx)[vector::dims],
+																 typename vector::stype (& xp)[vector::dims],
+																 const grid_inte & a_int,
+																 typename vector::stype (& a)[vector::dims][kernel::np],
+																 typename vector::stype (& x)[vector::dims][kernel::np],
+																 size_t (& sz)[vector::dims],
+																 const CellList<vector::dims,typename vector::stype,Mem_fast<vector::dims,typename vector::stype>,shift<vector::dims,typename vector::stype>> & geo_cell)
+{
+	auto key_p = it.get();
+
+	Point<vector::dims,typename vector::stype> p = vd.getPos(key_p);
+
+	// On which sub-domain we interpolate the particle
+	size_t sub = getSub<vector>(p,geo_cell,gd);
+
+	typename vector::stype x0[vector::dims];
+
+	// calculate the position of the particle in the grid
+	// coordinated
+	for (size_t i = 0 ; i < vector::dims ; i++)
+		x0[i] = (p.get(i)-domain.getLow(i))*dx[i];
+
+	// convert into integer
+	for (size_t i = 0 ; i < vector::dims ; i++)
+		ip[i][0] = (int)x0[i];
+
+	// convert the global grid position into local grid position
+	grid_key_dx<vector::dims> base;
+
+	for (size_t i = 0 ; i < vector::dims ; i++)
+		base.set_d(i,ip[i][0] - gd.getLocalGridsInfo().get(sub).origin.get(i) - (long int)kernel::np/2 + 1);
+
+	// convenient grid of points
+
+	for (size_t j = 0 ; j < kernel::np-1 ; j++)
+	{
+		for (size_t i = 0 ; i < vector::dims ; i++)
+			ip[i][j+1] = (int)ip[i][j]+1;
+	}
+
+	for (size_t i = 0 ; i < vector::dims ; i++)
+		xp[i] = x0[i] - ip[i][0];
+
+	for (long int j = 0 ; j < kernel::np ; j++)
+	{
+		for (size_t i = 0 ; i < vector::dims ; i++)
+			x[i][j] = - xp[i] + typename vector::stype((long int)j - (long int)kernel::np/2 + 1);
+	}
+
+	for (size_t j = 0 ; j < kernel::np ; j++)
+	{
+		for (size_t i = 0 ; i < vector::dims ; i++)
+			a[i][j] = kernel::value(x[i][j],j);
+	}
+
+	grid_sm<vector::dims,void> gs(sz);
+	grid_key_dx_iterator<vector::dims> kit(gs);
+
+	while (kit.isNext())
+	{
+		auto key = kit.get();
+
+		a_int.template get<0>(key) = 1;
+
+		for (size_t i = 0 ; i < vector::dims ; i++)
+			a_int.template get<0>(key) *= a[i][key.get(i)];
+
+		++kit;
+	}
+
+	grid_key_dx_iterator<vector::dims> kit2(gs);
+	grid_dist_key_dx<vector::dims> k_dist;
+	k_dist.setSub(sub);
+
+	while (kit2.isNext())
+	{
+		auto key = kit2.get();
+
+		for (size_t i = 0 ; i < vector::dims ; i++)
+			k_dist.getKeyRef().set_d(i,key.get(i) + base.get(i));
+
+		inte_template<prp_g,prp_v,m2p_or_p2m>::value(gd,vd,k_dist,key_p,a_int,key);
+
+		++kit2;
+	}
+}
+
+
+template<typename vector,typename grid, typename kernel>
+class interpolate
+{
+	CellList<vector::dims,typename vector::stype,Mem_fast<vector::dims,typename vector::stype>,shift<vector::dims,typename vector::stype>> geo_cell;
+
+	struct Box_vol
+	{
+		Box<vector::dims,size_t> bv;
+
+		size_t vol;
+
+		void operator<(const Box_vol & bv)
+		{
+			return vol < bv.vol;
+		}
+	};
+
+	//! particles
+	vector & vd;
+
+	//! grid
+	grid & gd;
+
+
+public:
+
+	interpolate(vector & vd, grid & gd)
+	:vd(vd),gd(gd)
+	{
+		// get the processor bounding box in grid units
+		Box<vector::dims,typename vector::stype> bb = gd.getDecomposition().getProcessorBounds();
+		Box<vector::dims,typename vector::stype> bunit = gd.getDecomposition().getCellDecomposer().getCellBox();
+
+		size_t div[vector::dims];
+
+		for (size_t i = 0 ; i < vector::dims ; i++)
+			div[i] = (bb.getHigh(i) - bb.getLow(i)) / bunit.getHigh(i);
+
+		geo_cell.Initialize(bb,div);
+
+		// Now draw the domain into the cell list
+
+		auto & dec = gd.getDecomposition();
+
+		for (size_t i = 0 ; i < dec.getNSubDomain() ; i++)
+		{
+			const Box<vector::dims,typename vector::stype> & bx = dec.getSubDomain(i);
+
+			// get the cells this box span
+			const grid_key_dx<vector::dims> p1 = geo_cell.getCellGrid(bx.getP1());
+			const grid_key_dx<vector::dims> p2 = geo_cell.getCellGrid(bx.getP2());
+
+			// Get the grid and the sub-iterator
+			auto & gi = geo_cell.getGrid();
+			grid_key_dx_iterator_sub<vector::dims> g_sub(gi,p1,p2);
+
+			// add the box-id to the cell list
+			while (g_sub.isNext())
+			{
+				auto key = g_sub.get();
+				geo_cell.addCell(gi.LinId(key),i);
+				++g_sub;
+			}
+		}
+	};
+
+	template<unsigned int prp_v, unsigned int prp_g> void p2m(vector & vd, grid & gd)
+	{
+#ifdef SE_CLASS1
+
+		if (!vd.getDecomposition().is_equal_ng(gd.getDecomposition()) )
+		{
+			std::cerr << __FILE__ << ":" << __LINE__ << " Error: the distribution of the vector of particles" <<
+					" and the grid is different. In order to interpolate the two data structure must have the" <<
+					" same decomposition" << std::endl;
+
+			ACTION_ON_ERROR(INTERPOLATION_ERROR_OBJECT)
+		}
+
+#endif
+
+		Box<vector::dims,typename vector::stype> domain = vd.getDecomposition().getDomain();
+
+		// grid spacing
+		typename vector::stype dx[vector::dims];
+
+		for (size_t i = 0 ; i < vector::dims ; i++)
+			dx[i] = 1.0/gd.spacing(i);
+
+		// point position
+		typename vector::stype xp[vector::dims];
+
+		int ip[vector::dims][kernel::np];
+		typename vector::stype x[vector::dims][kernel::np];
+		typename vector::stype a[vector::dims][kernel::np];
+
+		size_t sz[vector::dims];
+
+		for (size_t i = 0 ; i < vector::dims ; i++)
+			sz[i] = kernel::np;
+
+		grid_cpu<vector::dims,aggregate<typename vector::stype>> a_int(sz);
+		a_int.setMemory();
+
+		auto it = vd.getDomainIterator();
+
+		while (it.isNext() == true)
+		{
+			inte_calc<prp_g,prp_v,inte_p2m,kernel>(it,vd,domain,ip,gd,dx,xp,a_int,a,x,sz,geo_cell);
+
+			++it;
+		}
+	}
+
+	template<unsigned int prp_v, unsigned int prp_g> void m2p(grid & gd, vector & vd)
+	{
+#ifdef SE_CLASS1
+
+		if (!vd.getDecomposition().is_equal_ng(gd.getDecomposition()) )
+		{
+			std::cerr << __FILE__ << ":" << __LINE__ << " Error: the distribution of the vector of particles" <<
+					" and the grid is different. In order to interpolate the two data structure must have the" <<
+					" same decomposition" << std::endl;
+
+			ACTION_ON_ERROR(INTERPOLATION_ERROR_OBJECT)
+		}
+
+#endif
+
+		Box<vector::dims,typename vector::stype> domain = vd.getDecomposition().getDomain();
+
+		// grid spacing
+		typename vector::stype dx[vector::dims];
+
+		for (size_t i = 0 ; i < vector::dims ; i++)
+			dx[i] = 1.0/gd.spacing(i);
+
+		// point position
+		typename vector::stype xp[vector::dims];
+
+		int ip[vector::dims][kernel::np];
+		typename vector::stype x[vector::dims][kernel::np];
+		typename vector::stype a[vector::dims][kernel::np];
+
+		size_t sz[vector::dims];
+
+		for (size_t i = 0 ; i < vector::dims ; i++)
+			sz[i] = kernel::np;
+
+		grid_cpu<vector::dims,aggregate<typename vector::stype>> a_int(sz);
+		a_int.setMemory();
+
+		auto it = vd.getDomainIterator();
+
+		while (it.isNext() == true)
+		{
+			inte_calc<prp_g,prp_v,inte_m2p,kernel>(it,vd,domain,ip,gd,dx,xp,a_int,a,x,sz,geo_cell);
+
+			++it;
+		}
+	}
+
+};
+
+#endif /* OPENFPM_NUMERICS_SRC_INTERPOLATION_INTERPOLATION_HPP_ */
diff --git a/src/interpolation/interpolation_unit_tests.hpp b/src/interpolation/interpolation_unit_tests.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..888341672b4dc113dd297af2cb4f030e0b5b014a
--- /dev/null
+++ b/src/interpolation/interpolation_unit_tests.hpp
@@ -0,0 +1,423 @@
+/*
+ * interpolation_unit_tests.hpp
+ *
+ *  Created on: May 5, 2017
+ *      Author: i-bird
+ */
+
+#ifndef OPENFPM_NUMERICS_SRC_INTERPOLATION_INTERPOLATION_UNIT_TESTS_HPP_
+#define OPENFPM_NUMERICS_SRC_INTERPOLATION_INTERPOLATION_UNIT_TESTS_HPP_
+
+#include "interpolation/mp4_kernel.hpp"
+#include "interpolation/interpolation.hpp"
+#include "interpolation/z_spline.hpp"
+
+BOOST_AUTO_TEST_SUITE( interpolation_test )
+
+template<typename grid, unsigned int mom_p> void momenta_grid(grid & gd,typename grid::stype (& mom_tot)[grid::dims])
+{
+	auto it = gd.getDomainGhostIterator();
+
+	for (size_t i = 0 ; i < grid::dims ; i++)
+		mom_tot[i] = 0.0;
+
+	while (it.isNext())
+	{
+		auto key = it.get();
+		auto key_g = gd.getGKey(key);
+
+		for (size_t i = 0 ; i < grid::dims ; i++)
+		{
+			typename grid::stype coord = gd.spacing(i)*key_g.get(i);
+
+			mom_tot[i] += boost::math::pow<mom_p>(coord)*gd.template getProp<0>(key);
+		}
+
+		++it;
+	}
+}
+
+template<typename grid, unsigned int mom_p> void momenta_grid_domain(grid & gd,typename grid::stype (& mom_tot)[grid::dims])
+{
+	auto it = gd.getDomainIterator();
+
+	for (size_t i = 0 ; i < grid::dims ; i++)
+		mom_tot[i] = 0.0;
+
+	while (it.isNext())
+	{
+		auto key = it.get();
+		auto key_g = gd.getGKey(key);
+
+		for (size_t i = 0 ; i < grid::dims ; i++)
+		{
+			typename grid::stype coord = gd.spacing(i)*key_g.get(i);
+
+			mom_tot[i] += boost::math::pow<mom_p>(coord)*gd.template getProp<0>(key);
+		}
+
+		++it;
+	}
+}
+
+template<typename vector, unsigned int mom_p> void momenta_vector(vector & vd,typename vector::stype (& mom_tot)[vector::dims])
+{
+	auto it = vd.getDomainIterator();
+
+	for (size_t i = 0 ; i < vector::dims ; i++)
+		mom_tot[i] = 0.0;
+
+	while (it.isNext())
+	{
+		auto key = it.get();
+
+		for (size_t i = 0 ; i < vector::dims ; i++)
+		{
+			typename vector::stype coord = vd.getPos(key)[i];
+
+			mom_tot[i] += boost::math::pow<mom_p>(coord)*vd.template getProp<0>(key);
+		}
+
+		++it;
+	}
+}
+
+
+BOOST_AUTO_TEST_CASE( interpolation_full_test )
+{
+	Box<2,float> domain({0.0,0.0},{1.0,1.0});
+	size_t sz[2] = {64,64};
+
+	Ghost<2,long int> gg(3);
+	Ghost<2,float> gv(0.01);
+
+	size_t bc_v[2] = {PERIODIC,PERIODIC};
+
+	{
+	vector_dist<2,float,aggregate<float>> vd(4096,domain,bc_v,gv);
+	grid_dist_id<2,float,aggregate<float>> gd(vd.getDecomposition(),sz,gg);
+
+	// set one particle on vd
+
+	auto it = vd.getDomainIterator();
+
+	while (it.isNext())
+	{
+		auto p = it.get();
+
+		vd.getPos(p)[0] = (double)rand()/RAND_MAX;
+		vd.getPos(p)[1] = (double)rand()/RAND_MAX;
+		vd.getPos(p)[2] = (double)rand()/RAND_MAX;
+
+		vd.getProp<0>(p) = 5.0/*(double)rand()/RAND_MAX*/;
+
+		++it;
+	}
+
+	vd.map();
+
+	// Reset the grid
+
+	auto it2 = gd.getDomainGhostIterator();
+
+	while (it2.isNext())
+	{
+		auto key = it2.get();
+
+		gd.template get<0>(key) = 0.0;
+
+		++it2;
+	}
+
+	interpolate<decltype(vd),decltype(gd),mp4_kernel<float>> inte(vd,gd);
+
+	inte.p2m<0,0>(vd,gd);
+
+	float mg[2];
+	float mv[2];
+
+	momenta_grid<decltype(gd),0>(gd,mg);
+	momenta_vector<decltype(vd),0>(vd,mv);
+
+	BOOST_REQUIRE_CLOSE(mg[0],mv[0],0.001);
+	BOOST_REQUIRE_CLOSE(mg[1],mv[1],0.001);
+
+	momenta_grid<decltype(gd),1>(gd,mg);
+	momenta_vector<decltype(vd),1>(vd,mv);
+
+	BOOST_REQUIRE_CLOSE(mg[0],mv[0],0.001);
+	BOOST_REQUIRE_CLOSE(mg[1],mv[1],0.001);
+
+	momenta_grid<decltype(gd),2>(gd,mg);
+	momenta_vector<decltype(vd),2>(vd,mv);
+
+	BOOST_REQUIRE_CLOSE(mg[0],mv[0],0.001);
+	BOOST_REQUIRE_CLOSE(mg[1],mv[1],0.001);
+
+	// We have to do a ghost get before interpolating m2p
+	// Before doing mesh to particle particle must be arranged
+	// into a grid like
+
+	vd.clear();
+
+	auto it4 = vd.getGridIterator(sz);
+
+	while(it4.isNext())
+	{
+		auto key = it4.get();
+
+		vd.add();
+
+		vd.getLastPos()[0] = key.get(0) * it4.getSpacing(0) + domain.getLow(0) + 0.1*it4.getSpacing(0);
+		vd.getLastPos()[1] = key.get(1) * it4.getSpacing(1) + domain.getLow(1) + 0.1*it4.getSpacing(1);
+
+		vd.getLastProp<0>() = 0.0;
+
+		++it4;
+	}
+
+	// Reset also the grid
+
+	auto it5 = gd.getDomainGhostIterator();
+
+	while(it5.isNext())
+	{
+		auto key = it5.get();
+
+		gd.get<0>(key) = 0.0;
+
+		++it5;
+	}
+	gd.ghost_get<0>();
+
+	grid_key_dx<2> start({3,3});
+	grid_key_dx<2> stop({(long int)gd.size(0) - 3,(long int)gd.size(1) - 3});
+
+	auto it6 = gd.getSubDomainIterator(start,stop);
+	while(it6.isNext())
+	{
+		auto key = it6.get();
+
+		gd.get<0>(key) = (double)rand()/RAND_MAX;;
+
+		++it6;
+	}
+	gd.ghost_get<0>();
+
+	vd.map();
+	gd.ghost_get<0>();
+	inte.m2p<0,0>(gd,vd);
+
+	momenta_grid_domain<decltype(gd),0>(gd,mg);
+	momenta_vector<decltype(vd),0>(vd,mv);
+
+	BOOST_REQUIRE_CLOSE(mg[0],mv[0],0.001);
+	BOOST_REQUIRE_CLOSE(mg[1],mv[1],0.001);
+
+	momenta_grid_domain<decltype(gd),1>(gd,mg);
+	momenta_vector<decltype(vd),1>(vd,mv);
+
+	BOOST_REQUIRE_CLOSE(mg[0],mv[0],0.001);
+	BOOST_REQUIRE_CLOSE(mg[1],mv[1],0.001);
+
+	momenta_grid_domain<decltype(gd),2>(gd,mg);
+	momenta_vector<decltype(vd),2>(vd,mv);
+
+	BOOST_REQUIRE_CLOSE(mg[0],mv[0],0.001);
+	BOOST_REQUIRE_CLOSE(mg[1],mv[1],0.001);
+
+	}
+}
+
+BOOST_AUTO_TEST_CASE( int_kernel_test )
+{
+		mp4_kernel<float> mp4;
+
+		float tot = 0.0;
+
+		// Check momenta 0
+
+		tot += mp4.value(-1.3f,0);
+		tot += mp4.value(-0.3f,1);
+		tot += mp4.value(0.7f,2);
+		tot += mp4.value(1.7f,3);
+
+		BOOST_REQUIRE_CLOSE(tot,1.0f,0.001);
+
+		// Check momenta 1
+
+		tot = 0.0;
+
+		tot += -1.3f*mp4.value(-1.3f,0);
+		tot += -0.3f*mp4.value(-0.3f,1);
+		tot += 0.7f*mp4.value(0.7f,2);
+		tot += 1.7f*mp4.value(1.7f,3);
+
+		BOOST_REQUIRE_SMALL(tot,0.001f);
+
+		// Check momenta 2
+
+		tot = 0.0;
+
+		tot += (1.3f)*(1.3f)*mp4.value(-1.3f,0);
+		tot += (0.3f)*(0.3f)*mp4.value(-0.3f,1);
+		tot += (0.7f)*(0.7f)*mp4.value(0.7f,2);
+		tot += (1.7f)*(1.7f)*mp4.value(1.7f,3);
+
+		BOOST_REQUIRE_SMALL(tot,0.001f);
+
+
+		//////// Check zeta 1
+
+		tot = 0.0;
+
+		z_kernel<float,1> zk1;
+
+		tot += zk1.value(-0.3f,0);
+		tot += zk1.value(0.7f,1);
+
+		BOOST_REQUIRE_CLOSE(tot,1.0f,0.001);
+
+		//////// zeta 2 is equivalent to mp4 we do not test
+
+		//////// zeta 3
+
+		z_kernel<float,3> zk3;
+
+		tot = 0.0;
+
+		// Check momenta 0
+
+		tot += zk3.value(-2.3f,0);
+		tot += zk3.value(-1.3f,1);
+		tot += zk3.value(-0.3f,2);
+		tot += zk3.value(0.7f,3);
+		tot += zk3.value(1.7f,4);
+		tot += zk3.value(2.7f,5);
+
+		BOOST_REQUIRE_CLOSE(tot,1.0f,0.001);
+
+		// Check momenta 1
+
+		tot = 0.0;
+
+		tot += -2.3*zk3.value(-2.3f,0);
+		tot += -1.3*zk3.value(-1.3f,1);
+		tot += -0.3*zk3.value(-0.3f,2);
+		tot += 0.7*zk3.value(0.7f,3);
+		tot += 1.7*zk3.value(1.7f,4);
+		tot += 2.7*zk3.value(2.7f,5);
+
+		BOOST_REQUIRE_SMALL(tot,0.001f);
+
+		// Check momenta 2
+
+		tot = 0.0;
+
+		tot += 2.3*2.3*zk3.value(-2.3f,0);
+		tot += 1.3*1.3*zk3.value(-1.3f,1);
+		tot += 0.3*0.3*zk3.value(-0.3f,2);
+		tot += 0.7*0.7*zk3.value(0.7f,3);
+		tot += 1.7*1.7*zk3.value(1.7f,4);
+		tot += 2.7*2.7*zk3.value(2.7f,5);
+
+		BOOST_REQUIRE_SMALL(tot,0.001f);
+
+		// Check momenta 3
+
+		tot = 0.0;
+
+		tot += -2.3*-2.3*-2.3*zk3.value(-2.3f,0);
+		tot += -1.3*-1.3*-1.3*zk3.value(-1.3f,1);
+		tot += -0.3*-0.3*-0.3*zk3.value(-0.3f,2);
+		tot += 0.7*0.7*0.7*zk3.value(0.7f,3);
+		tot += 1.7*1.7*1.7*zk3.value(1.7f,4);
+		tot += 2.7*2.7*2.7*zk3.value(2.7f,5);
+
+		BOOST_REQUIRE_SMALL(tot,0.001f);
+
+
+		// z4
+
+		z_kernel<float,4> zk4;
+
+		// Check momenta 0
+
+		tot = 0.0;
+
+		tot += zk4.value(-3.3f,0);
+		tot += zk4.value(-2.3f,1);
+		tot += zk4.value(-1.3f,2);
+		tot += zk4.value(-0.3f,3);
+		tot += zk4.value(0.7f,4);
+		tot += zk4.value(1.7f,5);
+		tot += zk4.value(2.7f,6);
+		tot += zk4.value(3.7f,7);
+
+		BOOST_REQUIRE_CLOSE(tot,1.0f,0.001);
+
+		// Check momenta 1
+
+		tot = 0.0;
+
+		tot += -3.3*zk4.value(-3.3f,0);
+		tot += -2.3*zk4.value(-2.3f,1);
+		tot += -1.3*zk4.value(-1.3f,2);
+		tot += -0.3*zk4.value(-0.3f,3);
+		tot += 0.7*zk4.value(0.7f,4);
+		tot += 1.7*zk4.value(1.7f,5);
+		tot += 2.7*zk4.value(2.7f,6);
+		tot += 3.7*zk4.value(3.7f,7);
+
+		BOOST_REQUIRE_SMALL(tot,0.001f);
+
+		// Check momenta 2
+
+		tot = 0.0;
+
+		tot += 3.3*3.3*zk4.value(-3.3f,0);
+		tot += 2.3*2.3*zk4.value(-2.3f,1);
+		tot += 1.3*1.3*zk4.value(-1.3f,2);
+		tot += 0.3*0.3*zk4.value(-0.3f,3);
+		tot += 0.7*0.7*zk4.value(0.7f,4);
+		tot += 1.7*1.7*zk4.value(1.7f,5);
+		tot += 2.7*2.7*zk4.value(2.7f,6);
+		tot += 3.7*3.7*zk4.value(3.7f,7);
+
+		BOOST_REQUIRE_SMALL(tot,0.001f);
+
+		// Check momenta 3
+
+		tot = 0.0;
+
+		tot += -3.3*-3.3*-3.3*zk4.value(-3.3f,0);
+		tot += -2.3*-2.3*-2.3*zk4.value(-2.3f,1);
+		tot += -1.3*-1.3*-1.3*zk4.value(-1.3f,2);
+		tot += -0.3*-0.3*-0.3*zk4.value(-0.3f,3);
+		tot += 0.7*0.7*0.7*zk4.value(0.7f,4);
+		tot += 1.7*1.7*1.7*zk4.value(1.7f,5);
+		tot += 2.7*2.7*2.7*zk4.value(2.7f,6);
+		tot += 3.7*3.7*3.7*zk4.value(3.7f,7);
+
+		BOOST_REQUIRE_SMALL(tot,0.001f);
+
+		// Check momenta 4
+
+		tot = 0.0;
+
+		tot += -3.3*-3.3*-3.3*-3.3*zk4.value(-3.3f,0);
+		tot += -2.3*-2.3*-2.3*-2.3*zk4.value(-2.3f,1);
+		tot += -1.3*-1.3*-1.3*-1.3*zk4.value(-1.3f,2);
+		tot += -0.3*-0.3*-0.3*-0.3*zk4.value(-0.3f,3);
+		tot += 0.7*0.7*0.7*0.7*zk4.value(0.7f,4);
+		tot += 1.7*1.7*1.7*1.7*zk4.value(1.7f,5);
+		tot += 2.7*2.7*2.7*2.7*zk4.value(2.7f,6);
+		tot += 3.7*3.7*3.7*3.7*zk4.value(3.7f,7);
+
+		BOOST_REQUIRE_SMALL(tot,0.001f);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+
+#endif /* OPENFPM_NUMERICS_SRC_INTERPOLATION_INTERPOLATION_UNIT_TESTS_HPP_ */
diff --git a/src/interpolation/z_spline.hpp b/src/interpolation/z_spline.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6988dada28f91854c9d163919f9388b12a6ffbf6
--- /dev/null
+++ b/src/interpolation/z_spline.hpp
@@ -0,0 +1,106 @@
+/*
+ * z_spline.hpp
+ *
+ *  Created on: May 8, 2017
+ *      Author: i-bird
+ */
+
+#ifndef OPENFPM_NUMERICS_SRC_INTERPOLATION_Z_SPLINE_HPP_
+#define OPENFPM_NUMERICS_SRC_INTERPOLATION_Z_SPLINE_HPP_
+
+#include "mp4_kernel.hpp"
+
+template<typename st, unsigned int ord>
+class z_kernel
+{
+public:
+
+	static const int np = 4;
+
+	static inline st value(st x, size_t i)
+	{
+		std::cerr << __FILE__ << ":" << __LINE__ << ": Error this order has not been implemented" << std::endl;
+		return 0.0;
+	}
+};
+
+template<typename st>
+class z_kernel<st,1>
+{
+public:
+
+	static const int np = 2;
+
+	static inline st value(st x, size_t i)
+	{
+		if (i == 0)
+			return x + 1;
+		else if (i == 1)
+			return 1-x;
+		return 0.0;
+	}
+};
+
+template<typename st>
+class z_kernel<st,2>: public mp4_kernel<st>
+{
+
+};
+
+template<typename st>
+class z_kernel<st,3>
+{
+public:
+
+	static const int np = 6;
+
+	static inline st value(st x, size_t i)
+	{
+		if (i == 0)
+			return 18.0 + ( 38.25 + (31.875 + ( 313.0/24.0 + (2.625 + 5.0/24.0*x)*x)*x)*x)*x;
+		else if (i == 1)
+			return -4.0 + (-18.75 + (-30.625 + (-545.0/24.0 + ( -7.875 - 25.0/24.0*x)*x)*x)*x)*x;
+		else if (i == 2)
+			return 1.0 + (-1.25 +(35.0/12.0 +(5.25 + 25.0/12.0*x)*x)*x)*x*x;
+		else if (i == 3)
+			return 1.0 + (-1.25 +(-35.0/12.0 +(5.25 - 25.0/12.0*x)*x)*x)*x*x;
+		else if (i == 4)
+			return -4.0 + (18.75 + (-30.625 + (545.0/24.0 + ( -7.875 + 25.0/24.0*x)*x)*x)*x)*x;
+		else if (i == 5)
+			return 18.0 + (-38.25 + (31.875 + (-313.0/24.0 + (2.625 - 5.0/24.0*x)*x)*x)*x)*x;
+
+		return 0.0;
+	}
+};
+
+template<typename st>
+class z_kernel<st,4>
+{
+public:
+
+	static const int np = 8;
+
+	static inline st value(st x, size_t i)
+	{
+		if (i == 0)
+			return 726.4 + ( 1491.2 + (58786.0/45.0 + ( 633.0 + (26383.0/144.0 + (22807.0/720.0 + (727.0/240.0 + 89.0/720.0*x)*x)*x)*x)*x)*x)*x;
+		else if (i == 1)
+			return -440 +( -1297.45 + ( -117131/72.0 + ( -1123.5 + ( -66437.0/144.0 + ( -81109.0/720.0 + ( -727.0/48.0 - 623.0/720.0*x)*x)*x)*x)*x)*x)*x;
+		else if (i == 2)
+			return 27.6 + (8617.0/60.0 +(321.825 +(395.5 +( 284.8125 + (119.7875 +(27.2625 + 623.0/240.0*x)*x)*x)*x)*x)*x)*x;
+		else if (i == 3)
+			return 1.0 + (( -49.0/36.0 + (( -959.0/144.0 + ( -2569.0/144.0 + ( -727.0/48.0 - 623.0/144.0*x)*x)*x)*x)*x)*x)*x;
+		else if (i == 4)
+			return 1.0 + (( -49.0/36.0 + (( -959.0/144.0 + ( 2569.0/144.0 + ( -727.0/48.0 + 623.0/144.0*x)*x)*x)*x)*x)*x)*x;
+		else if (i == 5)
+			return 27.6 + (-8617.0/60.0 +(321.825 +(-395.5 +( 284.8125 + (-119.7875 +(27.2625 - 623.0/240.0*x)*x)*x)*x)*x)*x)*x;
+		else if (i == 6)
+			return -440 +( 1297.45 + ( -117131/72.0 + ( 1123.5 + ( -66437.0/144.0 + ( 81109.0/720.0 + ( -727.0/48.0 + 623.0/720.0*x)*x)*x)*x)*x)*x)*x;
+		else if (i == 7)
+			return 726.4 + ( -1491.2 + (58786.0/45.0 + ( -633.0 + (26383.0/144.0 + (-22807.0/720.0 + (727.0/240.0 - 89.0/720.0*x)*x)*x)*x)*x)*x)*x;
+
+		return 0.0;
+	}
+};
+
+#endif /* OPENFPM_NUMERICS_SRC_INTERPOLATION_Z_SPLINE_HPP_ */