/*
 * Copyright 2020 Makani Technologies LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef SIM_MATH_ODE_SOLVER_GSL_H_
#define SIM_MATH_ODE_SOLVER_GSL_H_

#include <gsl/gsl_errno.h>
#include <gsl/gsl_odeiv2.h>
#include <stdint.h>

#include <vector>

#include "common/macros.h"
#include "sim/math/ode_solver.h"
#include "sim/sim_types.h"

namespace sim {

// Wrapper for the GSL ODE library.
class GslOdeSolver : public OdeSolver {
 public:
  explicit GslOdeSolver(const OdeSystem &ode_system,
                        const SimOdeSolverParams &params);
  ~GslOdeSolver() {
    if (ode_driver_ != nullptr) gsl_odeiv2_driver_free(ode_driver_);
  }

  OdeSolverStatus Integrate(double t0, double tf, const std::vector<double> &x0,
                            double *t_int, std::vector<double> *x) override;

 private:
  // Static callback function for the GSL ODE solver.
  //
  // Args:
  //   t: Time at which derivative will be evaluated.
  //   x: Array of length num_states() containing the state at which
  //       the derivative will be evaluated.
  //   dx: Array of length num_states() into which the derivative is stored.
  //   context: Pointer to the OdeSolver class containing the OdeSystem.
  //
  // Returns:
  //   GSL_SUCCESS if the derivative was calculated successfully,
  //   GSL_FAILURE if the time step was too large, or
  //   GSL_EBADFUNC if the integration should be aborted immediately.
  static int32_t GslCallback(double t, const double x[], double dx[],
                             void *context);

  // Solver parameters.
  const SimOdeSolverParams &params_;

  // ODE to be solved.
  const OdeSystem &ode_system_;

  // Parameters for GSL.
  gsl_odeiv2_system sys_;
  gsl_odeiv2_driver *ode_driver_;

  DISALLOW_COPY_AND_ASSIGN(GslOdeSolver);
};

}  // namespace sim

#endif  // SIM_MATH_ODE_SOLVER_GSL_H_


#ifndef BCT_TEST_H
#define BCT_TEST_H

#include <bct/bct.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <octave/oct.h>
#include <vector>

namespace bct_test {
	Matrix from_gsl(const gsl_vector*, int = 0);
	Matrix from_gsl(const gsl_matrix*, int = 0, int = 0);
	NDArray from_gsl(const std::vector<gsl_matrix*>, int = 0);
	gsl_vector* to_gslv(const Matrix, int = 0);
	gsl_matrix* to_gslm(const Matrix, int = 0, int = 0);
	std::vector<gsl_matrix*> to_gsl(const NDArray, int = 0);
}

#include "bct_test.cpp"

#define MATRIX_TO_SCALAR_FUNCTION(function_name) \
	DEFUN_DLD(function_name##_cpp, args, , "Wrapper for C++ function.") { \
		if (args.length() == 0) { \
			return octave_value_list(); \
		} \
		Matrix m = args(0).matrix_value(); \
		if (!error_state) { \
			gsl_matrix* m_gsl = bct_test::to_gslm(m); \
			octave_value ret = octave_value(bct::function_name(m_gsl)); \
			gsl_matrix_free(m_gsl); \
			return ret; \
		} else { \
			return octave_value_list(); \
		} \
	}

#define MATRIX_TO_VECTOR_FUNCTION(function_name) \
	DEFUN_DLD(function_name##_cpp, args, , "Wrapper for C++ function.") { \
		if (args.length() == 0) { \
			return octave_value_list(); \
		} \
		Matrix m = args(0).matrix_value(); \
		if (!error_state) { \
			gsl_matrix* m_gsl = bct_test::to_gslm(m); \
			gsl_vector* ret_gsl = bct::function_name(m_gsl); \
			octave_value ret = bct_test::from_gsl(ret_gsl); \
			gsl_matrix_free(m_gsl); \
			gsl_vector_free(ret_gsl); \
			return ret; \
		} else { \
			return octave_value_list(); \
		} \
	}

#define MATRIX_TO_MATRIX_FUNCTION(function_name) \
	DEFUN_DLD(function_name##_cpp, args, , "Wrapper for C++ function.") { \
		if (args.length() == 0) { \
			return octave_value_list(); \
		} \
		Matrix m = args(0).matrix_value(); \
		if (!error_state) { \
			gsl_matrix* m_gsl = bct_test::to_gslm(m); \
			gsl_matrix* ret_gsl = bct::function_name(m_gsl); \
			octave_value ret = bct_test::from_gsl(ret_gsl); \
			gsl_matrix_free(m_gsl); \
			gsl_matrix_free(ret_gsl); \
			return ret; \
		} else { \
			return octave_value_list(); \
		} \
	}

#endif


#ifndef BATCH_EXTRACTOR_H_IVH3CLLQ
#define BATCH_EXTRACTOR_H_IVH3CLLQ

#include <algorithm>
#include <gsl/gsl>
#include <opencv2/core/types.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <optional>
#include <sens_loc/util/correctness_util.h>

using namespace std;
using namespace cv;

namespace sens_loc::apps {

/// Helper class that visits a list of images and extracts features with the
/// provided detectors.
/// \ingroup feature-extractor-driver
class batch_extractor {
  public:
    using filter_func = function<vector<KeyPoint>::iterator(vector<KeyPoint>&)>;

    /// \param detector,descriptor Algorithms used for detection and
    /// description.
    /// \param input_pattern,output_pattern Formattable string for IO.
    /// \param keypoint_filter Callable that determines if a keypoint shall be
    /// dropped from consideration. All keypoints with 'keypoint_filter(kp) ==
    /// true' are removed.
    batch_extractor(Ptr<Feature2D>      detector,
                    Ptr<Feature2D>      descriptor,
                    string_view         input_pattern,
                    string_view         output_pattern,
                    vector<filter_func> keypoint_filter)
        : _detector{move(detector)}
        , _descriptor{move(descriptor)}
        , _input_pattern{input_pattern}
        , _ouput_pattern{output_pattern}
        , _keypoint_filter{move(keypoint_filter)} {
        Expects(!_input_pattern.empty());
        Expects(!_ouput_pattern.empty());
        Expects(!_detector.empty());
    }

    /// Process a whole batch of files in the range [start, end].
    [[nodiscard]] bool process_batch(int start, int end) const noexcept;

  private:
    /// Detect and describe one single index. Handles the IO as well.
    [[nodiscard]] bool process_index(int idx) const noexcept;

    /// Do IO and handle detection down to \c compute_features.
    bool process_detector(const math::image<uchar>& image,
                          const string&             out_file,
                          const string&             in_file) const noexcept;

    /// Compute and filter keypoints and run the descriptor on them
    /// afterwards.
    [[nodiscard]] pair<vector<KeyPoint>, Mat>
    compute_features(const math::image<uchar>& img) const noexcept;

    mutable Ptr<Feature2D> _detector;
    mutable Ptr<Feature2D> _descriptor;
    string_view            _input_pattern;
    string_view            _ouput_pattern;
    vector<filter_func>    _keypoint_filter;
};
}  // namespace sens_loc::apps

#endif /* end of include guard: BATCH_EXTRACTOR_H_IVH3CLLQ */


#ifndef UTIL_H_
#define UTIL_H_

#include <gsl/gsl_randist.h>
#include <gsl/gsl_rng.h>
#include <time.h>

/*
 * constants
 */

#define TYPE_REQ 1
#define TYPE_REQ_FOLLOW 2
#define TYPE_RES 0

#define NB_MBUF 8191
#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
#define MBUF_CACHE_SIZE 32
#define MAX_BURST_SIZE 32
#define MAX_LCORES 32
#define NB_RXD 128  // RX descriptors
#define NB_TXD 512  // TX descriptors
#define RX_QUEUE_PER_LCORE 4

#define IP_SRC "10.1.0.12"
#define IP_DST "10.1.0.1"
#define CLIENT_PORT 11234
#define SERVICE_PORT 1234

static const struct rte_eth_conf port_conf = {
    .rxmode =
        {
            .max_rx_pkt_len = ETHER_MAX_LEN,
            .split_hdr_size = 0,
            .header_split = 0,    // Header Split disabled
            .hw_ip_checksum = 0,  // IP checksum offload disabled
            .hw_vlan_filter = 0,  // VLAN filtering disabled
            .jumbo_frame = 0,     // Jumbo Frame Support disabled
            .hw_strip_crc = 0,    // CRC stripped by hardware
        },
    .txmode =
        {
            .mq_mode = ETH_MQ_TX_NONE,
        },
};

/*
 * custom types
 */

/********* Misc *********/

// Get current time in ns
uint64_t get_cur_ns() {
  struct timespec ts;
  clock_gettime(CLOCK_REALTIME, &ts);
  uint64_t t = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
  return t;
}

// For storing results
typedef struct LatencyResults_ {
  uint64_t *sjrn_times;
  uint64_t *sjrn_times_short;
  uint64_t *sjrn_times_long;
  uint64_t *work_ratios;
  uint64_t *work_ratios_short;
  uint64_t *work_ratios_long;
  uint32_t (*queue_lengths)[3];
  size_t count;
  size_t count_short;
  size_t count_long;
} LatencyResults;

/********* Distributions *********/

// Exponential Distribution
typedef struct ExpDist_ {
  gsl_rng *r;
  double mu;
  uint64_t cur_ns;
} ExpDist;

void init_exp_dist(ExpDist *exp_dist, double mu) {
  gsl_rng_env_setup();
  const gsl_rng_type *T = gsl_rng_default;
  gsl_rng *r = gsl_rng_alloc(T);
  uint64_t cur_ns = get_cur_ns();
  ExpDist temp_exp_dist = {r, mu, cur_ns};
  memcpy(exp_dist, &temp_exp_dist, sizeof(ExpDist));
}

uint64_t exp_dist_next_arrival_ns(ExpDist *exp_dist) {
  exp_dist->cur_ns += gsl_ran_exponential(exp_dist->r, exp_dist->mu);
  return exp_dist->cur_ns;
}

uint64_t exp_dist_work_ns(ExpDist *exp_dist) {
  return gsl_ran_exponential(exp_dist->r, exp_dist->mu);
}

void free_exp_dist(const ExpDist *exp_dist) {
  gsl_rng_free(exp_dist->r);
  free((void *)exp_dist);
}

// Lognormal Distribution
typedef struct LognormalDist_ {
  gsl_rng *r;
  double mu;
  double sigma;
} LognormalDist;

void init_lognormal_dist(LognormalDist *lognormal_dist, double mu,
                         double sigma) {
  gsl_rng_env_setup();
  const gsl_rng_type *T = gsl_rng_default;
  gsl_rng *r = gsl_rng_alloc(T);
  LognormalDist temp_lognormal_dist = {r, mu, sigma};
  memcpy(lognormal_dist, &temp_lognormal_dist, sizeof(LognormalDist));
}

uint64_t lognormal_dist_work_ns(LognormalDist *lognormal_dist) {
  return gsl_ran_lognormal(lognormal_dist->r, lognormal_dist->mu,
                           lognormal_dist->sigma);
}

void free_lognormal_dist(LognormalDist *lognormal_dist) {
  gsl_rng_free(lognormal_dist->r);
  free((void *)lognormal_dist);
}

// Bimodal Distribution
typedef struct BimodalDist_ {
  gsl_rng *r;
  uint64_t work_1;
  uint64_t work_2;
  double ratio;
} BimodalDist;

void init_bimodal_dist(BimodalDist *bimodal_dist, uint64_t work_1_ns,
                       uint64_t work_2_ns, double ratio) {
  gsl_rng_env_setup();
  const gsl_rng_type *T = gsl_rng_default;
  gsl_rng *r = gsl_rng_alloc(T);
  BimodalDist temp_bimodal_dist = {r, work_1_ns, work_2_ns, ratio};
  memcpy(bimodal_dist, &temp_bimodal_dist, sizeof(BimodalDist));
}

uint64_t bimodal_dist_work_ns(BimodalDist *bimodal_dist) {
  double num = gsl_ran_flat(bimodal_dist->r, 0.0, 1.0);
  if (num < bimodal_dist->ratio) {
    return bimodal_dist->work_1;
  } else {
    return bimodal_dist->work_2;
  }
}

void free_bimodal_dist(BimodalDist *bimodal_dist) {
  gsl_rng_free(bimodal_dist->r);
  free((void *)bimodal_dist);
}

// Trimodal Distribution
typedef struct TrimodalDist_ {
  gsl_rng *r;
  uint64_t work_1;
  uint64_t work_2;
  uint64_t work_3;
  double ratio1;
  double ratio2;
} TrimodalDist;

void init_trimodal_dist(TrimodalDist *trimodal_dist, uint64_t work_1_ns,
                        uint64_t work_2_ns, uint64_t work_3_ns, double ratio1,
                        double ratio2) {
  gsl_rng_env_setup();
  const gsl_rng_type *T = gsl_rng_default;
  gsl_rng *r = gsl_rng_alloc(T);
  TrimodalDist temp_trimodal_dist = {
      r, work_1_ns, work_2_ns, work_3_ns, ratio1, ratio2,
  };
  memcpy(trimodal_dist, &temp_trimodal_dist, sizeof(TrimodalDist));
}

uint64_t trimodal_dist_work_ns(TrimodalDist *trimodal_dist) {
  double num = gsl_ran_flat(trimodal_dist->r, 0.0, 1.0);
  if (num < trimodal_dist->ratio1) {
    return trimodal_dist->work_1;
  } else if (num < trimodal_dist->ratio1 + trimodal_dist->ratio2) {
    return trimodal_dist->work_2;
  } else {
    return trimodal_dist->work_3;
  }
}

void free_trimodal_dist(TrimodalDist *trimodal_dist) {
  gsl_rng_free(trimodal_dist->r);
  free((void *)trimodal_dist);
}

