/*
 * Copyright (c) 2008 Filip Niksic
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 **/

// 
// File:   triLU.cpp
// Author: filip
//
// Created on 2008. ožujak 30, 22:57
//

#include "MPIAppDLL.hpp"
#include "triLU.hpp"
#include "matrix.hpp"
#include "H5Cpp.h"
//#include "myHdf5.hpp"

#include <iostream>
#include <cstdlib>
#include <numeric>
#include <algorithm>

namespace trilu {
    
    struct {
        int NP; // number of processes
        int myWorldRank;
        int myReverseRank;
        MPI::Group myWorldGroup;
        MPI::Group myReverseGroup;
        MPI::Intracomm myReverseComm;
    } comm;
    
    int setCommunication() {
        comm.NP = MPI::COMM_WORLD.Get_size();
        
        // Kreiranje "obrnute" grupe i komunikatora koji kasnije sluze za
        // izvodjenje paralelnog sufiksa.
        comm.myWorldGroup = MPI::COMM_WORLD.Get_group();
        
        int *ranks(new int[comm.NP]);
        for (int i = 0; i < comm.NP; ++i)
            ranks[i] = comm.NP - 1 - i;
        
        comm.myReverseGroup = comm.myWorldGroup.Incl(comm.NP, ranks);
        
        delete [] ranks; ranks = 0;
        
        if ((comm.myReverseComm = MPI::COMM_WORLD.Create(comm.myReverseGroup)) == MPI::COMM_NULL) {
            if (comm.myWorldRank)
                return EXIT_FAILURE;
            throw MyException(__FILE__, __LINE__, "myReverseComm nije postavljen!");
        }
        
        if ((comm.myReverseRank = comm.myReverseComm.Get_rank()) != comm.NP - 1 - comm.myWorldRank) {
            if (comm.myWorldRank)
                return EXIT_FAILURE;
            throw MyException(__FILE__, __LINE__, "myReverseRank postavljen na neocekivanu vrijednost!");
        }
        
        return EXIT_SUCCESS;
    }
    
    struct {
        const char *fName;
        const char *ds_a;
        const char *ds_b;
        const char *ds_c;
        const char *ds_d;
        const char *ds_l;
        const char *ds_x;
        const char *ds_y;
        H5::H5File *inOut;
        int dim;
        double *a;
        double *b;
        double *c;
        double *y;
        Matrix<double, 2> *M;
        div_t n_p;
        int n;
    } data;
    
