 }
 
 
+void layer_create_sparse_exprs(fppoly_t* const fp, const double* filter_weights, const double* filter_bias,
+                               const size_t* input_size, const size_t* filter_size, const size_t num_filters, const size_t* strides,
+                               const bool is_valid_padding, const bool has_bias)
+{
+    const size_t num_pixels = input_size[0]*input_size[1]*input_size[2];
+
+    size_t output_size[3];
+
+    if(is_valid_padding)
+    {
+        output_size[0] = ceil((double)(input_size[0] - filter_size[0] + 1)/(double)strides[0]);
+        output_size[1] = ceil((double)(input_size[1] - filter_size[1] + 1)/(double)strides[1]);
+    }
+    else
+    {
+        output_size[0] = ceil((double)input_size[0]/(double)strides[0]);
+        output_size[1] = ceil((double)input_size[1]/(double)strides[1]);
+    }
+
+    output_size[2] = num_filters;
+
+    const size_t num_out_neurons = output_size[0]*output_size[1]*output_size[2];
+    fppoly_add_new_layer(fp, num_out_neurons, CONV, RELU);
+    neuron_t** out_neurons = fp->layers[fp->numlayers - 1]->neurons;
+
+    long int pad_along_height = 0;
+    long int pad_along_width = 0;
+    long int pad_top = 0;
+    long int pad_left = 0;
+
+    if(!is_valid_padding)
+    {
+        if(input_size[0]%strides[0] == 0)
+        {
+            const long int tmp = filter_size[0] - strides[0];
+            pad_along_height = max(tmp, long(0));
+        }
+        else
+        {
+            const long int tmp = filter_size[0] - (input_size[0]%strides[0]);
+            pad_along_height = max(tmp, long(0));
+        }
+
+        if(input_size[1]%strides[1] == 0)
+        {
+            const long int tmp = filter_size[1] - strides[1];
+            pad_along_width = max(tmp, long(0));
+        }
+        else
+        {
+            const long int tmp = filter_size[1] - (input_size[1]%strides[1]);
+            pad_along_width = max(tmp, long(0));
+        }
+
+        pad_top = pad_along_height/2;
+        pad_left = pad_along_width/2;
+    }
+
+    const size_t size = filter_size[0]*filter_size[1]*input_size[2]*output_size[2];
+
+    double* filter_weights_tmp = (double*) malloc(size*sizeof(double));
+    double* filter_bias_tmp = (double*) malloc(output_size[2]*sizeof(double));
+
+    size_t* input_size_tmp = (size_t*) malloc(3*sizeof(size_t));
+    size_t* output_size_tmp = (size_t*) malloc(3*sizeof(size_t));
+    size_t* filter_size_tmp = (size_t*) malloc(2*sizeof(size_t));
+    size_t* strides_tmp = (size_t*) malloc(2*sizeof(size_t));
+
+    cudaMemcpy(filter_weights_tmp, filter_weights, size*sizeof(double), cudaMemcpyHostToHost);
+    cudaMemcpy(filter_bias_tmp, filter_bias, output_size[2]*sizeof(double), cudaMemcpyHostToHost);
+
+    cudaMemcpy(input_size_tmp, input_size, 3*sizeof(size_t), cudaMemcpyHostToHost);
+    cudaMemcpy(output_size_tmp, output_size, 3*sizeof(size_t), cudaMemcpyHostToHost);
+    cudaMemcpy(filter_size_tmp, filter_size, 2*sizeof(size_t), cudaMemcpyHostToHost);
+    cudaMemcpy(strides_tmp, strides, 2*sizeof(size_t), cudaMemcpyHostToHost);
+
+    device_layer_create_sparse_exprs(out_neurons, filter_weights_tmp, filter_bias_tmp, input_size_tmp, output_size_tmp,
+                                     filter_size_tmp, strides_tmp, has_bias, pad_top, pad_left, num_pixels);
+
+    free(filter_weights_tmp);
+    free(filter_bias_tmp);
+
+    free(input_size_tmp);
+    free(output_size_tmp);
+    free(filter_size_tmp);
+    free(strides_tmp);
+}
+
+
 void conv_handle_first_layer(elina_manager_t* man, elina_abstract0_t* element, const double* filter_weights, const double* filter_bias,
                              const size_t* input_size, const size_t* filter_size, const size_t num_filters, const size_t* strides,
                              const bool is_valid_padding, const bool has_bias)

            first = true;
        }

        std::swap(a, b);
        std::swap(mirror_1_a, mirror_1_b);
        std::swap(mirror_2_a, mirror_2_b);

        i = 0;
        startl = 0;



__global__
void device_layer_create_sparse_exprs(expr_t** expr_array, const double* weights, const double* bias, const size_t* dims, const size_t num_out_neurons, const size_t num_in_neurons)
{
    size_t i = blockIdx.x;

    if(i < num_out_neurons)
    {
        const double* weight_i = weights + i*num_in_neurons;
        const size_t* dims_i = dims + i*num_in_neurons;
        const double bias_i = bias[i];
        expr_array[i] = create_sparse_expr(weight_i, bias_i, dims_i, num_in_neurons);
        sort_sparse_expr(expr_array[i]);
    }
}


void layer_create_sparse_exprs(expr_t** expr_array, const double* weights, const double* bias, const size_t* dims, const size_t num_out_neurons, const size_t num_in_neurons)
{
    double* tmp_weights;
    cudaMalloc((void**) &tmp_weights, num_out_neurons*num_in_neurons*sizeof(double));
    size_t* tmp_dims;
    cudaMalloc((void**) &tmp_dims   , num_out_neurons*num_in_neurons*sizeof(size_t));

    cudaMemcpy(tmp_weights, weights, num_out_neurons*num_in_neurons*sizeof(double), cudaMemcpyHostToDevice);
    cudaMemcpy(tmp_dims   , dims   , num_out_neurons*num_in_neurons*sizeof(size_t), cudaMemcpyHostToDevice);

    double* tmp_bias;
    cudaMalloc((void**) &tmp_bias, num_out_neurons*sizeof(double));
    cudaMemcpy(tmp_bias, bias, num_out_neurons*sizeof(double), cudaMemcpyHostToDevice);

    device_layer_create_sparse_exprs<<<num_out_neurons, 1>>>(expr_array, tmp_weights, tmp_bias, tmp_dims, num_out_neurons, num_in_neurons);

    cudaFree(tmp_weights);
    cudaFree(tmp_dims);
    cudaFree(tmp_bias);
}