typedef struct Message_ {
  uint8_t recir_flag;
  uint8_t core_idx;
  uint8_t type;
  uint16_t seq_num;
  uint32_t queue_length[3];
  uint16_t client_id;
  uint32_t req_id;
  uint32_t pkts_length;
  uint64_t run_ns;
  uint64_t gen_ns;
} __attribute__((__packed__)) Message;

struct mbuf_table {
  uint32_t len;
  struct rte_mbuf *m_table[MAX_BURST_SIZE];
};

struct lcore_configuration {
  uint32_t vid;                                // virtual core id
  uint32_t port;                               // one port
  uint32_t tx_queue_id;                        // one TX queue
  uint32_t n_rx_queue;                         // number of RX queues
  uint32_t rx_queue_list[RX_QUEUE_PER_LCORE];  // list of RX queues
  struct mbuf_table tx_mbufs;                  // mbufs to hold TX queue
} __rte_cache_aligned;

/*
 * global variables
 */

uint32_t enabled_port_mask = 1;
uint32_t enabled_ports[RTE_MAX_ETHPORTS];
uint32_t n_enabled_ports = 0;
uint32_t n_rx_queues = 0;
uint32_t n_lcores = 0;

struct rte_mempool *pktmbuf_pool = NULL;
struct ether_addr port_eth_addrs[RTE_MAX_ETHPORTS];
struct lcore_configuration lcore_conf[MAX_LCORES];

uint8_t header_template[sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) +
                        sizeof(struct udp_hdr)];

/*
 * functions for generation
 */

// send packets, drain TX queue
static void send_pkt_burst(uint32_t lcore_id) {
  struct lcore_configuration *lconf = &lcore_conf[lcore_id];
  struct rte_mbuf **m_table = (struct rte_mbuf **)lconf->tx_mbufs.m_table;

  uint32_t n = lconf->tx_mbufs.len;
  uint32_t ret = rte_eth_tx_burst(lconf->port, lconf->tx_queue_id, m_table,
                                  lconf->tx_mbufs.len);
  if (unlikely(ret < n)) {
    do {
      rte_pktmbuf_free(m_table[ret]);
    } while (++ret < n);
  }
  lconf->tx_mbufs.len = 0;
}

// put packet into TX queue
static void enqueue_pkt(uint32_t lcore_id, struct rte_mbuf *mbuf) {
  struct lcore_configuration *lconf = &lcore_conf[lcore_id];
  lconf->tx_mbufs.m_table[lconf->tx_mbufs.len++] = mbuf;

  // enough packets in TX queue
  if (unlikely(lconf->tx_mbufs.len == MAX_BURST_SIZE)) {
    send_pkt_burst(lcore_id);
  }
}

/*
 * functions for initialization
 */

// init header template
static void init_header_template(void) {
  memset(header_template, 0, sizeof(header_template));
  struct ether_hdr *eth = (struct ether_hdr *)header_template;
  struct ipv4_hdr *ip =
      (struct ipv4_hdr *)((uint8_t *)eth + sizeof(struct ether_hdr));
  struct udp_hdr *udp =
      (struct udp_hdr *)((uint8_t *)ip + sizeof(struct ipv4_hdr));
  uint32_t pkt_len = sizeof(header_template) + sizeof(Message);

  // eth header
  eth->ether_type = rte_cpu_to_be_16(ETHER_TYPE_IPv4);
  struct ether_addr src_addr = {
      .addr_bytes = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}};
  struct ether_addr dst_addr = {
      .addr_bytes = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}};
  ether_addr_copy(&src_addr, &eth->s_addr);
  ether_addr_copy(&dst_addr, &eth->d_addr);

  // ip header
  char src_ip[] = IP_SRC;
  char dst_ip[] = IP_DST;
  int st1 = inet_pton(AF_INET, src_ip, &(ip->src_addr));
  int st2 = inet_pton(AF_INET, dst_ip, &(ip->dst_addr));
  if (st1 != 1 || st2 != 1) {
    fprintf(stderr, "inet_pton() failed.Error message: %s %s", strerror(st1),
            strerror(st2));
    exit(EXIT_FAILURE);
  }
  ip->total_length = rte_cpu_to_be_16(pkt_len - sizeof(struct ether_hdr));
  ip->version_ihl = 0x45;
  ip->type_of_service = 0;
  ip->packet_id = 0;
  ip->fragment_offset = 0;
  ip->time_to_live = 64;
  ip->next_proto_id = IPPROTO_UDP;
  uint32_t ip_cksum;
  uint16_t *ptr16 = (uint16_t *)ip;
  ip_cksum = 0;
  ip_cksum += ptr16[0];
  ip_cksum += ptr16[1];
  ip_cksum += ptr16[2];
  ip_cksum += ptr16[3];
  ip_cksum += ptr16[4];
  ip_cksum += ptr16[6];
  ip_cksum += ptr16[7];
  ip_cksum += ptr16[8];
  ip_cksum += ptr16[9];
  ip_cksum = ((ip_cksum & 0xffff0000) >> 16) + (ip_cksum & 0x0000ffff);
  if (ip_cksum > 65535) {
    ip_cksum -= 65535;
  }
  ip_cksum = (~ip_cksum) & 0x0000ffff;
  if (ip_cksum == 0) {
    ip_cksum = 0xffff;
  }
  ip->hdr_checksum = (uint16_t)ip_cksum;

  // udp header
  udp->src_port = htons(CLIENT_PORT);
  udp->dst_port = htons(SERVICE_PORT);
  udp->dgram_len = rte_cpu_to_be_16(pkt_len - sizeof(struct ether_hdr) -
                                    sizeof(struct ipv4_hdr));
  udp->dgram_cksum = 0;
}

// check link status
static void check_link_status(void) {
  const uint32_t check_interval_ms = 100;
  const uint32_t check_iterations = 90;
  uint32_t i, j;
  struct rte_eth_link link;
  for (i = 0; i < check_iterations; i++) {
    uint8_t all_ports_up = 1;
    for (j = 0; j < n_enabled_ports; j++) {
      uint32_t portid = enabled_ports[j];
      memset(&link, 0, sizeof(link));
      rte_eth_link_get_nowait(portid, &link);
      if (link.link_status) {
        printf("\tport %u link up - speed %u Mbps - %s\n", portid,
               link.link_speed,
               (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? "full-duplex"
                                                          : "half-duplex");
      } else {
        all_ports_up = 0;
      }
    }

    if (all_ports_up == 1) {
      printf("check link status finish: all ports are up\n");
      break;
    } else if (i == check_iterations - 1) {
      printf("check link status finish: not all ports are up\n");
    } else {
      rte_delay_ms(check_interval_ms);
    }
  }
}

// initialize all status
static void init(void) {
  uint32_t i, j;

  // create mbuf pool
  printf("create mbuf pool\n");
  pktmbuf_pool = rte_mempool_create(
      "mbuf_pool", NB_MBUF, MBUF_SIZE, MBUF_CACHE_SIZE,
      sizeof(struct rte_pktmbuf_pool_private), rte_pktmbuf_pool_init, NULL,
      rte_pktmbuf_init, NULL, rte_socket_id(), 0);
  if (pktmbuf_pool == NULL) {
    rte_exit(EXIT_FAILURE, "cannot init mbuf pool\n");
  }

  // determine available ports
  printf("create enabled ports\n");
  uint32_t n_total_ports = 0;
  n_total_ports = rte_eth_dev_count();

  if (n_total_ports == 0) {
    rte_exit(EXIT_FAILURE, "cannot detect ethernet ports\n");
  }
  if (n_total_ports > RTE_MAX_ETHPORTS) {
    n_total_ports = RTE_MAX_ETHPORTS;
  }

  // get info for each enabled port
  struct rte_eth_dev_info dev_info;
  n_enabled_ports = 0;
  printf("\tports: ");
  for (i = 0; i < n_total_ports; i++) {
    if ((enabled_port_mask & (1 << i)) == 0) {
      continue;
    }
    enabled_ports[n_enabled_ports++] = i;
    rte_eth_dev_info_get(i, &dev_info);
    printf("%u ", i);
  }
  printf("\n");

  // find number of active lcores
  printf("create enabled cores\n\tcores: ");
  n_lcores = 0;
  for (i = 0; i < MAX_LCORES; i++) {
    if (rte_lcore_is_enabled(i)) {
      n_lcores++;
      printf("%u ", i);
    }
  }

  // ensure numbers are correct
  if (n_lcores % n_enabled_ports != 0) {
    rte_exit(EXIT_FAILURE,
             "number of cores (%u) must be multiple of ports (%u)\n", n_lcores,
             n_enabled_ports);
  }

  uint32_t rx_queues_per_lcore = RX_QUEUE_PER_LCORE;
  uint32_t rx_queues_per_port =
      rx_queues_per_lcore * n_lcores / n_enabled_ports;
  uint32_t tx_queues_per_port = n_lcores / n_enabled_ports;

  if (rx_queues_per_port < rx_queues_per_lcore) {
    rte_exit(EXIT_FAILURE,
             "rx_queues_per_port (%u) must be >= rx_queues_per_lcore (%u)\n",
             rx_queues_per_port, rx_queues_per_lcore);
  }

  // assign each lcore some RX queues and a port
  printf("set up %d RX queues per port and %d TX queues per port\n",
         rx_queues_per_port, tx_queues_per_port);
  uint32_t portid_offset = 0;
  uint32_t rx_queue_id = 0;
  uint32_t tx_queue_id = 0;
  uint32_t vid = 0;
  for (i = 0; i < MAX_LCORES; i++) {
    if (rte_lcore_is_enabled(i)) {
      lcore_conf[i].vid = vid++;
      lcore_conf[i].n_rx_queue = rx_queues_per_lcore;
      for (j = 0; j < rx_queues_per_lcore; j++) {
        lcore_conf[i].rx_queue_list[j] = rx_queue_id++;
      }
      lcore_conf[i].port = enabled_ports[portid_offset];
      lcore_conf[i].tx_queue_id = tx_queue_id++;
      if (rx_queue_id % rx_queues_per_port == 0) {
        portid_offset++;
        rx_queue_id = 0;
        tx_queue_id = 0;
      }
    }
  }

  // initialize each port
  for (portid_offset = 0; portid_offset < n_enabled_ports; portid_offset++) {
    uint32_t portid = enabled_ports[portid_offset];

    int32_t ret = rte_eth_dev_configure(portid, rx_queues_per_port,
                                        tx_queues_per_port, &port_conf);
    if (ret < 0) {
      rte_exit(EXIT_FAILURE, "cannot configure device: err=%d, port=%u\n", ret,
               portid);
    }
    rte_eth_macaddr_get(portid, &port_eth_addrs[portid]);

    // initialize RX queues
    for (i = 0; i < rx_queues_per_port; i++) {
      ret = rte_eth_rx_queue_setup(
          portid, i, NB_RXD, rte_eth_dev_socket_id(portid), NULL, pktmbuf_pool);
      if (ret < 0) {
        rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup: err=%d, port=%u\n", ret,
                 portid);
      }
    }

    // initialize TX queues
    for (i = 0; i < tx_queues_per_port; i++) {
      ret = rte_eth_tx_queue_setup(portid, i, NB_TXD,
                                   rte_eth_dev_socket_id(portid), NULL);
      if (ret < 0) {
        rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup: err=%d, port=%u\n", ret,
                 portid);
      }
    }

    // start device
    ret = rte_eth_dev_start(portid);
    if (ret < 0) {
      rte_exit(EXIT_FAILURE, "rte_eth_dev_start: err=%d, port=%u\n", ret,
               portid);
    }

    rte_eth_promiscuous_enable(portid);

    char mac_buf[ETHER_ADDR_FMT_SIZE];
    ether_format_addr(mac_buf, ETHER_ADDR_FMT_SIZE, &port_eth_addrs[portid]);
    printf("initiaze queues and start port %u, MAC address:%s\n", portid,
           mac_buf);
  }

  if (!n_enabled_ports) {
    rte_exit(EXIT_FAILURE,
             "all available ports are disabled. Please set portmask.\n");
  }
  check_link_status();
  init_header_template();
}

/*
 * misc
 */

static uint64_t timediff_in_us(uint64_t new_t, uint64_t old_t) {
  return (new_t - old_t) * 1000000UL / rte_get_tsc_hz();
}

#endif  // UTIL_H_


//====---- Sudoku/Board_Section.h                                     ----====//
//
// Internal in-between object for provide access to a section of Board.
//====--------------------------------------------------------------------====//
#pragma once

#include "Board_Section_iterator.h"
#include "Board_Section_traits.h" // Convenience include
#include "Location.h"
#include "Location_Utilities.h"
#include "exceptions.h"
#include "traits.h"

#include <gsl/gsl> // index

#include <type_traits>

#include "Board.fwd.h" // Forward declarations

#include <cassert>

namespace Sudoku::Board_Section
{
template<typename T, int N, Section S, bool is_const>
class [[nodiscard]] Board_Section_
{
	using index          = gsl::index;
	using Location       = ::Sudoku::Location<N>;
	using Location_Block = ::Sudoku::Location_Block<N>;
	using OwnerT =
		std::conditional_t<is_const, Board<T, N> const&, Board<T, N>&>;

	// explicit friends for conversion
	friend class Board_Section_<T, N, Section::row, true>;
	friend class Board_Section_<T, N, Section::row, false>;
	friend class Board_Section_<T, N, Section::col, true>;
	friend class Board_Section_<T, N, Section::col, false>;
	friend class Board_Section_<T, N, Section::block, true>;
	friend class Board_Section_<T, N, Section::block, false>;

public:
	using value_type             = T;
	using pointer                = std::conditional_t<is_const, T const*, T*>;
	using reference              = std::conditional_t<is_const, T const&, T&>;
	using iterator               = Section_iterator<T, N, S, is_const, false>;
	using const_iterator         = Section_iterator<T, N, S, true, false>;
	using reverse_iterator       = Section_iterator<T, N, S, is_const, true>;
	using const_reverse_iterator = Section_iterator<T, N, S, true, true>;