    int readData() {
        data.n_p = div(data.dim, comm.NP);
        // Prvih data.n_p.rem procesa dobije data.n_p.quot+1 podataka, ostali data.n_p.quot
        data.n = data.n_p.quot;
        if (comm.myWorldRank < data.n_p.rem)
            ++data.n;
        
        // Poredak vektora u memoriji mi je bitan zbog komunikacije.
        // Slat cu sve u paketu. Kasnije cu zamijeniti a s d, c s l, y s x.
        data.a = new double[4*data.n+4]();
        data.c = data.a + (data.n + 1);
        data.y = data.c + (data.n + 1);
        data.b = data.y + data.n;
        data.M = new Matrix<double, 2>[data.n+1]();
        
        data.inOut = (comm.myWorldRank ? 0 : new H5::H5File(data.fName, H5F_ACC_RDWR));
        
        H5::Exception::dontPrint();
        
        if (data.inOut) {
            const H5::DataSet dataSet_a = data.inOut->openDataSet(data.ds_a);
            if (dataSet_a.getTypeClass() != H5T_FLOAT)
                throw MyException(__FILE__, __LINE__, "a nema floating-point type-class!");
            const H5::DataSpace dataSpace_a = dataSet_a.getSpace();
            if (dataSpace_a.getSimpleExtentType() != H5S_SIMPLE)
                throw MyException(__FILE__, __LINE__, "a nema simple data-space!");
            int rank = dataSpace_a.getSimpleExtentNdims();
            if (rank != 1 && rank != 2)
                throw MyException(__FILE__, __LINE__, "a nije 1D polje ili nx1 2D polje!");
            hsize_t dims[2];
            dataSpace_a.getSimpleExtentDims(dims);
            if (rank == 2 && dims[1] != 1)
                throw MyException(__FILE__, __LINE__, "a je pravo 2D polje!");
            if (dims[0] < data.dim)
                throw MyException(__FILE__, __LINE__, "a je prekratak vektor!");
            
            const H5::DataSet dataSet_c = data.inOut->openDataSet(data.ds_c);
            if (dataSet_c.getTypeClass() != H5T_FLOAT)
                throw MyException(__FILE__, __LINE__, "c nema floating-point type-class!");
            const H5::DataSpace dataSpace_c = dataSet_c.getSpace();
            if (dataSpace_c.getSimpleExtentType() != H5S_SIMPLE)
                throw MyException(__FILE__, __LINE__, "c nema simple data-space!");
            rank = dataSpace_c.getSimpleExtentNdims();
            if (rank != 1 && rank != 2)
                throw MyException(__FILE__, __LINE__, "c nije 1D polje ili nx1 2D polje!");
            dataSpace_c.getSimpleExtentDims(dims);
            if (rank == 2 && dims[1] != 1)
                throw MyException(__FILE__, __LINE__, "c je pravo 2D polje!");
            if (dims[0] < data.dim - 1)
                throw MyException(__FILE__, __LINE__, "c je prekratak vektor!");
            
            const H5::DataSet dataSet_y = data.inOut->openDataSet(data.ds_y);
            if (dataSet_y.getTypeClass() != H5T_FLOAT)
                throw MyException(__FILE__, __LINE__, "y nema floating-point type-class!");
            const H5::DataSpace dataSpace_y = dataSet_y.getSpace();
            if (dataSpace_y.getSimpleExtentType() != H5S_SIMPLE)
                throw MyException(__FILE__, __LINE__, "y nema simple data-space!");
            rank = dataSpace_y.getSimpleExtentNdims();
            if (rank != 1 && rank != 2)
                throw MyException(__FILE__, __LINE__, "y nije 1D polje ili nx1 2D polje!");
            dataSpace_y.getSimpleExtentDims(dims);
            if (rank == 2 && dims[1] != 1)
                throw MyException(__FILE__, __LINE__, "y je pravo 2D polje!");
            if (dims[0] < data.dim)
                throw MyException(__FILE__, __LINE__, "y je prekratak vektor!");
            
            const H5::DataSet dataSet_b = data.inOut->openDataSet(data.ds_b);
            if (dataSet_b.getTypeClass() != H5T_FLOAT)
                throw MyException(__FILE__, __LINE__, "b nema floating-point type-class!");
            const H5::DataSpace dataSpace_b = dataSet_b.getSpace();
            if (dataSpace_b.getSimpleExtentType() != H5S_SIMPLE)
                throw MyException(__FILE__, __LINE__, "b nema simple data-space!");
            rank = dataSpace_b.getSimpleExtentNdims();
            if (rank != 1 && rank != 2)
                throw MyException(__FILE__, __LINE__, "b nije 1D polje ili nx1 2D polje!");
            dataSpace_b.getSimpleExtentDims(dims);
            if (rank == 2 && dims[1] != 1)
                throw MyException(__FILE__, __LINE__, "b je pravo 2D polje!");
            if (dims[0] < data.dim - 1)
                throw MyException(__FILE__, __LINE__, "b je prekratak vektor!");
            
            hsize_t start[2];
            hsize_t total;
            start[1] = 0;
            H5::DataSpace destDataSpace(H5S_SIMPLE);
            for (int i = 1; i < comm.NP; ++i) {
                // ucitaj a
                // dimenzija je quot + 2, odnosno + 1 -- saljem sad vise da kasnije ne moram komunicirati
                dims[0] = data.n_p.quot + (i < data.n_p.rem ? 2 : 1);
                start[0] = std::min(i, data.n_p.rem) * (data.n_p.quot + 1) + std::max(0, i - data.n_p.rem) * data.n_p.quot - 1;
                destDataSpace.setExtentSimple(1, dims);
                dataSpace_a.selectHyperslab(H5S_SELECT_SET, dims, start);
                if (!dataSpace_a.selectValid())
                    throw MyException(__FILE__, __LINE__, "Invalidna selekcija podvektora od a!");
                dataSet_a.read(data.a, H5::PredType::NATIVE_DOUBLE, destDataSpace, dataSpace_a);
                total = dims[0];
                //ucitaj c
                dims[0] = data.n_p.quot + (i < data.n_p.rem ? 2 : 1);
                start[0] = std::min(i, data.n_p.rem) * (data.n_p.quot + 1) + std::max(0, i - data.n_p.rem) * data.n_p.quot - 2;
                destDataSpace.setExtentSimple(1, dims);
                dataSpace_c.selectHyperslab(H5S_SELECT_SET, dims, start);
                if (!dataSpace_c.selectValid())
                    throw MyException(__FILE__, __LINE__, "Invalidna selekcija podvektora od c!");
                dataSet_c.read(data.a + total, H5::PredType::NATIVE_DOUBLE, destDataSpace, dataSpace_c);
                total += dims[0];
                // ucitaj y
                dims[0] = data.n_p.quot + (i < data.n_p.rem ? 1 : 0);
                start[0] = std::min(i, data.n_p.rem) * (data.n_p.quot + 1) + std::max(0, i - data.n_p.rem) * data.n_p.quot;
                destDataSpace.setExtentSimple(1, dims);
                dataSpace_y.selectHyperslab(H5S_SELECT_SET, dims, start);
                if (!dataSpace_y.selectValid())
                    throw MyException(__FILE__, __LINE__, "Invalidna selekcija podvektora od y!");
                dataSet_y.read(data.a + total, H5::PredType::NATIVE_DOUBLE, destDataSpace, dataSpace_y);
                total += dims[0];
                // ucitaj b
                // dimenzija je quot + (2, odnosno 1) + (1, odnosno 0)
                // objasnjenje: treba mi djelic da ne moram komunicirati i djelic za povratnu supstituciju
                dims[0] = data.n_p.quot + (i < data.n_p.rem ? 2 : 1) + (i == comm.NP-1 ? 0 : 1);
                start[0] = std::min(i, data.n_p.rem) * (data.n_p.quot + 1) + std::max(0, i - data.n_p.rem) * data.n_p.quot - 2;
                destDataSpace.setExtentSimple(1, dims);
                dataSpace_b.selectHyperslab(H5S_SELECT_SET, dims, start);
                if (!dataSpace_b.selectValid())
                    throw MyException(__FILE__, __LINE__, "Invalidna selekcija podvektora od b!");
                dataSet_b.read(data.a + total, H5::PredType::NATIVE_DOUBLE, destDataSpace, dataSpace_b);
                if (i == comm.NP-1) {
                    *(data.a + total + dims[0]) = 0.0; // Moram zadnjeg postaviti na nulu
                    ++dims[0]; // I u ovom slucaju povecavam dims jer uvijek saljem isti broj
                }
                total += dims[0];
                // Salji
                MPI::COMM_WORLD.Send(data.a, total, MPI::DOUBLE, i, 1);
            }
            // ucitaj a na root-u
            dims[0] = data.n;
            start[0] = 0;
            destDataSpace.setExtentSimple(1, dims);
            dataSpace_a.selectHyperslab(H5S_SELECT_SET, dims, start);
            if (!dataSpace_a.selectValid())
                throw MyException(__FILE__, __LINE__, "Invalidna selekcija prvog podvektora od a!");
            dataSet_a.read(data.a + 1, H5::PredType::NATIVE_DOUBLE, destDataSpace, dataSpace_a);
            data.a[0] = 1.0; // ovo je u skladu s prosirenjem kojim se rjesavam komunikacije za d[-1]
            // ucitaj c na root-u
            dims[0] = data.n - 1;
            start[0] = 0;
            destDataSpace.setExtentSimple(1, dims);
            dataSpace_c.selectHyperslab(H5S_SELECT_SET, dims, start);
            if (!dataSpace_c.selectValid())
                throw MyException(__FILE__, __LINE__, "Invalidna selekcija prvog podvektora od c!");
            dataSet_c.read(data.c + 2, H5::PredType::NATIVE_DOUBLE, destDataSpace, dataSpace_c);
            data.c[0] = data.c[1] = 0.0;
            // ucitaj y na root-u
            dims[0] = data.n;
            start[0] = 0;
            destDataSpace.setExtentSimple(1, dims);
            dataSpace_y.selectHyperslab(H5S_SELECT_SET, dims, start);
            if (!dataSpace_y.selectValid())
                throw MyException(__FILE__, __LINE__, "Invalidna selekcija prvog podvektora od y!");
            dataSet_y.read(data.y, H5::PredType::NATIVE_DOUBLE, destDataSpace, dataSpace_y);
            // ucitaj b na root-u
            dims[0] = data.n - (comm.NP == 1 ? 1 : 0);  // Treba paziti ako je root jedini proces da ne ucitamo previse
            start[0] = 0;
            destDataSpace.setExtentSimple(1, dims);
            dataSpace_b.selectHyperslab(H5S_SELECT_SET, dims, start);
            if (!dataSpace_b.selectValid())
                throw MyException(__FILE__, __LINE__, "Invalidna selekcija prvog podvektora od b!");
            dataSet_b.read(data.b + 2, H5::PredType::NATIVE_DOUBLE, destDataSpace, dataSpace_b);
            data.b[0] = data.b[1] = 0.0;
        }
        else // ne-root
            MPI::COMM_WORLD.Recv(data.a, 4 * data.n + 4, MPI::DOUBLE, 0, 1);
        
        return EXIT_SUCCESS;
    }
    
    int factLU() {
        // Prepare matrices
        // M[i] = [a[i] -b[i]*c[i] // 1.0 0.0]
        for (int i = 0; i <= data.n; ++i) {
            data.M[i][0][0] = data.a[i];
            data.M[i][0][1] = - data.b[i] * data.c[i];
            data.M[i][1][0] = 1.0;
            data.M[i][1][1] = 0.0;
        }

        // Local partial products
        std::partial_sum(data.M, data.M + data.n + 1, data.M, leftMul<double, 2>);

        // Global partial products -- exclusive parallel prefix
        MPI::Datatype MPIMatrix(MPI::DOUBLE.Create_contiguous(4));
        MPIMatrix.Commit();

        MPI::Op myOp;
        myOp.Init(MPIRightMul<double, 2>, false);

        const double Id[2][2] = {1.0, 0.0, 0.0, 1.0}; // Identity
        Matrix<double, 2> N(Id);

        MPI::COMM_WORLD.Exscan(&data.M[data.n - 1], &N, 1, MPIMatrix, myOp);

       for (int i = 0; i <= data.n; ++i) {
            if (comm.myWorldRank)
                data.M[i].rightMul(N);
            //   p[i] = data.M[i][0][0]
            //   q[i] = data.M[i][1][0]
            data.a[i] = data.M[i][0][0] / data.M[i][1][0];
            if (i)
                //data.l[i] = data.c[i] / data.d[i-1];
                data.c[i] /= data.a[i-1];
        }

        return EXIT_SUCCESS;
    }
    