	template<typename idT, typename = std::enable_if_t<is_int_v<idT>>>
	constexpr Board_Section_(OwnerT board, idT id) noexcept
		: board_(board), id_(id)
	{
		assert(is_valid_size<N>(id));
	}
	constexpr Board_Section_(OwnerT board, Location loc) noexcept
		: board_(board), id_(to_id(loc))
	{
		assert(is_valid<N>(loc));
	}
	// Conversion to other Section type (maintaining or gaining const)
	template<
		Section Sx,
		bool B,
		typename = std::enable_if_t<!B || B == is_const>>
	constexpr Board_Section_(
		Board_Section_<T, N, Sx, B> other, index pivot_elem) noexcept
		: board_(other.board_), id_(to_id(other.location(pivot_elem)))
	{
	}
	// [[implicit]] conversion to const
	// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
	constexpr operator Board_Section_<T, N, S, true>() const noexcept
	{
		return Board_Section_<T, N, S, true>(board_, id_);
	}

	[[nodiscard]] static constexpr int size() noexcept { return elem_size<N>; }
	[[nodiscard]] constexpr index id() const noexcept { return id_; }
	[[nodiscard]] constexpr Location location(index elem) const noexcept;

	[[nodiscard]] constexpr reference front() noexcept { return (*this)[0]; }
	[[nodiscard]] constexpr reference back() noexcept
	{
		return (*this)[static_cast<index>(size()) - 1];
	}
	[[nodiscard]] constexpr reference operator[](const index elem) noexcept
	{
		return board_[location(elem)];
	}
	[[nodiscard]] constexpr T const& operator[](const index elem) const noexcept
	{
		return board_[location(elem)];
	}
	// Checked access
	[[nodiscard]] constexpr reference at(const index elem);