    int solveLzy() {
        // Prepare matrices
        // M[i] = [-l[i] y[i] // 0.0 1.0]
        for (int i = 0; i < data.n; ++i) {
            data.M[i][0][0] = - data.c[i+1];
            data.M[i][0][1] = data.y[i];
            data.M[i][1][0] = 0.0;
            data.M[i][1][1] = 1.0;
        }
        
        // Local partial products
        std::partial_sum(data.M, data.M + data.n, data.M, leftMul<double, 2>);

        // Global partial products -- exclusive parallel prefix
        MPI::Datatype MPIMatrix(MPI::DOUBLE.Create_contiguous(4));
        MPIMatrix.Commit();
        
        MPI::Op myOp;
        myOp.Init(MPIRightMul<double, 2>, false);
        
        const double Id[2][2] = {1.0, 0.0, 0.0, 1.0}; // Identity
        Matrix<double, 2> N(Id);
        
        MPI::COMM_WORLD.Exscan(&data.M[data.n - 1], &N, 1, MPIMatrix, myOp);

        for (int i = 0; i < data.n; ++i) {
            if (comm.myWorldRank)
                data.M[i].rightMul(N);
            //data.z[i] = data.M[i][0][1];
            // dijeljenje s data.M[i][1][1] dodano naknadno
            data.y[i] = data.M[i][0][1] / data.M[i][1][1];
        }

        return EXIT_SUCCESS;
    }
    
    int solveUxz() {
        // Prepare matrices
        // M[i] = [-b[i+1]/d[i] z[i]/d[i] // 0.0 1.0]
        for (int i = 0; i < data.n; ++i) {
            data.M[i][0][0] = - data.b[data.n - i + 1] / data.a[data.n - i];
            data.M[i][0][1] = data.y[data.n - i - 1] / data.a[data.n - i];
            data.M[i][1][0] = 0.0;
            data.M[i][1][1] = 1.0;
        }
        
        // Local partial products
        std::partial_sum(data.M, data.M + data.n, data.M, leftMul<double, 2>);
        
        // Global partial products -- exclusive parallel prefix
        MPI::Datatype MPIMatrix(MPI::DOUBLE.Create_contiguous(4));
        MPIMatrix.Commit();
        
        MPI::Op myOp;
        myOp.Init(MPIRightMul<double, 2>, false);
        
        const double Id[2][2] = {1.0, 0.0, 0.0, 1.0}; // Identity
        Matrix<double, 2> N(Id);
        
        comm.myReverseComm.Exscan(&data.M[data.n - 1], &N, 1, MPIMatrix, myOp);
        
        for (int i = 0; i < data.n; ++i) {
            if (comm.myReverseRank)
                data.M[i].rightMul(N);
            // Moj x[data.n - i - 1] bi trebao biti pohranjen u data.M[i][0][1]
            // dijeljenje s data.M[i][1][1] dodano naknadno
            data.y[data.n - i - 1] = data.M[i][0][1] / data.M[i][1][1];
        }
        
        return EXIT_SUCCESS;
    }
    
    inline int compute() {
        return factLU()
                || solveLzy()
                || solveUxz();
    }
    