	constexpr iterator begin() noexcept { return iterator(&board_, id_, 0); }
	constexpr iterator end() noexcept { return iterator(&board_, id_, size()); }
	[[nodiscard]] constexpr const_iterator cbegin() const noexcept
	{
		return const_iterator(&board_, id_, 0);
	}
	[[nodiscard]] constexpr const_iterator cend() const noexcept
	{
		return const_iterator(&board_, id_, size());
	}
	[[nodiscard]] constexpr const_iterator begin() const noexcept
	{
		return cbegin();
	}
	[[nodiscard]] constexpr const_iterator end() const noexcept
	{
		return cend();
	}
	constexpr reverse_iterator rbegin() noexcept
	{
		return reverse_iterator(&board_, id_, size() - 1);
	}
	constexpr reverse_iterator rend() noexcept
	{
		return reverse_iterator(&board_, id_, -1);
	}
	[[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept
	{
		return const_reverse_iterator(&board_, id_, size() - 1);
	}
	[[nodiscard]] constexpr const_reverse_iterator crend() const noexcept
	{
		return const_reverse_iterator(&board_, id_, -1);
	}
	[[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept
	{
		return crbegin();
	}
	[[nodiscard]] constexpr const_reverse_iterator rend() const noexcept
	{
		return crend();
	}

private:
	OwnerT board_;
	const gsl::index id_{};

	// Internal helper
	static constexpr gsl::index to_id(Location loc) noexcept;
};

//====--------------------------------------------------------------------====//
// Member Functions

// Checked access
template<typename T, int N, Section S, bool is_const>
[[nodiscard]] inline constexpr
	typename Board_Section_<T, N, S, is_const>::reference
	Board_Section_<T, N, S, is_const>::at(const index elem)
{
	if (!is_valid_size<N>(elem))
	{
		throw error::invalid_Location{"Board_Section::at(elem)"};
	}
	return board_.at(location(elem));
}

template<typename T, int N, Section S, bool is_const>
[[nodiscard]] constexpr Location<N>
	Board_Section_<T, N, S, is_const>::location(index elem) const noexcept
{
	assert(is_valid_size<N>(elem));
	switch (S)
	{
	case Section::row: return Location(id_, elem);
	case Section::col: return Location(elem, id_);
	case Section::block: return Location_Block(id_, elem);
	}
}

// Internal helper
template<typename T, int N, Section S, bool is_const>
inline constexpr gsl::index
	Board_Section_<T, N, S, is_const>::to_id(Location loc) noexcept
{
	switch (S)
	{
	case Section::row: return loc.row();
	case Section::col: return loc.col();
	case Section::block: return loc.block();
	}
}

} // namespace Sudoku::Board_Section


/***************************************************************************/
/*                                                                         */
/* matrix.h - Matrix class for mruby                                       */
/* Copyright (C) 2015 Paolo Bosetti                                        */
/* paolo[dot]bosetti[at]unitn.it                                           */
/* Department of Industrial Engineering, University of Trento              */
/*                                                                         */
/* This library is free software.  You can redistribute it and/or          */
/* modify it under the terms of the GNU GENERAL PUBLIC LICENSE 2.0.        */
/*                                                                         */
/* This library 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           */
/* Artistic License 2.0 for more details.                                  */
/*                                                                         */
/* See the file LICENSE                                                    */
/*                                                                         */
/***************************************************************************/

#ifndef MATRIX_H
#define MATRIX_H

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <time.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>

#include "mruby.h"
#include "mruby/variable.h"
#include "mruby/string.h"
#include "mruby/data.h"
#include "mruby/class.h"
#include "mruby/value.h"
#include "mruby/array.h"
#include "mruby/numeric.h"
#include "mruby/compile.h"

#define E_MATRIX_ERROR (mrb_class_get(mrb, "MatrixError"))

/***********************************************\
 MATRICES
\***********************************************/

// Garbage collector handler, for play_data struct
// if play_data contains other dynamic data, free it too!
// Check it with GC.start
void matrix_destructor(mrb_state *mrb, void *p_);

// Utility function for getting the struct out of the wrapping IV @data
void mrb_matrix_get_data(mrb_state *mrb, mrb_value self, gsl_matrix **data);

void mrb_gsl_matrix_init(mrb_state *mrb);

#endif // MATRIX_H


#ifndef bcfunctions_h
#define bcfunctions_h

#include <petsc.h>

PetscErrorCode BCsDiff(PetscInt dim, PetscReal time, const PetscReal x[],
                       PetscInt num_comp_u, PetscScalar *u, void *ctx);
PetscErrorCode BCsMass(PetscInt dim, PetscReal time, const PetscReal x[],
                       PetscInt num_comp_u, PetscScalar *u, void *ctx);

#endif // bcfunctions_h


/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
**                                                                                **
** This file forms part of the Underworld geophysics modelling application.         **
**                                                                                **
** For full license and copyright information, please refer to the LICENSE.md file  **
** located at the project root, or contact the authors.                             **
**                                                                                **
**~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*/
#include <petsc.h>
#include <petscmat.h>

#include "common-driver-utils.h"

#include <StGermain/libStGermain/src/StGermain.h>
#include <StgDomain/libStgDomain/src/StgDomain.h>
#include <StgFEM/libStgFEM/src/StgFEM.h>
#include <PICellerator/libPICellerator/src/PICellerator.h>
#include <Underworld/libUnderworld/src/Underworld.h>
#include "Solvers/SLE/src/SLE.h" /* to give the AugLagStokes_SLE type */
#include "Solvers/KSPSolvers/src/KSPSolvers.h" /* for __KSP_COMMON */
#include "BSSCR.h"
#include "writeMatVec.h"

#if( (PETSC_VERSION_MAJOR==2) && (PETSC_VERSION_MINOR==3) && (PETSC_VERSION_SUBMINOR==0) )
#define FILE_OPTION PETSC_FILE_RDONLY
#endif
#if( (PETSC_VERSION_MAJOR==2) && (PETSC_VERSION_MINOR==3) && (PETSC_VERSION_SUBMINOR>=2) )
#define FILE_OPTION FILE_MODE_READ
#endif
#if( PETSC_VERSION_MAJOR==3 )
#define FILE_OPTION FILE_MODE_READ
#endif

PetscErrorCode BSSCR_FormSchurApproximation1( Mat A11, Mat A12, Mat A21, Mat A22, Mat *_Shat, PetscTruth sym );
PetscErrorCode BSSCR_FormSchurApproximationDiag( Mat A11, Mat A12, Mat A21, Mat A22, Mat *_Shat, PetscTruth sym );

PetscErrorCode BSSCR_BSSCR_StokesReadPCSchurMat_binary( MPI_Comm comm, Mat *S )
{
	PetscViewer mat_view_file;
	char	op_name[PETSC_MAX_PATH_LEN];
	PetscTruth flg;

	PetscOptionsGetString( PETSC_NULL,"-stokes_Smat",op_name,PETSC_MAX_PATH_LEN-1,&flg );
	*S = PETSC_NULL;
	if (flg) {
		if (!S)	Stg_SETERRQ(1,"Memory space for Smat is NULL");

		PetscViewerBinaryOpen( comm, op_name,  FILE_OPTION, &mat_view_file );
		Stg_MatLoad( mat_view_file, MATAIJ, S );
		Stg_PetscViewerDestroy(&mat_view_file );
	}

	PetscFunctionReturn(0);
}

PetscErrorCode BSSCR_BSSCR_StokesReadPCSchurMat_ascii( MPI_Comm comm, Mat *S )
{
	char	op_name[PETSC_MAX_PATH_LEN];
	PetscTruth flg;

	PetscOptionsGetString( PETSC_NULL,"-stokes_Smat",op_name,PETSC_MAX_PATH_LEN-1,&flg );
	*S = PETSC_NULL;
	if (flg) {
		if (!S)	Stg_SETERRQ(1,"Memory space for Smat is NULL");
		Stg_SETERRQ(1,"Currently Disabled");
		//MatAIJLoad_MatrixMarket( comm, op_name, S );
	}

	PetscFunctionReturn(0);
}

PetscErrorCode BSSCR_StokesReadPCSchurMat( MPI_Comm comm, Mat *S )
{
	char	op_name[PETSC_MAX_PATH_LEN];
	PetscTruth flg;

	PetscOptionsGetString( PETSC_NULL,"-stokes_ascii",op_name,PETSC_MAX_PATH_LEN-1,&flg );
	if (flg==PETSC_TRUE) {
		BSSCR_BSSCR_StokesReadPCSchurMat_ascii( comm, S );
	}
	else {
		BSSCR_BSSCR_StokesReadPCSchurMat_binary( comm, S );
	}

	PetscFunctionReturn(0);
}

PetscErrorCode BSSCR_StokesCreatePCSchur( Mat K, Mat G, PC pc_S )
{
	char pc_type[PETSC_MAX_PATH_LEN];
	PetscTruth flg;

	PetscOptionsGetString( PETSC_NULL, "-Q22_pc_type", pc_type, PETSC_MAX_PATH_LEN-1, &flg );
	if( !flg ) {
		Stg_SETERRQ( PETSC_ERR_SUP, "OPTION: -Q22_pc_type must be set" );
	}

	/* 1. define S pc to be "none" */

	if( strcmp(pc_type,"none")==0 ) { /* none */
		PetscPrintf(PETSC_COMM_WORLD,"  Setting schur_pc to \"none\" \n" );
		PCSetType( pc_S, "none" );
	}
	else if( strcmp(pc_type,"uw")==0 ) { /* diag */
		Mat S, Amat;
		MatStructure mstruct;
		MPI_Comm comm;

		PetscPrintf(PETSC_COMM_WORLD,"  Setting schur_pc to \"uw\" \n" );

		PetscObjectGetComm( (PetscObject)pc_S, &comm );
		S = PETSC_NULL;
		BSSCR_StokesReadPCSchurMat( comm, &S );
		if (!S) {	Stg_SETERRQ(1,"Must indicate location of file for SchurPC matrix with the stokes_Smat option");	}

		Stg_PCGetOperators( pc_S, &Amat, &Amat, &mstruct );
		Stg_PCSetOperators( pc_S, Amat, S, SAME_NONZERO_PATTERN );

		MatView( S, PETSC_VIEWER_STDOUT_WORLD );
	}
	else { /* not valid option */
		Stg_SETERRQ( PETSC_ERR_SUP, "OPTION: -seg_schur_pc_type is not valid" );
	}

	PetscFunctionReturn(0);
}

PetscErrorCode BSSCR_BSSCR_StokesCreatePCSchur2(
   Mat K, Mat G, Mat D, Mat C, Mat Smat, PC pc_S,
   PetscTruth sym, KSP_BSSCR * bsscrp )
{
	char pc_type[PETSC_MAX_PATH_LEN];
	PetscTruth flg;

	PetscOptionsGetString( PETSC_NULL, "-Q22_pc_type", pc_type, PETSC_MAX_PATH_LEN-1, &flg );
	if( !flg ) {
	    strcpy(pc_type, "uw");
	    //Stg_SETERRQ( PETSC_ERR_SUP, "OPTION: -Q22_pc_type must be set" );
	}

	/* 1. define S pc to be "none" */

	if( strcmp(pc_type,"none")==0 ) { /* none */
		/* Mat Amat,Pmat; */
		/* MatStructure mstruct; */

		PetscPrintf(PETSC_COMM_WORLD,"  Setting schur_pc to \"none\" \n" );
		/* PCGetOperators( pc_S, &Amat, &Pmat, &mstruct ); */
		/* PCSetOperators( pc_S, Amat, Amat, SAME_NONZERO_PATTERN ); */
		PCSetType( pc_S, "none" );
	}
	else if( strcmp(pc_type,"uw")==0 ) { /* diag */
	    Mat Amat,Pmat;
		MatStructure mstruct;

		PetscPrintf(PETSC_COMM_WORLD,"  Setting schur_pc to \"uw\" \n" );

		if (!Smat) {	Stg_SETERRQ(1,"Smat cannot be NULL if -Q22_pc_type = uw");	}

		Stg_PCGetOperators( pc_S, &Amat, &Pmat, &mstruct );
		Stg_PCSetOperators( pc_S, Amat, Smat, SAME_NONZERO_PATTERN );
	}
	else if( strcmp(pc_type,"uwscale")==0 ) { /* diag */
	    Mat Amat, Shat, Pmat;
		MatStructure mstruct;

		PetscPrintf(PETSC_COMM_WORLD,"  Setting schur_pc to \"uwscale\" \n" );

		if (!Smat) {	Stg_SETERRQ(1,"Smat cannot be NULL if -Q22_pc_type = uwscale");	}

		BSSCR_FormSchurApproximation1( K, G, D, C, &Shat, sym );

		Stg_PCGetOperators( pc_S, &Amat, &Pmat, &mstruct );
		Stg_PCSetOperators( pc_S, Amat, Shat, SAME_NONZERO_PATTERN );

		Stg_MatDestroy(&Shat);
	}
	else if( strcmp(pc_type,"gkgdiag")==0 ) { /* diag */
	    Mat Amat, Shat, Pmat;
		MatStructure mstruct;

		PetscPrintf(PETSC_COMM_WORLD,"  Setting schur_pc to \"gkgdiag\" \n" );

		if (!Smat) {	Stg_SETERRQ(1,"Smat cannot be NULL if -Q22_pc_type = uwscale");	}

		BSSCR_FormSchurApproximationDiag( K, G, D, C, &Shat, sym );

		Stg_PCGetOperators( pc_S, &Amat, &Pmat, &mstruct );
		Stg_PCSetOperators( pc_S, Amat, Shat, SAME_NONZERO_PATTERN );

		Stg_MatDestroy(&Shat);
	}
	else if( strcmp(pc_type,"gtkg")==0 ) { /* GtKG */
	    PetscPrintf(PETSC_COMM_WORLD,"  Setting schur_pc to \"gtkg\" \n" );

	    /* Build the schur pc GtKG */
	    PCSetType( pc_S, "gtkg" );
	    //AugLagStokes_SLE * stokesSLE = (AugLagStokes_SLE*)bsscrp->st_sle;
        StokesBlockKSPInterface* Solver = bsscrp->solver;
	    Mat M=0;
	    if (Solver->vmStiffMat){
		M = Solver->vmStiffMat->matrix;
	    }
	    BSSCR_PCGtKGSet_Operators( pc_S, K, G, M );
	    //BSSCR_PCGtKGAttachNullSpace( pc_S );
	}
	else { /* not valid option */
		Stg_SETERRQ( PETSC_ERR_SUP, "OPTION: -Q22_pc_type is not valid" );
	}

	PetscFunctionReturn(0);
}

/*

Recomended usage:

Stg_KSPSetOperators( ksp_S, S, Shat, SAME_NONZERO_PATTERN );

KSPGetPC( ksp_S, &pc_S );
PCSetType( pc_S, "jacobi" );

NOTE: In pracise "jacobi" works better than keep the entire matrix and
factoring it using something like "cholesky"

*/
PetscErrorCode BSSCR_FormSchurApproximation1( Mat A11, Mat A12, Mat A21, Mat A22, Mat *_Shat, PetscTruth sym )
{
   Mat Shat, A21_cpy;
   Vec diag;

   MatGetVecs( A11, &diag, PETSC_NULL );
   MatGetDiagonal( A11, diag );
   VecReciprocal( diag );

/*    if( sym ) { */
/* #if( PETSC_VERSION_MAJOR <= 2 ) */
/*       MatTranspose( A12, &A21_cpy ); */
/* #else */
/*       MatTranspose( A12, MAT_INITIAL_MATRIX, &A21_cpy ); */
/* #endif */
/*       MatDiagonalScale(A21_cpy, PETSC_NULL, diag ); */
/*    } */
/*    else { */
      MatDuplicate( A21, MAT_COPY_VALUES, &A21_cpy );
      MatDiagonalScale(A21_cpy, PETSC_NULL, diag );
   /* } */

   MatMatMult( A21_cpy, A12, MAT_INITIAL_MATRIX, 1.2, &Shat );  /* A21 diag(K)^{-1} A12 */

   if( A22 != PETSC_NULL )
      MatAXPY( Shat, -1.0, A22, DIFFERENT_NONZERO_PATTERN ); /* S <- -C + A21 diag(K)^{-1} A12 */

   *(void**)_Shat = (void*)Shat;

   Stg_MatDestroy(&A21_cpy);
   Stg_VecDestroy(&diag);

   PetscFunctionReturn(0);
}

PetscErrorCode BSSCR_FormSchurApproximationDiag( Mat A11, Mat A12, Mat A21, Mat A22, Mat *_Shat, PetscTruth sym )
{
   Mat Shat, A21_cpy;
   Vec diag;

   MatGetVecs( A11, &diag, PETSC_NULL );
   MatGetDiagonal( A11, diag );
   VecReciprocal( diag );

/*    if( sym ) { */
/* #if( PETSC_VERSION_MAJOR <= 2 ) */
/*       MatTranspose( A12, &A21_cpy ); */
/* #else */
/*       MatTranspose( A12, MAT_INITIAL_MATRIX, &A21_cpy ); */
/* #endif */
/*       MatDiagonalScale(A21_cpy, PETSC_NULL, diag ); */
/*    } */
/*    else { */
      MatDuplicate( A21, MAT_COPY_VALUES, &A21_cpy );
      MatDiagonalScale(A21_cpy, PETSC_NULL, diag );
   /* } */

   MatMatMult( A21_cpy, A12, MAT_INITIAL_MATRIX, 1.2, &Shat );  /* A21 diag(K)^{-1} A12 */

   if( A22 != PETSC_NULL )
      MatAXPY( Shat, -1.0, A22, DIFFERENT_NONZERO_PATTERN ); /* S <- -C + A21 diag(K)^{-1} A12 */

   Stg_MatDestroy(&A21_cpy);
   Stg_VecDestroy(&diag);

   MatGetVecs( Shat, &diag, PETSC_NULL );
   MatGetDiagonal( Shat, diag );

   MatZeroEntries( Shat );
   MatDiagonalSet( Shat, diag, INSERT_VALUES );

   *(void**)_Shat = (void*)Shat;

   Stg_VecDestroy(&diag);

   PetscFunctionReturn(0);
}


#ifndef PyGSL_COMPLEX_HELPERS_H
#define PyGSL_COMPLEX_HELPERS_H 1
#include <pygsl/utils.h>
#include <pygsl/intern.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_errno.h>

/*
 *  These macros convert a PyObect to  a complex if numeric
 *  Input recieved. If complex, accessing the data, otherwise
 *  reverting to a function.
 */
/* -------------------------------------------------------------------------
   Helper Functions
   ------------------------------------------------------------------------- */
PyGSL_API_EXTERN int
PyGSL_PyComplex_to_gsl_complex(PyObject * src, gsl_complex * mycomplex);

PyGSL_API_EXTERN int
PyGSL_PyComplex_to_gsl_complex_float(PyObject * src,
				     gsl_complex_float * mycomplex);
PyGSL_API_EXTERN int
PyGSL_PyComplex_to_gsl_complex_long_double(PyObject * src,
					   gsl_complex_long_double * mycomplex);

#ifndef _PyGSL_API_MODULE
#define PyGSL_PyComplex_to_gsl_complex \
(*(int (*) (PyObject *, gsl_complex *))              PyGSL_API[PyGSL_PyComplex_to_gsl_complex_NUM])
#define PyGSL_PyComplex_to_gsl_complex_float \
(*(int (*) (PyObject *, gsl_complex_float *))        PyGSL_API[PyGSL_PyComplex_to_gsl_complex_float_NUM])
#define PyGSL_PyComplex_to_gsl_complex_long_double \
(*(int (*) (PyObject *, gsl_complex_long_double *))  PyGSL_API[PyGSL_PyComplex_to_gsl_complex_long_double_NUM])
#endif /* _PyGSL_API_MODULE */

#define PyGSL_PyCOMPLEX_TO_gsl_complex(object, tmp)            \
(PyComplex_Check((object))) ? 				       \
     ((tmp)->dat[0] = ((PyComplexObject *)(object))->cval.real,\
      (tmp)->dat[1] = ((PyComplexObject *)(object))->cval.imag,\
      GSL_SUCCESS) 					       \
     : PyGSL_PyComplex_to_gsl_complex(object, tmp)

#define PyGSL_PyCOMPLEX_TO_gsl_complex_float(object, tmp )     \
(PyComplex_Check((object))) ?                                  \
     ((tmp)->dat[0] = ((PyComplexObject *)(object))->cval.real,\
      (tmp)->dat[1] = ((PyComplexObject *)(object))->cval.imag,\
      GSL_SUCCESS)                                             \
     : PyGSL_PyComplex_to_gsl_complex_float(object, tmp)

#define PyGSL_PyCOMPLEX_TO_gsl_complex_long_double(object, tmp ) \
(PyComplex_Check((object))) ?                                     \
     ((tmp)->dat[0] = ((PyComplexObject *)(object))->cval.real,  \
      (tmp)->dat[1] = ((PyComplexObject *)(object))->cval.imag,  \
      GSL_SUCCESS)                                               \
     : PyGSL_PyComplex_to_gsl_complex_long_double(object, tmp)

#endif  /* PyGSL_COMPLEX_HELPERS_H */


// @file utils.h
//
//  \date Created on: Sep 23, 2017
//  \author Gopalakrishna Hegde
//
//   Description:
//
//
//

#ifndef INC_UTILS_H_
#define INC_UTILS_H_
#include "common_types.h"
#include <cblas.h>

void PrintMat(char *name, const float *ptr, int H, int W, CBLAS_LAYOUT layout);
void RandInitF32(float *p_data, int N);
void SeqInitF32(float *p_data, int N);
void PrintTensor(const float *data, TensorDim dim);
int TensorSize(TensorDim dim);
bool TensorCompare(const float *t1, const float *t2, TensorDim dim);
#endif  // INC_UTILS_H_


/* vector/gsl_vector_double.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_VECTOR_DOUBLE_H__
#define __GSL_VECTOR_DOUBLE_H__

#include <stdlib.h>
#include <gsl/gsl_errno.h>
#include <gsl/block/gsl_check_range.h>
#include <gsl/block/gsl_block_double.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size;		//the number of elements in this vector
  size_t stride;	//in the block pointer, this indicates the spacing between elements
  double *data;		//a pointer to the data in this vector
  gsl_block *block;	//a pointer to the block that the data belongs to
  int owner;		//if this is 1 it means that this vector is the owner of this block, otherwise it means it inherited it from somewhere else and it shouldn't be deallocated with the vector
}
gsl_vector;

typedef struct
{
  gsl_vector vector;
} _gsl_vector_view;

typedef _gsl_vector_view gsl_vector_view;

typedef struct
{
  gsl_vector vector;
} _gsl_vector_const_view;

typedef const _gsl_vector_const_view gsl_vector_const_view;

/* Allocation */

GSL_FUNC gsl_vector *gsl_vector_alloc (const size_t n);
GSL_FUNC gsl_vector *gsl_vector_calloc (const size_t n);

GSL_FUNC gsl_vector *gsl_vector_alloc_from_block (gsl_block * b,
                                                     const size_t offset,
                                                     const size_t n,
                                                     const size_t stride);

GSL_FUNC gsl_vector *gsl_vector_alloc_from_vector (gsl_vector * v,
                                                      const size_t offset,
                                                      const size_t n,
                                                      const size_t stride);

GSL_FUNC void gsl_vector_free (gsl_vector * v);

/* Views */

GSL_FUNC _gsl_vector_view
gsl_vector_view_array (double *v, size_t n);

GSL_FUNC _gsl_vector_view
gsl_vector_view_array_with_stride (double *base,
                                         size_t stride,
                                         size_t n);

GSL_FUNC _gsl_vector_const_view
gsl_vector_const_view_array (const double *v, size_t n);

GSL_FUNC _gsl_vector_const_view
gsl_vector_const_view_array_with_stride (const double *base,
                                               size_t stride,
                                               size_t n);

GSL_FUNC _gsl_vector_view
gsl_vector_subvector (gsl_vector *v,
                            size_t i,
                            size_t n);

GSL_FUNC _gsl_vector_view
gsl_vector_subvector_with_stride (gsl_vector *v,
                                        size_t i,
                                        size_t stride,
                                        size_t n);

GSL_FUNC _gsl_vector_const_view
gsl_vector_const_subvector (const gsl_vector *v,
                                  size_t i,
                                  size_t n);

GSL_FUNC _gsl_vector_const_view
gsl_vector_const_subvector_with_stride (const gsl_vector *v,
                                              size_t i,
                                              size_t stride,
                                              size_t n);

/* Operations */

GSL_FUNC double gsl_vector_get (const gsl_vector * v, const size_t i);
GSL_FUNC void gsl_vector_set (gsl_vector * v, const size_t i, double x);

GSL_FUNC double *gsl_vector_ptr (gsl_vector * v, const size_t i);
GSL_FUNC const double *gsl_vector_const_ptr (const gsl_vector * v, const size_t i);

GSL_FUNC void gsl_vector_set_zero (gsl_vector * v);
GSL_FUNC void gsl_vector_set_all (gsl_vector * v, double x);
GSL_FUNC int gsl_vector_set_basis (gsl_vector * v, size_t i);

GSL_FUNC int gsl_vector_fread (FILE * stream, gsl_vector * v);
GSL_FUNC int gsl_vector_fwrite (FILE * stream, const gsl_vector * v);
GSL_FUNC int gsl_vector_fscanf (FILE * stream, gsl_vector * v);
GSL_FUNC int gsl_vector_fprintf (FILE * stream, const gsl_vector * v,
                              const char *format);

GSL_FUNC int gsl_vector_memcpy (gsl_vector * dest, const gsl_vector * src);

GSL_FUNC int gsl_vector_reverse (gsl_vector * v);

GSL_FUNC int gsl_vector_swap (gsl_vector * v, gsl_vector * w);
GSL_FUNC int gsl_vector_swap_elements (gsl_vector * v, const size_t i, const size_t j);

GSL_FUNC double gsl_vector_max (const gsl_vector * v);
GSL_FUNC double gsl_vector_min (const gsl_vector * v);
GSL_FUNC void gsl_vector_minmax (const gsl_vector * v, double * min_out, double * max_out);

GSL_FUNC size_t gsl_vector_max_index (const gsl_vector * v);
GSL_FUNC size_t gsl_vector_min_index (const gsl_vector * v);
GSL_FUNC void gsl_vector_minmax_index (const gsl_vector * v, size_t * imin, size_t * imax);

GSL_FUNC int gsl_vector_add (gsl_vector * a, const gsl_vector * b);
GSL_FUNC int gsl_vector_sub (gsl_vector * a, const gsl_vector * b);
GSL_FUNC int gsl_vector_mul (gsl_vector * a, const gsl_vector * b);
GSL_FUNC int gsl_vector_div (gsl_vector * a, const gsl_vector * b);
GSL_FUNC int gsl_vector_scale (gsl_vector * a, const double x);
GSL_FUNC int gsl_vector_add_constant (gsl_vector * a, const double x);

GSL_FUNC int gsl_vector_isnull (const gsl_vector * v);
GSL_FUNC int gsl_vector_ispos (const gsl_vector * v);
GSL_FUNC int gsl_vector_isneg (const gsl_vector * v);

#ifdef HAVE_INLINE

extern inline
double
gsl_vector_get (const gsl_vector * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return v->data[i * v->stride];
}

extern inline
void
gsl_vector_set (gsl_vector * v, const size_t i, double x)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  v->data[i * v->stride] = x;
}

extern inline
double *
gsl_vector_ptr (gsl_vector * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (double *) (v->data + i * v->stride);
}

extern inline
const double *
gsl_vector_const_ptr (const gsl_vector * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (const double *) (v->data + i * v->stride);
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_DOUBLE_H__ */


#ifndef setupdm_h
#define setupdm_h

#include <ceed.h>
#include <petsc.h>
#include <petscdmplex.h>
#include <petscfe.h>
#include "../include/structs.h"

// -----------------------------------------------------------------------------
// Setup DM
// -----------------------------------------------------------------------------
PetscErrorCode CreateBCLabel(DM dm, const char name[]);

// Create FE by degree
PetscErrorCode PetscFECreateByDegree(DM dm, PetscInt dim, PetscInt Nc,
                                     PetscBool is_simplex, const char prefix[],
                                     PetscInt order, PetscFE *fem);

// Read mesh and distribute DM in parallel
PetscErrorCode CreateDistributedDM(MPI_Comm comm, AppCtx app_ctx, DM *dm);

// Setup DM with FE space of appropriate degree
PetscErrorCode SetupDMByDegree(DM dm, AppCtx app_ctx, PetscInt order,
                               PetscBool boundary, PetscInt num_comp_u);

#endif // setupdm_h


#ifndef PyGSL_TRANSFORM_TYPES_H
#define PyGSL_TRANSFORM_TYPES_H 1
/*
 * All different transforms are handled by only two functions.
 *       PyGSL_transform_
 * and
 *       PyGSL_transform_2d_.
 *
 * This was done as similar functionallity is needed by all the transforms.
 * Copy the input data (transforms are often in place), check or allocate
 * help spaces. Call the function and make clean up. To allow that the
 * functions need to do quite some decisions to prepare all in proper order
 * and to do the clean up.
 * In this file various data types are defined. The first two are needed by the
 * python type which allows to allocate the proper help space and pass it to
 * the function. All the others are needed to store the information about the
 * transform.
 */

/* ------------------------------------------------------------------------- */
/*
 * One Python type is used to encapsulate all the different workspaces. This
 * enum allows the C Code to destinquish between the different types.
 */

enum pygsl_transform_space_type{
	NOSPACE=0,
	COMPLEX_WORKSPACE,
	REAL_WORKSPACE,
	COMPLEX_WAVETABLE,
	REAL_WAVETABLE,
	HALFCOMPLEX_WAVETABLE,
	COMPLEX_WORKSPACE_FLOAT,
	REAL_WORKSPACE_FLOAT,
	COMPLEX_WAVETABLE_FLOAT,
	REAL_WAVETABLE_FLOAT,
	HALFCOMPLEX_WAVETABLE_FLOAT,
	WAVELET_WORKSPACE
};

#include <pygsl/pygsl_features.h>
#include <gsl/gsl_fft.h>
#include <gsl/gsl_fft_complex.h>
#include <gsl/gsl_fft_real.h>
#include <gsl/gsl_fft_halfcomplex.h>
#include <gsl/gsl_fft_complex_float.h>
#include <gsl/gsl_fft_real_float.h>
#include <gsl/gsl_fft_halfcomplex_float.h>
#include <gsl/gsl_blas.h>
#ifdef  _PYGSL_GSL_HAS_WAVELET
#define forward forward_wavelet
#define backward backward_wavelet
#include <gsl/gsl_wavelet.h>
#include <gsl/gsl_wavelet2d.h>
#undef forward
#undef backward
#else /* _PYGSL_GSL_HAS_WAVELET */
#endif /* _PYGSL_GSL_HAS_WAVELET */

/*
 * Here the corresponding union to address the pointer as the proper type.
 */
union pygsl_transform_space_t{
	gsl_fft_complex_workspace           *cws;
	gsl_fft_complex_wavetable           *cwt;
	gsl_fft_real_workspace              *rws;
	gsl_fft_real_wavetable              *rwt;
	gsl_fft_halfcomplex_wavetable       *hcwt;
	gsl_fft_complex_workspace_float     *cwsf;
	gsl_fft_complex_wavetable_float     *cwtf;
	gsl_fft_real_workspace_float        *rwsf;
	gsl_fft_real_wavetable_float        *rwtf;
	gsl_fft_halfcomplex_wavetable_float *hcwtf;
#ifdef _PYGSL_GSL_HAS_WAVELET
	gsl_wavelet_workspace               *wws;
#endif
	void                                *v;
};

/* ------------------------------------------------------------------------- */
/*
 * From here all data types are defined needed to guide the transform
 * functions.
 */
/*
 * Transformation in float or double mode
 */
enum pygsl_transform_mode{
	MODE_DOUBLE = 1,
	MODE_FLOAT
};

/*
 * Input data in ... output data in ...
 */
enum transform_mode{
	ComplexComplex = 1,
	RealReal,
	RealHalfcomplex,
	HalfComplexReal

};

/*
 * Should be renamed. Originally the transform mode would only handle the FFT.
 * There one only needed to destinquish between the radix. Wavelets are also a
 * bit different. Others to come.
 */
enum radix_mode{
	RADIX_TWO = 1,
	RADIX_FREE,
	WAVELET
};

/*
 * Does the basis type consist of only one machine type or two. e.g real
 * is SINGLE_TYPE, complex PACKED_TYPE (a real and an imaginary part).
 */
enum pygsl_packed_type{
	SINGLE_TYPE=1,
	PACKED_TYPE=2
};

#define PyGSL_TRANSFORM_MODE_SWITCH(mode, double_element, float_element) \
        ((mode == MODE_DOUBLE) ? double_element : float_element)

typedef int transform(void * data, size_t stride, size_t N, const void *, void *);
typedef int transform_r2(void * data, size_t stride, size_t N);
#ifdef  _PYGSL_GSL_HAS_WAVELET
typedef int wavelet(const gsl_wavelet * W, double *data, size_t stride, size_t N, void *);
typedef int wavelet2d(const gsl_wavelet * W, gsl_matrix *m, void *);
#endif
typedef void * pygsl_transform_helpn_t(int);
typedef void * pygsl_transform_help_t(void *);

/*
 * The different transform types.
 */
union transforms{
	transform *free;
	transform_r2 *radix2;
#ifdef  _PYGSL_GSL_HAS_WAVELET
	wavelet *wavelet;
	wavelet2d *wavelet2d;
#endif
	void *v;
};

/*
 * Functions to construct and destruct the helpers space and the helpers table.
 * The user can pass them as a proper python type. If not, the proper space
 * will be allocated using these functions.
 */
struct _pygsl_transform_func_rf_s {
	pygsl_transform_helpn_t  * space_alloc;
	pygsl_transform_help_t   * space_free;
	pygsl_transform_helpn_t  * table_alloc;
	pygsl_transform_help_t   * table_free;
	enum pygsl_transform_space_type space_type;
	enum pygsl_transform_space_type table_type;
};

/*
 * Not all transforms need additional workspace. The ones that need it will put
 * an instance somewhere and point the func to the approbriate initalisers.
 * free_space and free_table is used to store if the space and table have to be
 * freeded at the end of the transform.
 */
struct _pygsl_transform_help_rf_s{
	const struct _pygsl_transform_func_rf_s *func;
	void * space;
	void * table;
	int free_space;
	int free_table;
};

/*
 * The info about which transform, what input and output
 * arrays and so forth. Used by _pygsl_transform_help_s to inform the transform
 * function about the various properties.
 */
struct _pygsl_transform_info_s {
	/*
	 *  complex -> complex
	 *  real    -> half_complex
	 *  half_complex -> real
	 *  real -> real
	 */
	enum transform_mode mode;
        /* float or double ? */
	enum pygsl_transform_mode datatype;
	enum PyArray_TYPES input_array_type;
	enum PyArray_TYPES output_array_type;
        /*
	 *  Do I need to add some additional offset to the data
	 *  pointer before
	 *  calling the function. A trick needed for the real to
	 *  halfcomplex transform.
	 */
	int data_offset;
	/* Type of the transform. Should be renamed */
	enum radix_mode radix2;
	/* one or two machine types make on basis type ? */
	enum pygsl_packed_type packed;
};

/*
 * Finally the struct which gathers all the information. The split up was made, as the
 * info struct is always needed, but not the helpers.
 */
struct _pygsl_transform_help_s{
	struct _pygsl_transform_info_s *info;
	union transforms  transform;
	struct _pygsl_transform_help_rf_s *helpers;
};
typedef  struct _pygsl_transform_help_s pygsl_transform_help_s;

/*
 * The main function doing all the work. Its a function for the ffts and a
 *  method for the wavelets!
 */
static PyObject *
PyGSL_transform_(PyObject *self, PyObject *args, pygsl_transform_help_s *helps);

/*
 * Currently only wavelets provide a 2 dimensional transform
 */
#ifdef _PYGSL_GSL_HAS_WAVELET
static PyObject *
PyGSL_transform_2d_(PyObject *self, PyObject *args, pygsl_transform_help_s *helps);
#endif

#endif /* PyGSL_TRANSFORM_TYPES_H */
/*
 * Local Variables:
 * mode: C
 * c-file-style: "python"
 * End:
 */


/*
 * Copyright (c) 2011-2018 The University of Tennessee and The University
 *                         of Tennessee Research Foundation.  All rights
 *                         reserved.
 *
 * @precisions normal z -> s d c
 *
 */
#include "dplasma_cores.h"
#include "dplasma_zcores.h"

#if defined(PARSEC_HAVE_STRING_H)
#include <string.h>
#endif  /* defined(PARSEC_HAVE_STRING_H) */
#if defined(PARSEC_HAVE_STDARG_H)
#include <stdarg.h>
#endif  /* defined(PARSEC_HAVE_STDARG_H) */
#include <stdio.h>
#ifdef PARSEC_HAVE_LIMITS_H
#include <limits.h>
#endif
#include <stdlib.h>

#include <cblas.h>
#include <core_blas.h>

#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#if defined(ADD_)
#define  DLARFG      zlarfg_
#else
#define  DLARFG      zlarfg
#endif
extern void DLARFG(int *N, parsec_complex64_t *ALPHA, parsec_complex64_t *X, int *INCX, parsec_complex64_t *TAU);

//static void band_to_trd_vmpi1(int N, int NB, parsec_complex64_t *A, int LDA);
//static void band_to_trd_vmpi2(int N, int NB, parsec_complex64_t *A, int LDA);
//static void band_to_trd_v8seq(int N, int NB, parsec_complex64_t *A, int LDA, int INgrsiz, int INthgrsiz);
//static int  TRD_seqgralgtype(int N, int NB, parsec_complex64_t *A, int LDA, parsec_complex64_t *C, parsec_complex64_t *S, int i, int j, int m, int grsiz, int BAND);
int  blgchase_ztrdv1(int NT, int N, int NB, parsec_complex64_t *A, parsec_complex64_t *V, parsec_complex64_t *TAU, int sweep, int id, int blktile);
int  blgchase_ztrdv2(int NT, int N, int NB, parsec_complex64_t *A1, parsec_complex64_t *A2, parsec_complex64_t *V1, parsec_complex64_t *TAU1, parsec_complex64_t *V2, parsec_complex64_t *TAU2, int sweep, int id, int blktile);

int CORE_zlarfx2(int side, int N,
                parsec_complex64_t V,
                parsec_complex64_t TAU,
                parsec_complex64_t *C1, int LDC1,
                parsec_complex64_t *C2, int LDC2);

int CORE_zlarfx2c(int uplo,
                parsec_complex64_t V,
                parsec_complex64_t TAU,
                parsec_complex64_t *C1,
                parsec_complex64_t *C2,
                parsec_complex64_t *C3);

static void CORE_zhbtelr(int N, int NB, parsec_complex64_t *A1, int LDA1, parsec_complex64_t *A2, int LDA2, parsec_complex64_t *V1, parsec_complex64_t *TAU1, int st, int ed);
static void CORE_zhbtrce(int N, int NB, parsec_complex64_t *A1, int LDA1, parsec_complex64_t *A2, int LDA2, parsec_complex64_t *V1, parsec_complex64_t *TAU1, parsec_complex64_t *V2, parsec_complex64_t *TAU2, int st, int ed, int edglob);
static void CORE_zhbtlrx(int N, int NB, parsec_complex64_t *A1, int LDA1, parsec_complex64_t *A2, int LDA2, parsec_complex64_t *V1, parsec_complex64_t *TAU1, int st, int ed);

static void DLARFX_C(char side, int N, parsec_complex64_t V, parsec_complex64_t TAU, parsec_complex64_t *C, int LDC);
static void TRD_type1bHL(int N, int NB, parsec_complex64_t *A, int LDA, parsec_complex64_t *V, parsec_complex64_t *TAU, int st, int ed);
static void TRD_type2bHL(int N, int NB, parsec_complex64_t *A, int LDA, parsec_complex64_t *V, parsec_complex64_t *TAU, int st, int ed);
static void TRD_type3bHL(int N, int NB, parsec_complex64_t *A, int LDA, parsec_complex64_t *V, parsec_complex64_t *TAU, int st, int ed);

int CORE_zlarfx2(int side, int N,
                parsec_complex64_t V,
                parsec_complex64_t TAU,
                parsec_complex64_t *C1, int LDC1,
                parsec_complex64_t *C2, int LDC2)
{
    static parsec_complex64_t zzero = 0.0;
    int    J;
    parsec_complex64_t V2, T2, SUM;

    /* Quick return */
    /*
    if (N == 0)
        return PLASMA_SUCCESS;
    */
    if (TAU == zzero)
        return 0;

    /*
     * Special code for 2 x 2 Householder where V1 = I
    */
    if(side==PlasmaLeft){
        V2 = conj(V);
        T2 = TAU*conj(V2);
        for (J = 0; J < N ; J++){
           SUM         = C1[J*LDC1]   + V2*C2[J*LDC2];
           C1[J*LDC1]  = C1[J*LDC1]   - SUM*TAU;
           C2[J*LDC2]  = C2[J*LDC2]   - SUM*T2;
        }
    }else if(side==PlasmaRight){
        V2 = V;
        T2 = TAU*conj(V2);
        for (J = 0; J < N ; J++){
           SUM     = C1[J]   + V2*C2[J];
           C1[J]   = C1[J]   - SUM*TAU;
           C2[J]   = C2[J]   - SUM*T2;
        }
    }

    return 0;
}

/***************************************************************************//**
 *
 **/
int CORE_zlarfx2c(int uplo,
                parsec_complex64_t V,
                parsec_complex64_t TAU,
                parsec_complex64_t *C1,
                parsec_complex64_t *C2,
                parsec_complex64_t *C3)
{
    static parsec_complex64_t zzero = 0.0;
    parsec_complex64_t T2, SUM, TEMP;

    /* Quick return */
    if (TAU == zzero)
        return 0;

   /*
    *        Special code for a diagonal block  C1
    *                                           C2  C3
    */
 if(uplo==PlasmaLower){   //do the corner Left then Right (used for the lower case tridiag)
      // L and R for the 2x2 corner
      //            C(N-1, N-1)  C(N-1,N)        C1  TEMP
      //            C(N  , N-1)  C(N  ,N)        C2  C3
      // For Left : use conj(TAU) and V.
      // For Right: nothing, keep TAU and V.
      // Left 1 ==> C1
      //            C2
      TEMP    = conj(C2[0]); // copy C2 here before modifying it.
      T2      = conj(TAU)*V;
      SUM     = C1[0]   + conj(V)*C2[0];
      C1[0]   = C1[0]   - SUM*conj(TAU);
      C2[0]   = C2[0]   - SUM*T2;
      // Left 2 ==> TEMP
      //            C3
      SUM     = TEMP    + conj(V)*C3[0];
      TEMP    = TEMP    - SUM*conj(TAU);
      C3[0]   = C3[0]   - SUM*T2;
      // Right 1 ==>  C1 TEMP.  NB: no need to compute corner (2,2)=TEMP
      T2      = TAU*conj(V);
      SUM     = C1[0]   + V*TEMP;
      C1[0]   = C1[0]   - SUM*TAU;
      // Right 2 ==> C2 C3
      SUM     = C2[0]   + V*C3[0];
      C2[0]   = C2[0]   - SUM*TAU;
      C3[0]   = C3[0]   - SUM*T2;
 }else if(uplo==PlasmaUpper){ // do the corner Right then Left (used for the upper case tridiag)
      //            C(N-1, N-1)  C(N-1,N)        C1    C2
      //            C(N  , N-1)  C(N  ,N)        TEMP  C3
      // For Left : use TAU       and conj(V).
      // For Right: use conj(TAU) and conj(V).
      // Right 1 ==> C1 C2
      V       = conj(V);
      TEMP    = conj(C2[0]); // copy C2 here before modifying it.
      T2      = conj(TAU)*conj(V);
      SUM     = C1[0]   + V*C2[0];
      C1[0]   = C1[0]   - SUM*conj(TAU);
      C2[0]   = C2[0]   - SUM*T2;
      // Right 2 ==> TEMP C3
      SUM     = TEMP    + V*C3[0];
      TEMP    = TEMP    - SUM*conj(TAU);
      C3[0]   = C3[0]   - SUM*T2;
      // Left 1 ==> C1
      //            TEMP. NB: no need to compute corner (2,1)=TEMP
      T2      = TAU*V;
      SUM     = C1[0]   + conj(V)*TEMP;
      C1[0]   = C1[0]   - SUM*TAU;
      // Left 2 ==> C2
      //            C3
      SUM     = C2[0]   + conj(V)*C3[0];
      C2[0]   = C2[0]   - SUM*TAU;
      C3[0]   = C3[0]   - SUM*T2;
 }

 return 0;
}

///////////////////////////////////////////////////////////
//                  DLARFX en C
///////////////////////////////////////////////////////////
static void DLARFX_C(char side, int N, parsec_complex64_t V, parsec_complex64_t TAU, parsec_complex64_t *C, int LDC)
{
 parsec_complex64_t T2, SUM, TEMP;
 int    J, pt;

/*
 *        Special code for 2 x 2 Householder
 */

 T2 = TAU*V;
 if(side=='L'){
     for (J = 0; J < N ; J++){
        pt      = LDC*J;
        SUM     = C[pt]   + V*C[pt+1];
        C[pt]   = C[pt]   - SUM*TAU;
        C[pt+1] = C[pt+1] - SUM*T2;
     }
 }else if(side=='R'){
     for (J = 0; J < N ; J++){
        pt      = J+LDC;
        SUM     = C[J]   + V*C[pt];
        C[J]    = C[J]   - SUM*TAU;
        C[pt]   = C[pt]  - SUM*T2;
     }
 }else if(side=='B'){
     TEMP    = C[ LDC * (N-2) +1];
     for (J = 0; J < N-1 ; J++){
        pt      = LDC*J;
        SUM     = C[pt]   + V*C[pt+1];
        C[pt]   = C[pt]   - SUM*TAU;
        C[pt+1] = C[pt+1] - SUM*T2;
     }
     // L and R for the 2x2 corner
     //            C(1, N-1)  C(1,N)        C(1, N-1)  TEMP
     //            C(2, N-1)  C(2,N)        C(2, N-1)  C(2,N)
     // Left 1 ==> C(1,N-1) C(2,N-1) deja fait dans la boucle
     //pt      = LDC * (N-2);
     //TEMP    = C[pt+1];
     //SUM     = C[pt]   + V*C[pt+1];
     //C[pt]   = C[pt]   - SUM*TAU;
     //C[pt+1] = C[pt+1] - SUM*T2;
     // Left 2 ==> TEMP C(2,N)
     pt      = LDC * (N-1) +1;
     SUM     = TEMP    + V*C[pt];
     TEMP    = TEMP    - SUM*TAU;
     C[pt]   = C[pt]   - SUM*T2;
     // Right 1 ==>  C(1,N-1) TEMP.  NB: no need to compute corner (2,2)
     J       = LDC * (N-2);
     SUM     = C[J]   + V*TEMP;
     C[J]    = C[J]   - SUM*TAU;
     // Right 2 ==> C(2,N-1) C(2,N)
     J      = LDC * (N-2) + 1;
     pt     = LDC * (N-1) + 1;
     SUM    = C[J]   + V*C[pt];
     C[J]   = C[J]   - SUM*TAU;
     C[pt]  = C[pt]  - SUM*T2;
 }
}
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
#define A1(m,n)   &(A1[((m)-(n)) + LDA1*(n)])
#define A2(m,n)   &(A2[((m)-(n)) + LDA2*((n)-NB)])
#define V1(m)     &(V1[m-st])
#define TAU1(m)   &(TAU1[m-st])
#define V2(m)     &(V2[m-st])
#define TAU2(m)   &(TAU2[m-st])
///////////////////////////////////////////////////////////
//                  TYPE 1-BAND Householder
///////////////////////////////////////////////////////////
static void CORE_zhbtelr(int N, int NB, parsec_complex64_t *A1, int LDA1, parsec_complex64_t *A2, int LDA2, parsec_complex64_t *V1, parsec_complex64_t *TAU1, int st, int ed) {
  int    J1, J2, KDM1, LDX;
  int    len, len1, len2, t1ed, t2st;
  int    i, IONE, ITWO;
  IONE=1;
  ITWO=2;
  (void)N;

  KDM1 = NB-1;
  LDX = LDA2-1;
  /* **********************************************************************************************
   *   Annihiliate, then LEFT:
   * ***********************************************************************************************/
  for (i = ed; i >= st+1 ; i--){
     /* generate Householder to annihilate a(i+k-1,i) within the band */
     *V1(i)          = *A1(i, (st-1));
     *A1(i, (st-1))  = 0.0;
     DLARFG( &ITWO, A1((i-1),(st-1)), V1(i), &IONE, TAU1(i) );

     J1   = st;
     J2   = i-2;
     t1ed = min(J2,KDM1);
     t2st = max(J1, NB);
     len1 = t1ed - J1 +1;
     len2 = J2 - t2st +1;
     // printf("Type 1L st %d   ed %d    i %d    J1 %d   J2 %d  t1ed %d  t2st %d len1 %d len2 %d \n", st, ed, i, J1,J2,t1ed,t2st,len1,len2);
     /* apply reflector from the left (horizontal row) and from the right for only the diagonal 2x2.*/
     if(len2>=0){
        /* part of the left(if len2>0) and the corner are on tile T2 */
        if(len2>0) CORE_zlarfx2(PlasmaLeft, len2 , *V1(i), conj(*TAU1(i)), A2((i-1), t2st), LDX, A2(i, t2st), LDX);
        CORE_zlarfx2c(PlasmaLower, *V1(i), *TAU1(i), A2(i-1,i-1), A2(i,i-1), A2(i,i));
        if(len1>0) CORE_zlarfx2(PlasmaLeft, len1 , *V1(i), conj(*TAU1(i)), A1(i-1, J1), LDX, A1(i, J1), LDX);
     }else if(len2==-1){
        /* the left is on tile T1, and only A(i,i) of the corner is on tile T2 */
        CORE_zlarfx2c(PlasmaLower, *V1(i), *TAU1(i), A1(i-1,i-1), A1(i,i-1), A2(i,i));
        if(len1>0) CORE_zlarfx2(PlasmaLeft, len1 , *V1(i), conj(*TAU1(i)), A1(i-1, J1), LDX, A1(i, J1), LDX);
     }else{
        /* the left and the corner are on tile T1, nothing on tile T2 */
        CORE_zlarfx2c(PlasmaLower, *V1(i), *TAU1(i), A1(i-1,i-1), A1(i,i-1), A1(i,i)) ;
        if(len1>0) CORE_zlarfx2(PlasmaLeft, len1 , *V1(i), conj(*TAU1(i)), A1((i-1), J1), LDX, A1(i, J1), LDX);
     }
  }
  /* **********************************************************************************************
   *   APPLY RIGHT ON THE REMAINING ELEMENT OF KERNEL 1
   * ***********************************************************************************************/
  for (i = ed; i >= st+1 ; i--){
     J1    = i+1;
     J2    = ed;
     len   = J2-J1+1;
     if(len>0){
        if(i>NB)
           /* both column (i-1) and i are on tile T2 */
           CORE_zlarfx2(PlasmaRight, len, *V1(i), *TAU1(i), A2(J1, i-1), LDX, A2(J1, i), LDX);
        else if(i==NB)
           /* column (i-1) is on tile T1 while column i is on tile T2 */
           CORE_zlarfx2(PlasmaRight, len, *V1(i), *TAU1(i), A1(J1, i-1), LDX, A2(J1, i), LDX);
        else
           /* both column (i-1) and i are on tile T1 */
           CORE_zlarfx2(PlasmaRight, len, *V1(i), *TAU1(i), A1(J1, i-1), LDX, A1(J1, i), LDX);
     }
  }
}
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
//                  TYPE 2-BAND Householder
///////////////////////////////////////////////////////////
static void CORE_zhbtrce(int N, int NB, parsec_complex64_t *A1, int LDA1, parsec_complex64_t *A2, int LDA2, parsec_complex64_t *V1, parsec_complex64_t *TAU1, parsec_complex64_t *V2, parsec_complex64_t *TAU2, int st, int ed, int edglob) {
  int    J1, J2, J3, KDM1, LDX, pt;
  int    len, len1, len2, t1ed, t2st, iglob;
  int    i, IONE, ITWO;
  parsec_complex64_t V,T,SUM;
  IONE=1;
  ITWO=2;

  iglob = edglob+1;
  LDX   = LDA1-1;
  KDM1  = NB-1;
  /* **********************************************************************************************
   *   Right:
   * ***********************************************************************************************/
  for (i = ed; i >= st+1 ; i--){
     /* apply Householder from the right. and create newnnz outside the band if J3 < N */
     iglob = iglob -1;
     J1  = ed+1;
     if((iglob+NB)>= N){
        J2 = i +(N-iglob-1);
        J3 = J2;
     }else{
        J2 = i + KDM1;
        J3 = J2+1;
     }
     len   = J2-J1+1;
     /* printf("Type 2R st %d   ed %d    i %d   J1 %d   J2 %d  len %d iglob %d  \n",st,ed,i,J1,J2,len,iglob);*/

     if(len>0){
        if(i>NB){
           /* both column (i-1) and i are on tile T2 */
           CORE_zlarfx2(PlasmaRight, len, *V1(i), *TAU1(i), A2(J1, i-1), LDX, A2(J1, i), LDX);
           /* if nonzero element need to be created outside the band (if index < N) then create and eliminate it. */
           if(J3>J2){
              /* new nnz at TEMP=V2(i)  */
              V          = *V1(i);
              T          = *TAU1(i) * conj(V);
              SUM        =  V * (*A2(J3, i));
              *V2(i)     = -SUM * (*TAU1(i));
              *A2(J3, i) = *A2(J3, i) - SUM * T;
              /* generate Householder to annihilate a(j+kd,j-1) within the band */
              DLARFG( &ITWO, A2(J2,i-1), V2(i), &IONE, TAU2(i) );
           }
        }else if(i==NB){
           /* column (i-1) is on tile T1 while column i is on tile T2 */
           CORE_zlarfx2(PlasmaRight, len, *V1(i), *TAU1(i), A1(J1, i-1), LDX, A2(J1, i), LDX);
           /* if nonzero element need to be created outside the band (if index < N) then create and eliminate it. */
           if(J3>J2){
              /* new nnz at TEMP=V2(i)  */
              V          = *V1(i);
              T          = *TAU1(i) * conj(V);
              SUM        =  V * (*A2(J3, i));
              *V2(i)     = -SUM * (*TAU1(i));
              *A2(J3, i) = *A2(J3, i) - SUM * T;
              /* generate Householder to annihilate a(j+kd,j-1) within the band */
              DLARFG( &ITWO, A1(J2, i-1), V2(i), &IONE, TAU2(i) );
           }
        }else{
           /* both column (i-1) and i are on tile T1 */
           CORE_zlarfx2(PlasmaRight, len, *V1(i), *TAU1(i), A1(J1, i-1), LDX, A1(J1, i), LDX);
           /* if nonzero element need to be created outside the band (if index < N) then create and eliminate it. */
           if(J3>J2){
              /* new nnz at TEMP=V2(i)  */
              V              = *V1(i);
              T              = *TAU1(i) * conj(V);
              SUM            =  V * (*A1(J3, i));
              *V2(i)         = -SUM * (*TAU1(i));
              *A1(J3, i)    = *A1(J3, i) - SUM * T;
              /* generate Householder to annihilate a(j+kd,j-1) within the band */
              DLARFG( &ITWO, A1(J2, i-1), V2(i), &IONE, TAU2(i) );
           }
        }
     }
  }
              // if(id==1) return;

  /* **********************************************************************************************
   *   APPLY LEFT ON THE REMAINING ELEMENT OF KERNEL 1
   * ***********************************************************************************************/
  iglob = edglob+1;
  for (i = ed; i >= st+1 ; i--){
     iglob = iglob -1;
     if((iglob+NB)< N){ /* mean that J3>J2 and so a nnz has been created and so a left is required. */
        J1   = i;
        J2   = ed;
        t1ed = min(J2,KDM1);
        t2st = max(J1, NB);
        len1 = t1ed - J1 +1;
        len2 = J2 - t2st +1;
        pt   = i + KDM1; /* pt correspond to the J2 position of the corresponding right done above */
        //printf("Type 2L st %d   ed %d    i %d    J1 %d   J2 %d  t1ed %d  t2st %d len1 %d len2 %d \n", st, ed, i, J1,J2,t1ed,t2st,len1,len2);

        /* apply reflector from the left (horizontal row) and from the right for only the diagonal 2x2.*/
        if(len2>0){
           /* part of the left(if len2>0) and the corner are on tile T2 */
           CORE_zlarfx2(PlasmaLeft, len2 , *V2(i), conj(*TAU2(i)), A2(pt, t2st), LDX, A2(pt+1, t2st), LDX);
           if(len1>0) CORE_zlarfx2(PlasmaLeft, len1 , *V2(i), conj(*TAU2(i)), A1(pt, J1), LDX, A1(pt+1, J1), LDX);
        }else if(len1>0){
           /* the left and the corner are on tile T1, nothing on tile T2 */
           CORE_zlarfx2(PlasmaLeft, len1 , *V2(i), conj(*TAU2(i)), A1(pt, J1), LDX, A1(pt+1, J1), LDX);
        }
     }
  }
}
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
//                  TYPE 1-BAND Householder
///////////////////////////////////////////////////////////
static void CORE_zhbtlrx(int N, int NB, parsec_complex64_t *A1, int LDA1, parsec_complex64_t *A2, int LDA2, parsec_complex64_t *V1, parsec_complex64_t *TAU1, int st, int ed) {
  int    J1, J2, KDM1, LDX;
  int    len, len1, len2, t1ed, t2st;
  int    i;

  (void)N;
  KDM1 = NB-1;
  LDX = LDA2-1;
  /* **********************************************************************************************
   *   Annihiliate, then LEFT:
   * ***********************************************************************************************/
  for (i = ed; i >= st+1 ; i--){
     J1   = st;
     J2   = i-2;
     t1ed = min(J2,KDM1);
     t2st = max(J1, NB);
     len1 = t1ed - J1 +1;
     len2 = J2 - t2st +1;
     //printf("Type 3L st %d   ed %d    i %d    J1 %d   J2 %d  t1ed %d  t2st %d len1 %d len2 %d \n", st, ed, i, J1,J2,t1ed,t2st,len1,len2);
     /* apply reflector from the left (horizontal row) and from the right for only the diagonal 2x2.*/
     if(len2>=0){
        /* part of the left(if len2>0) and the corner are on tile T2 */
        if(len2>0) CORE_zlarfx2(PlasmaLeft, len2 , *V1(i), conj(*TAU1(i)), A2((i-1), t2st), LDX, A2(i, t2st), LDX);
        CORE_zlarfx2c(PlasmaLower, *V1(i), *TAU1(i), A2(i-1,i-1), A2(i,i-1), A2(i,i));
        if(len1>0) CORE_zlarfx2(PlasmaLeft, len1 , *V1(i), conj(*TAU1(i)), A1(i-1, J1), LDX, A1(i, J1), LDX);
     }else if(len2==-1){
        /* the left is on tile T1, and only A(i,i) of the corner is on tile T2 */
        CORE_zlarfx2c(PlasmaLower, *V1(i), *TAU1(i), A1(i-1,i-1), A1(i,i-1), A2(i,i));
        if(len1>0) CORE_zlarfx2(PlasmaLeft, len1 , *V1(i), conj(*TAU1(i)), A1(i-1, J1), LDX, A1(i, J1), LDX);
     }else{
        /* the left and the corner are on tile T1, nothing on tile T2 */
        CORE_zlarfx2c(PlasmaLower, *V1(i), *TAU1(i), A1(i-1,i-1), A1(i,i-1), A1(i,i)) ;
        if(len1>0) CORE_zlarfx2(PlasmaLeft, len1 , *V1(i), conj(*TAU1(i)), A1((i-1), J1), LDX, A1(i, J1), LDX);
     }
  }
  /* **********************************************************************************************
   *   APPLY RIGHT ON THE REMAINING ELEMENT OF KERNEL 1
   * ***********************************************************************************************/
  for (i = ed; i >= st+1 ; i--){
     J1    = i+1;
     J2    = ed;
     len   = J2-J1+1;
     if(len>0){
        if(i>NB)
           /* both column (i-1) and i are on tile T2 */
           CORE_zlarfx2(PlasmaRight, len, *V1(i), *TAU1(i), A2(J1, i-1), LDX, A2(J1, i), LDX);
        else if(i==NB)
           /* column (i-1) is on tile T1 while column i is on tile T2 */
           CORE_zlarfx2(PlasmaRight, len, *V1(i), *TAU1(i), A1(J1, i-1), LDX, A2(J1, i), LDX);
        else
           /* both column (i-1) and i are on tile T1 */
           CORE_zlarfx2(PlasmaRight, len, *V1(i), *TAU1(i), A1(J1, i-1), LDX, A1(J1, i), LDX);
     }
  }
}
///////////////////////////////////////////////////////////
#undef A1
#undef A2
#undef V1
#undef TAU1
#undef V2
#undef TAU2

///////////////////////////////////////////////////////////
//                  TYPE 1-BAND Householder
///////////////////////////////////////////////////////////
//// add -1 because of C
#define A(m,n)   &(A[((m)-(n)) + LDA*((n)-1)])
#define V(m)     &(V[m-1])
#define TAU(m)   &(TAU[m-1])
static void TRD_type1bHL(int N, int NB, parsec_complex64_t *A, int LDA, parsec_complex64_t *V, parsec_complex64_t *TAU, int st, int ed) {
  int    J1, J2, len, LDX;
  int    i, IONE, ITWO;
  IONE=1;
  ITWO=2;
  (void)NB;

  if(ed <= st){
    printf("TRD_type 1bH: ERROR st and ed %d  %d \n",st,ed);
    exit(-10);
  }

  LDX = LDA-1;
  for (i = ed; i >= st+1 ; i--){
     // generate Householder to annihilate a(i+k-1,i) within the band
     *V(i)          = *A(i, (st-1));
     *A(i, (st-1))  = 0.0;
     DLARFG( &ITWO, A((i-1),(st-1)), V(i), &IONE, TAU(i) );

     // apply reflector from the left (horizontal row) and from the right for only the diagonal 2x2.
     J1  = st;
     J2  = i;
     len = J2-J1+1;
     printf("voici J1 %d J2 %d len %d \n",J1,J2,len);
     DLARFX_C('B', len , *V(i), *TAU(i), A((i-1),J1   ), LDX);
  }

  for (i = ed; i >= st+1 ; i--){
     len = min(ed,N)-i;
     if(len>0)DLARFX_C('R', len, *V(i), *TAU(i), A((i+1),(i-1)), LDX);
  }

}
#undef A
#undef V
#undef TAU
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
//                  TYPE 2-BAND Householder
///////////////////////////////////////////////////////////
//// add -1 because of C
#define A(m,n)   &(A[((m)-(n)) + LDA*((n)-1)])
#define V(m)     &(V[m-1])
#define TAU(m)   &(TAU[m-1])
static void TRD_type2bHL(int N, int NB, parsec_complex64_t *A, int LDA, parsec_complex64_t *V, parsec_complex64_t *TAU, int st, int ed) {
  int    J1, J2, J3, KDM2, len, LDX;
  int    i, IONE, ITWO;
  IONE=1;
  ITWO=2;

  if(ed <= st){
    printf("TRD_type 2H: ERROR st and ed %d  %d \n",st,ed);
    exit(-10);;
  }

  LDX  = LDA -1;
  KDM2 = NB-2;
  for (i = ed; i >= st+1 ; i--){
     // apply Householder from the right. and create newnnz outside the band if J3 < N
     J1  = ed+1;
     J2  = min((i+1+KDM2), N);
     J3  = min((J2+1), N);
     len = J2-J1+1;
     DLARFX_C('R', len, *V(i), *TAU(i), A(J1,(i-1)), LDX);

     // if nonzero element a(j+kd,j-1) has been created outside the band (if index < N) then eliminate it.
     len    = J3-J2; // soit 1 soit 0
     if(len>0){
         //  new nnz at TEMP=V(J3)
         *V(J3)     =            - *A(J3,(i)) * (*TAU(i)) * (*V(i));
         *A(J3,(i)) = *A(J3,(i)) + *V(J3)  * (*V(i)); //ATTENTION THIS replacement IS VALID IN FLOAT CASE NOT IN COMPLEX
         // generate Householder to annihilate a(j+kd,j-1) within the band
         DLARFG( &ITWO, A(J2,(i-1)), V(J3), &IONE, TAU(J3) );
     }
  }
               //if(id==1) return;

  for (i = ed; i >= st+1 ; i--){
     J2  = min((i+1+KDM2), N);
     J3  = min((J2+1), N);
     len    = J3-J2;
     if(len>0){
        len = min(ed,N)-i+1;
        DLARFX_C('L', len , *V(J3), *TAU(J3), A(J2, i), LDX);
     }
  }

}
#undef A
#undef V
#undef TAU
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
//                  TYPE 3-BAND Householder
///////////////////////////////////////////////////////////
//// add -1 because of C
#define A(m,n)   &(A[((m)-(n)) + LDA*((n)-1)])
#define V(m)     &(V[m-1])
#define TAU(m)   &(TAU[m-1])
static void TRD_type3bHL(int N, int NB, parsec_complex64_t *A, int LDA, parsec_complex64_t *V, parsec_complex64_t *TAU, int st, int ed) {
  int    J1, J2, len, LDX;
  int    i;
  (void)NB;

  if(ed <= st){
    printf("TRD_type 3H: ERROR st and ed %d  %d \n",st,ed);
    exit(-10);
  }

  LDX = LDA-1;
  for (i = ed; i >= st+1 ; i--){
     // apply rotation from the left. faire le DROT horizontal
     J1  = st;
     J2  = i;
     len = J2-J1+1;
     //len = NB+1;
     DLARFX_C('B', len , *V(i), *TAU(i), A((i-1),J1   ), LDX);
  }

  for (i = ed; i >= st+1 ; i--){
     len = min(ed,N)-i;
     if(len>0)DLARFX_C('R', len, *V(i), *TAU(i), A((i+1),(i-1)), LDX);
  }
}
#undef A
#undef V
#undef TAU
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
//                  grouping sched wrapper call
///////////////////////////////////////////////////////////
#if 0
int TRD_seqgralgtype(int N, int NB, parsec_complex64_t *A, int LDA, parsec_complex64_t *C, parsec_complex64_t *S, int i, int j, int m, int grsiz, int BAND) {
  int    k,shift=3;
  int    myid,colpt,stind,edind,blklastind,stepercol;
  (void) BAND;

  k   = shift/grsiz;
  stepercol =  k*grsiz == shift ? k:k+1;

   for (k = 1; k <=grsiz; k++){
      myid = (i-j)*(stepercol*grsiz) +(m-1)*grsiz + k;
      if(myid%2 ==0){
           colpt      = (myid/2)*NB+1+j-1;
           stind      = colpt-NB+1;
           edind      = min(colpt,N);
           blklastind = colpt;
           if(stind>=edind){
               printf("TRD_seqalg ERROR---------> st>=ed  %d  %d \n\n",stind, edind);
               return -10;
           }
      }else{
           colpt      = ((myid+1)/2)*NB + 1 +j -1 ;
           stind      = colpt-NB+1;
           edind      = min(colpt,N);
           if( (stind>=edind-1) && (edind==N) )
               blklastind=N;
           else
               blklastind=0;
           if(stind>=edind){
               printf("TRD_seqalg ERROR---------> st>=ed  %d  %d \n\n",stind, edind);
               return -10;

           }
      }

          if(myid == 1)
              TRD_type1bHL(N, NB, A, LDA, C, S, stind, edind);
          else if(myid%2 == 0)
              TRD_type2bHL(N, NB, A, LDA, C, S, stind, edind);
          else if(myid%2 == 1)
              TRD_type3bHL(N, NB, A, LDA, C, S, stind, edind);
          else{
              printf("COUCOU ERROR myid/2 %d\n",myid);
               return -10;
          }

      if(blklastind >= (N-1))  break;
      //if(myid==2)return;
  }   // END for k=1:grsiz
return 0;
}
///////////////////////////////////////////////////////////
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                  REDUCTION BAND TO TRIDIAG
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if 0
static void band_to_trd_v8seq(int N, int NB, parsec_complex64_t *A, int LDA, int INgrsiz, int INthgrsiz) {
  int myid, grsiz, shift, stt, st, ed, stind, edind, BAND;
  int blklastind, colpt;
  int stepercol,mylastid;
  parsec_complex64_t *C, *S;
  int i,j,m;
  int thgrsiz, thgrnb, thgrid, thed;
  int INFO;
  INFO=-1;
  BAND = 0;
  C   = malloc(N*sizeof(parsec_complex64_t));
  S   = malloc(N*sizeof(parsec_complex64_t));
  memset(C,0,N*sizeof(parsec_complex64_t));
  memset(S,0,N*sizeof(parsec_complex64_t));

  grsiz   = INgrsiz;
  thgrsiz = INthgrsiz;

  shift   = 3;
  if(grsiz==0)grsiz   = 6;
  if(thgrsiz==0)thgrsiz = N;

  if(LDA != (NB+1))
  {
      printf(" ERROR LDA not equal NB+1 and this code is special for LDA=NB+1. LDA=%d NB+1=%d \n",LDA,NB+1);
      return;
  }
  printf("  Version -8seq- grsiz %4d     thgrsiz %4d       N %5d      NB %5d    BAND %5d\n",grsiz,thgrsiz, N, NB, BAND);

  i = shift/grsiz;
  stepercol =  i*grsiz == shift ? i:i+1;

  i       = (N-2)/thgrsiz;
  thgrnb  = i*thgrsiz == (N-2) ? i:i+1;

  for (thgrid = 1; thgrid<=thgrnb; thgrid++){
     stt  = (thgrid-1)*thgrsiz+1;
     thed = min( (stt + thgrsiz -1), (N-2));
     for (i = stt; i <= N-2; i++){
        ed=min(i,thed);
        if(stt>ed)break;
        for (m = 1; m <=stepercol; m++){
            st=stt;
            for (j = st; j <=ed; j++){
                 myid     = (i-j)*(stepercol*grsiz) +(m-1)*grsiz + 1;
                 mylastid = myid+grsiz-1;
                 INFO = TRD_seqgralgtype(N, NB, A, LDA, C, S, i, j, m, grsiz, BAND);
                 if(INFO!=0){
                         printf("ERROR band_to_trd_v8seq INFO=%d\n",INFO);
                         return;
                 }
                 if(mylastid%2 ==0){
                     blklastind      = (mylastid/2)*NB+1+j-1;
                 }else{
                      colpt      = ((mylastid+1)/2)*NB + 1 +j -1 ;
                      stind      = colpt-NB+1;
                      edind      = min(colpt,N);
                      if( (stind>=edind-1) && (edind==N) )
                          blklastind=N;
                      else
                          blklastind=0;
                 }
                 if(blklastind >= (N-1))  stt=stt+1;
           } // END for j=st:ed
        } // END for m=1:stepercol
     } // END for i=1:N-2
  } // END for thgrid=1:thgrnb

} // END FUNCTION
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
int blgchase_ztrdv1(int NT, int N, int NB, parsec_complex64_t *A, parsec_complex64_t *V, parsec_complex64_t *TAU, int sweep, int id, int blktile) {
  int    /*edloc,*/ stloc, st, ed, KDM1, LDA;

  (void)NT;
  KDM1   = NB-1;
  LDA    = NB+1;
  /* generate the indiceslocal and global*/
  stloc  = (sweep+1)%NB;
  if(stloc==0) stloc=NB;
  /*if(id==NT-1)
     edloc = NB-1;
  else
     edloc = stloc + KDM1;
  */

  st = min(id*NB+stloc, N-1);
  ed = min(st+KDM1, N-1);
  /*
   * i     = (N-1)%ed;
   * edloc = i%NB+NB;
   */

  /* quick return in case of last tile */
  if(st==ed)
     return 0;

  /* because the kernel have been writted for fortran, add 1 */
   st = st +1;
   ed = ed +1;
  /* code for all tiles */
  if(id==blktile){
     TRD_type1bHL(N, NB, A, LDA, V, TAU, st, ed);
     TRD_type2bHL(N, NB, A, LDA, V, TAU, st, ed);
  }else{
     TRD_type3bHL(N, NB, A, LDA, V, TAU, st, ed);
     //if(id==6) return;
     TRD_type2bHL(N, NB, A, LDA, V, TAU, st, ed);
  }
  return 0;
}

#if 0
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void band_to_trd_vmpi1(int N, int NB, parsec_complex64_t *A, int LDA) {
  int NT;
  parsec_complex64_t *V, *TAU;
  int blktile, S, id, sweep;
  V   = malloc(N*sizeof(parsec_complex64_t));
  TAU = malloc(N*sizeof(parsec_complex64_t));
  memset(V,0,N*sizeof(parsec_complex64_t));
  memset(TAU,0,N*sizeof(parsec_complex64_t));

  NT = N/NB;
  if(NT*NB != N){
      printf("ERROR NT*NB not equal N \n");
      return;
  }

  //printf("voici NT, N NB %d %d %d\n",NT,N,NB);
  for (blktile = 0; blktile<NT; blktile++){
     for (S = 0; S<NB; S++){
        sweep = blktile*NB + S ;
        for (id = blktile; id<NT; id++){
               blgchase_ztrdv1 (NT, N, NB, A, V, TAU, sweep , id, blktile);
        }
     }
  }

}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
int blgchase_ztrdv2(int NT, int N, int NB, parsec_complex64_t *A1, parsec_complex64_t *A2, parsec_complex64_t *V1, parsec_complex64_t *TAU1, parsec_complex64_t *V2, parsec_complex64_t *TAU2, int sweep, int id, int blktile) {
  int    edloc, stloc, st, ed, KDM1, LDA;

  KDM1   = NB-1;
  LDA    = NB+1;
  /* generate the indiceslocal and global*/
  stloc  = (sweep+1)%NB;
  if(stloc==0) stloc=NB;
  if(id==NT-1)
     edloc = NB-1;
  else
     edloc = stloc + KDM1;

  st = min(id*NB+stloc, N-1);
  ed = min(st+KDM1, N-1);
  /*
   * i     = (N-1)%ed;
   * edloc = i%NB+NB;
   */

  /* quick return in case of last tile */
  if(st==ed)
     return 0;

  /* code for all tiles */
  if(id==blktile){
     CORE_zhbtelr(N, NB, A1, LDA, A2, LDA, V1, TAU1, stloc, edloc);
     CORE_zhbtrce(N, NB, A1, LDA, A2, LDA, V1, TAU1, V2, TAU2, stloc, edloc, ed);
  }else{
     CORE_zhbtlrx(N, NB, A1, LDA, A2, LDA, V1, TAU1, stloc, edloc);
     CORE_zhbtrce(N, NB, A1, LDA, A2, LDA, V1, TAU1, V2, TAU2, stloc, edloc, ed);
  }
  return 0;
}

#if 0
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void band_to_trd_vmpi2(int N, int NB, parsec_complex64_t *A, int LDA) {
    int NT;
    parsec_complex64_t *V, *TAU;
    int blktile, S, id, sweep;
    V   = malloc(N*sizeof(parsec_complex64_t));
    TAU = malloc(N*sizeof(parsec_complex64_t));
    memset(V,0,N*sizeof(parsec_complex64_t));
    memset(TAU,0,N*sizeof(parsec_complex64_t));

    NT = N/NB;
    if(NT*NB != N){
        printf("ERROR NT*NB not equal N \n");
        return;
    }

    //printf("voici NT, N NB %d %d %d\n",NT,N,NB);
    for (blktile = 0; blktile<NT; blktile++){
        for (S = 0; S<NB; S++){
            sweep = blktile*NB + S ;
            for (id = blktile; id<NT; id++){
                //printf("voici  blktile %d    S %d     id %d   sweep %d   \n",blktile, S, id, sweep);
                blgchase_ztrdv2 (NT, N, NB,
                                A+(id*NB*LDA), A+((id+1)*NB*LDA),
                                V+(id*NB), TAU+(id*NB),
                                V+((id+1)*NB), TAU+((id+1)*NB),
                                sweep, id, blktile);
            }
            //if(sweep==0) return;
        }
    }

}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endif


#ifndef MATCH_H_COIEXYNJ
#define MATCH_H_COIEXYNJ

#include <gsl/gsl>
#include <sens_loc/analysis/recognition_performance.h>
#include <sens_loc/math/pointcloud.h>
#include <utility>
#include <vector>

namespace sens_loc::analysis {

/// Create linear vectors that pair the keypoints from \c kp0 to \c kp1
/// according to \c matches.
///
/// \pre \c query is the query set in \c matches.
/// \pre \c train is the training set in \c matches.
/// \param matches Set of matches in \c query and \c train
/// \param query Set of keypoints that were matched as the query set.
/// \param train Set of keypoints that were matched as the training set.
/// \returns \c pair{QueriedKps, TrainedKps} were each vector contains only
/// matched keypoints. All other keypoints are discarded in the result.
std::pair<math::imagepoints_t, math::imagepoints_t>
gather_correspondences(gsl::span<const keypoint_correspondence> matches,
                       const math::imagepoints_t&               query,
                       const math::imagepoints_t&               train) noexcept;
}  // namespace sens_loc::analysis

#endif /* end of include guard: MATCH_H_COIEXYNJ */


// MIT License Copyright (c) 2020 Jarrett Wendt

#pragma once

#define _SILENCE_CLANG_COROUTINE_MESSAGE

// Standard
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <filesystem>
#include <fstream>
#include <functional>
#include <future>
#include <initializer_list>
#include <iostream>
#include <new>
#include <random>
#include <sstream>
#include <stdexcept>
#include <string>
#include <thread>
#include <tuple>
#include <utility>
#include <vector>

#ifdef _WIN32
#include <experimental/coroutine>
#else
#include <coroutine>
#endif

// External
#include <gsl/gsl>


#ifndef S3D_DISPARITY_VIEWER_DEPTH_CONVERTER_H
#define S3D_DISPARITY_VIEWER_DEPTH_CONVERTER_H

#include <s3d/utilities/eigen.h>

#include <vector>

#include <gsl/gsl>

namespace s3d {

struct ViewerContext;

class ViewerDepthConverter {
 public:
  using Point = Eigen::Vector2d;
  using Pointf = Eigen::Vector2f;

  explicit ViewerDepthConverter(gsl::not_null<ViewerContext*> context);

  std::vector<float> computePerceivedDepth(const std::vector<float>& disparitiesPercent);

  // horizontal position and depth in meters
  std::vector<Pointf> computeDepthPositions(const std::vector<Point>& imagePoints,
                                            const std::vector<float>& disparities);

  float computePerceivedDepth(float disparityPercent);

  // 0 in the middle of the image
  float computeHorizontalPosition(float imageX);

  std::vector<float> computeHorizontalPositions(const std::vector<Point>& imagePoints);

  void setViewerContext(gsl::not_null<ViewerContext*> viewerContext);

 private:
  ViewerContext* viewerContext_;
};
}  // namespace s3d

#endif  // S3D_DISPARITY_VIEWER_DEPTH_CONVERTER_H


#ifndef OPENMC_TALLIES_FILTER_MU_H
#define OPENMC_TALLIES_FILTER_MU_H

#include <vector>

#include <gsl/gsl>

#include "openmc/tallies/filter.h"

namespace openmc {

//==============================================================================
//! Bins the incoming-outgoing direction cosine.  This is only used for scatter
//! reactions.
//==============================================================================

class MuFilter : public Filter
{
public:
  //----------------------------------------------------------------------------
  // Constructors, destructors

  ~MuFilter() = default;

  //----------------------------------------------------------------------------
  // Methods

  std::string type() const override {return "mu";}

  void from_xml(pugi::xml_node node) override;

  void get_all_bins(const Particle& p, TallyEstimator estimator, FilterMatch& match)
  const override;

  void to_statepoint(hid_t filter_group) const override;

  std::string text_label(int bin) const override;

  //----------------------------------------------------------------------------
  // Accessors

  void set_bins(gsl::span<double> bins);

private:
  //----------------------------------------------------------------------------
  // Data members

  std::vector<double> bins_;
};

} // namespace openmc
#endif // OPENMC_TALLIES_FILTER_MU_H