    int writeData() {
        delete [] data.M; data.M = 0;
        
        if (data.inOut) {
            try { data.inOut->unlink(data.ds_d); }
            catch (H5::Exception&) {}
            try { data.inOut->unlink(data.ds_l); }
            catch (H5::Exception&) {}
            try { data.inOut->unlink(data.ds_x); }
            catch (H5::Exception&) {}
            // ispis vektora d na rootu
            hsize_t size = data.dim;
            const H5::DataSpace dataSpace_d(1, &size);
            const H5::DataSet dataSet_d = data.inOut->createDataSet(data.ds_d, H5::PredType::NATIVE_DOUBLE, dataSpace_d);
            size = data.n;
            hsize_t start = 0;
            dataSpace_d.selectHyperslab(H5S_SELECT_SET, &size, &start);
            if (!dataSpace_d.selectValid())
                throw MyException(__FILE__, __LINE__, "Invalidna selekcija prvog podvektora od d!");
            H5::DataSpace srcDataSpace(1, &size);
            dataSet_d.write(data.a + 1, H5::PredType::NATIVE_DOUBLE, srcDataSpace, dataSpace_d);
            // ispis vektora l na rootu
            size = data.dim - 1;
            const H5::DataSpace dataSpace_l(1, &size);
            const H5::DataSet dataSet_l = data.inOut->createDataSet(data.ds_l, H5::PredType::NATIVE_DOUBLE, dataSpace_l);
            size = data.n - 1;
            //start = 0;
            dataSpace_l.selectHyperslab(H5S_SELECT_SET, &size, &start);
            if (!dataSpace_l.selectValid())
                throw MyException(__FILE__, __LINE__, "Invalidna selekcija prvog podvektora od l!");
            srcDataSpace.setExtentSimple(1, &size);
            dataSet_l.write(data.c + 2, H5::PredType::NATIVE_DOUBLE, srcDataSpace, dataSpace_l);
            // ispis vektora x na rootu
            size = data.dim;
            const H5::DataSpace dataSpace_x(1, &size);
            const H5::DataSet dataSet_x = data.inOut->createDataSet(data.ds_x, H5::PredType::NATIVE_DOUBLE, dataSpace_x);
            size = data.n;
            //start = 0;
            dataSpace_x.selectHyperslab(H5S_SELECT_SET, &size, &start);
            if (!dataSpace_x.selectValid())
                throw MyException(__FILE__, __LINE__, "Invalidna selekcija prvog podvektora od x!");
            srcDataSpace.setExtentSimple(1, &size);
            dataSet_x.write(data.y, H5::PredType::NATIVE_DOUBLE, srcDataSpace, dataSpace_x);
            for (int i = 1; i < comm.NP; ++i) {
                size = data.n_p.quot + (i < data.n_p.rem ? 1 : 0);
                MPI::COMM_WORLD.Recv(data.a, 3 * size + 1, MPI::DOUBLE, i, 2);
                // ispis vektora d
                start = std::min(i, data.n_p.rem) * (data.n_p.quot + 1) + std::max(0, i - data.n_p.rem) * data.n_p.quot;
                dataSpace_d.selectHyperslab(H5S_SELECT_SET, &size, &start);
                if (!dataSpace_d.selectValid())
                    throw MyException(__FILE__, __LINE__, "Invalidna selekcija podvektora od d!");
                srcDataSpace.setExtentSimple(1, &size);
                dataSet_d.write(data.a, H5::PredType::NATIVE_DOUBLE, srcDataSpace, dataSpace_d);
                // ispis vektora x
                //start = std::min(i, data.n_p.rem) * (data.n_p.quot + 1) + std::max(0, i - data.n_p.rem) * data.n_p.quot;
                dataSpace_x.selectHyperslab(H5S_SELECT_SET, &size, &start);
                if (!dataSpace_x.selectValid())
                    throw MyException(__FILE__, __LINE__, "Invalidna selekcija podvektora od x!");
                dataSet_x.write(data.a + 2 * size + 1, H5::PredType::NATIVE_DOUBLE, srcDataSpace, dataSpace_x);
                // ispis vektora l
                //start = std::min(i, data.n_p.rem) * (data.n_p.quot + 1) + std::max(0, i - data.n_p.rem) * data.n_p.quot - 1;
                --start;
                dataSpace_l.selectHyperslab(H5S_SELECT_SET, &size, &start);
                if (!dataSpace_l.selectValid())
                    throw MyException(__FILE__, __LINE__, "Invalidna selekcija podvektora od l!");
                dataSet_l.write(data.a + size + 1, H5::PredType::NATIVE_DOUBLE, srcDataSpace, dataSpace_l);
            }
        }
        else // ne-root
            MPI::COMM_WORLD.Send(data.a + 1, 3 * data.n + 1, MPI::DOUBLE, 0, 2);
        
        delete [] data.a;
        data.a = data.b = data.c = data.y = 0;
        
        if (data.inOut) {
            data.inOut->close();
            delete data.inOut;
        }
        
        return EXIT_SUCCESS;
    }
    
    int triLU(int argc, char* argv[]) {
        comm.myWorldRank = MPI::COMM_WORLD.Get_rank();
        if (argc != 9) {
tluArgsError:
            if (!comm.myWorldRank)
                std::cerr << "triLU IN.h5 N a b c y l d x" << std::endl;
            return EXIT_FAILURE;
        }
        if (!(data.fName = argv[0]))
            goto tluArgsError;
        if ((data.dim = atoi(argv[1])) <= 0)
            goto tluArgsError;
        if (!(data.ds_a = argv[2]))
            goto tluArgsError;
        if (!(data.ds_b = argv[3]))
            goto tluArgsError;
        if (!(data.ds_c = argv[4]))
            goto tluArgsError;
        if (!(data.ds_y = argv[5]))
            goto tluArgsError;
        if (!(data.ds_l = argv[6]))
            goto tluArgsError;
        if (!(data.ds_d = argv[7]))
            goto tluArgsError;
        if (!(data.ds_x = argv[8]))
            goto tluArgsError;

        return setCommunication()
                || readData()
                || compute()
                || writeData();
    }
    
}
