#pragma once

#include <cblas.h>
#include <stdlib.h>
#include <immintrin.h>

#include <faiss/IndexIVF.h>

#include <faiss/utils/Heap.h>

#ifdef OPT_DTYPE_UTILS

namespace faiss {

//==================================Convertion================================

inline const float* convert_x_T_impl (size_t, const float* x, float*) {
    return x;
}

template <typename T>
const T* convert_x_T_impl (size_t d, const float* x, T*) {
    T* conv_x = new T[d];
    for (size_t i = 0; i < d; i++) {
        conv_x[i] = static_cast<T> (x[i]);
    }
    return conv_x;
}

template <typename T>
inline const T* convert_x_T (size_t d, const float* x) {
    return convert_x_T_impl (d, x, (T*)nullptr);
}

inline void del_converted_x_T (size_t, const float*) {
}

template <typename T>
inline void del_converted_x_T (size_t, const T* conv_x) {
    delete[] conv_x;
}

template <typename T>
struct Converter_T {

    const size_t d;
    const T* const x;

    Converter_T (size_t d, const float* x):
            d (d), x (convert_x_T<T> (d, x)) {
    }

    ~Converter_T () {
        del_converted_x_T (d, x);
    }

};

//==============================Distance Function=============================

template <typename Tdis, typename T>
Tdis vec_IP_ref_T (const T* x, const T* y, size_t d, Tdis sum = 0) {
    for (size_t i = 0; i < d; i++) {
        sum += static_cast<Tdis> (x[i]) * static_cast<Tdis> (y[i]);
    }
    return sum;
}

inline float vec_IP_ref_T (const float* x, const float* y, size_t d) {
    return vec_IP_ref_T<float> (x, y, d);
}

inline float vec_IP_ref_T (const bfp16_t* x, const bfp16_t* y,
        size_t d) {
    return vec_IP_ref_T<float> (x, y, d);
}

template <typename Tdis, typename T>
Tdis vec_L2Sqr_ref_T (const T* x, const T* y, size_t d, Tdis sum = 0) {
    for (size_t i = 0; i < d; i++) {
        Tdis diff = static_cast<Tdis> (x[i]) - static_cast<Tdis> (y[i]);
        sum += diff * diff;
    }
    return sum;
}

inline float vec_L2Sqr_ref_T (const float* x, const float* y, size_t d) {
    return vec_L2Sqr_ref_T<float> (x, y, d);
}

inline float vec_L2Sqr_ref_T (const bfp16_t* x, const bfp16_t* y,
        size_t d) {
    return vec_L2Sqr_ref_T<float> (x, y, d);
}

#ifdef __SSE4_1__

#define USE_SIMD_128

inline __m128 _mm_loadu_ps_T (const float* x) {
    return _mm_loadu_ps (x);
}

inline __m128 _mm_loadu_ps_T (const bfp16_t* x) {
    return _mm_castsi128_ps (_mm_unpacklo_epi16 (
            _mm_setzero_si128 (),
            _mm_loadl_epi64 ((const __m128i*)x)));
}

template <typename T>
float vec_IP_fp_128b_T (const T* x, const T* y, size_t d,
        __m128 msum = _mm_setzero_ps ()) {
    while (d >= 4) {
        __m128 mx = _mm_loadu_ps_T (x);
        x += 4;
        __m128 my = _mm_loadu_ps_T (y);
        y += 4;
        msum = _mm_add_ps (msum, _mm_mul_ps (mx, my));
        d -= 4;
    }
    msum = _mm_hadd_ps (msum, msum);
    msum = _mm_hadd_ps (msum, msum);
    float sum = _mm_cvtss_f32 (msum);
    return d == 0 ? sum : vec_IP_ref_T<float> (x, y, d, sum);
}

inline float vec_IP_128b_T (const float* x, const float* y, size_t d) {
    return vec_IP_fp_128b_T (x, y, d);
}

inline float vec_IP_128b_T (const bfp16_t* x, const bfp16_t* y,
        size_t d) {
    return vec_IP_fp_128b_T (x, y, d);
}

template <typename T>
float vec_L2Sqr_fp_128b_T (const T* x, const T* y, size_t d,
        __m128 msum = _mm_setzero_ps ()) {
    while (d >= 4) {
        __m128 mx = _mm_loadu_ps_T (x);
        x += 4;
        __m128 my = _mm_loadu_ps_T (y);
        y += 4;
        __m128 mdiff = _mm_sub_ps (mx, my);
        msum = _mm_add_ps (msum, _mm_mul_ps (mdiff, mdiff));
        d -= 4;
    }
    msum = _mm_hadd_ps (msum, msum);
    msum = _mm_hadd_ps (msum, msum);
    float sum = _mm_cvtss_f32 (msum);
    return d == 0 ? sum : vec_L2Sqr_ref_T<float> (x, y, d, sum);
}

inline float vec_L2Sqr_128b_T (const float* x, const float* y, size_t d) {
    return vec_L2Sqr_fp_128b_T (x, y, d);
}

inline float vec_L2Sqr_128b_T (const bfp16_t* x, const bfp16_t* y,
        size_t d) {
    return vec_L2Sqr_fp_128b_T (x, y, d);
}

#endif

#ifdef __AVX2__

#ifndef USE_SIMD_128
#error "SIMD 256 must have SIMD 128 enabled"
#endif

#define USE_SIMD_256

inline __m256 _mm256_loadu_ps_T (const float* x) {
    return _mm256_loadu_ps (x);
}

inline __m256 _mm256_loadu_ps_T (const bfp16_t* x) {
    return
        _mm256_castsi256_ps (
            _mm256_unpacklo_epi16 (
                _mm256_setzero_si256 (),
                _mm256_insertf128_si256 (
                    _mm256_castsi128_si256 (
                        _mm_loadl_epi64 ((const __m128i*)x)),
                    _mm_loadl_epi64 ((const __m128i*)(x + 4)),
                    1)));
}

template <typename T>
float vec_IP_fp_256b_T (const T* x, const T* y, size_t d,
        __m256 msum = _mm256_setzero_ps ()) {
    while (d >= 8) {
        __m256 mx = _mm256_loadu_ps_T (x);
        x += 8;
        __m256 my = _mm256_loadu_ps_T (y);
        y += 8;
        msum = _mm256_add_ps (msum, _mm256_mul_ps (mx, my));
        d -= 8;
    }
    __m128 msum2 = _mm256_extractf128_ps (msum, 1);
    msum2 = _mm_add_ps (msum2, _mm256_extractf128_ps (msum, 0));
    return vec_IP_fp_128b_T (x, y, d, msum2);
}

inline float vec_IP_256b_T (const float* x, const float* y, size_t d) {
    return vec_IP_fp_256b_T (x, y, d);
}

inline float vec_IP_256b_T (const bfp16_t* x, const bfp16_t* y,
        size_t d) {
    return vec_IP_fp_256b_T (x, y, d);
}

template <typename T>
float vec_L2Sqr_fp_256b_T (const T* x, const T* y, size_t d,
        __m256 msum = _mm256_setzero_ps ()) {
    while (d >= 8) {
        __m256 mx = _mm256_loadu_ps_T (x);
        x += 8;
        __m256 my = _mm256_loadu_ps_T (y);
        y += 8;
        __m256 mdiff = _mm256_sub_ps (mx, my);
        msum = _mm256_add_ps (msum, _mm256_mul_ps (mdiff, mdiff));
        d -= 8;
    }
    __m128 msum2 = _mm256_extractf128_ps (msum, 1);
    msum2 = _mm_add_ps (msum2, _mm256_extractf128_ps (msum, 0));
    return vec_L2Sqr_fp_128b_T (x, y, d, msum2);
}

inline float vec_L2Sqr_256b_T (const float* x, const float* y, size_t d) {
    return vec_L2Sqr_fp_256b_T (x, y, d);
}

inline float vec_L2Sqr_256b_T (const bfp16_t* x, const bfp16_t* y,
        size_t d) {
    return vec_L2Sqr_fp_256b_T (x, y, d);
}

#endif

#if defined (__AVX512F__) && defined(__AVX512DQ__) && defined(__AVX512BW__) \
        && defined(__AVX512VL__)

#ifndef USE_SIMD_256
#error "SIMD 512 must have SIMD 256 enabled"
#endif
#define USE_SIMD_512

inline __m512 _mm512_loadu_ps_T (const float* x) {
    return _mm512_loadu_ps (x);
}

inline __m512 _mm512_loadu_ps_T (const bfp16_t* x) {
    return
    _mm512_castsi512_ps (
        _mm512_unpacklo_epi16 (
            _mm512_setzero_si512 (),
            _mm512_inserti64x4 (
                _mm512_castsi256_si512 (
                    _mm256_inserti32x4 (
                        _mm256_castsi128_si256 (
                            _mm_loadl_epi64 ((const __m128i*)x)),
                        _mm_loadl_epi64 ((const __m128i*)(x + 4)),
                        1)),
                _mm256_inserti32x4 (
                    _mm256_castsi128_si256 (
                        _mm_loadl_epi64 ((const __m128i*)(x + 8))),
                    _mm_loadl_epi64 ((const __m128i*)(x + 12)),
                    1),
                1)));
}

template <typename T>
float vec_IP_fp_512b_T (const T* x, const T* y, size_t d,
        __m512 msum = _mm512_setzero_ps ()) {
    while (d >= 16) {
        __m512 mx = _mm512_loadu_ps_T (x);
        x += 16;
        __m512 my = _mm512_loadu_ps_T (y);
        y += 16;
        msum = _mm512_add_ps (msum, _mm512_mul_ps (mx, my));
        d -= 16;
    }
    __m256 msum2 = _mm512_extractf32x8_ps (msum, 1);
    msum2 = _mm256_add_ps (msum2, _mm512_extractf32x8_ps (msum, 0));
    return vec_IP_fp_256b_T (x, y, d, msum2);
}

inline float vec_IP_512b_T (const float* x, const float* y, size_t d) {
    return vec_IP_fp_512b_T (x, y, d);
}

inline float vec_IP_512b_T (const bfp16_t* x, const bfp16_t* y,
        size_t d) {
    return vec_IP_fp_512b_T (x, y, d);
}

template <typename T>
float vec_L2Sqr_fp_512b_T (const T* x, const T* y, size_t d,
        __m512 msum = _mm512_setzero_ps ()) {
    while (d >= 16) {
        __m512 mx = _mm512_loadu_ps_T (x);
        x += 16;
        __m512 my = _mm512_loadu_ps_T (y);
        y += 16;
        __m512 mdiff = _mm512_sub_ps (mx, my);
        msum = _mm512_add_ps (msum, _mm512_mul_ps (mdiff, mdiff));
        d -= 16;
    }
    __m256 msum2 = _mm512_extractf32x8_ps (msum, 1);
    msum2 = _mm256_add_ps (msum2, _mm512_extractf32x8_ps (msum, 0));
    return vec_L2Sqr_fp_256b_T (x, y, d, msum2);
}

inline float vec_L2Sqr_512b_T (const float* x, const float* y, size_t d) {
    return vec_L2Sqr_fp_512b_T (x, y, d);
}

inline float vec_L2Sqr_512b_T (const bfp16_t* x, const bfp16_t* y,
        size_t d) {
    return vec_L2Sqr_fp_512b_T (x, y, d);
}

#endif

#if defined (USE_SIMD_512)

template <typename T>
inline float vec_IP_T (const T* x, const T* y, size_t d) {
    return vec_IP_512b_T (x, y, d);
}

template <typename T>
inline float vec_L2Sqr_T (const T* x, const T* y, size_t d) {
    return vec_L2Sqr_512b_T (x, y, d);
}

#elif defined (USE_SIMD_256)

template <typename T>
inline float vec_IP_T (const T* x, const T* y, size_t d) {
    return vec_IP_256b_T (x, y, d);
}

template <typename T>
inline float vec_L2Sqr_T (const T* x, const T* y, size_t d) {
    return vec_L2Sqr_256b_T (x, y, d);
}

#elif defined (USE_SIMD_128)

template <typename T>
inline float vec_IP_T (const T* x, const T* y, size_t d) {
    return vec_IP_128b_T (x, y, d);
}

template <typename T>
inline float vec_L2Sqr_T (const T* x, const T* y, size_t d) {
    return vec_L2Sqr_128b_T (x, y, d);
}

#else

template <typename T>
inline float vec_IP_T (const T* x, const T* y, size_t d) {
    return vec_IP_ref_T (x, y, d);
}

template <typename T>
inline float vec_L2Sqr_T (const T* x, const T* y, size_t d) {
    return vec_L2Sqr_ref_T (x, y, d);
}

#endif

}

#endif

#ifdef OPT_FLAT_DTYPE

#define FLAT_BATCH_THRESHOLD    4

namespace faiss {

//=================================KNN Routine================================

template <typename T, typename D>
void knn_less_better_alone_T (const T* x, const T* y, size_t d,
        size_t nx, size_t ny, float_maxheap_array_t* res, D& distance) {
    size_t k = res->k;
    size_t check_period = InterruptCallback::get_period_hint (ny * d);
    check_period *= omp_get_max_threads ();
    for (size_t i0 = 0; i0 < nx; i0 += check_period) {
        size_t i1 = std::min (i0 + check_period, nx);
#pragma omp parallel for
        for (size_t i = i0; i < i1; i++) {
            const T* x_i = x + i * d;
            const T* y_j = y;
            float* simi = res->get_val (i);
            int64_t* idxi = res->get_ids (i);
            maxheap_heapify (k, simi, idxi);
            for (size_t j = 0; j < ny; j++) {
                float dis = distance (i, j, x_i, y_j, d);
                if (dis < simi[0]) {
                    maxheap_pop (k, simi, idxi);
                    maxheap_push (k, simi, idxi, dis, j);
                }
                y_j += d;
            }
            maxheap_reorder (k, simi, idxi);
        }
        InterruptCallback::check ();
    }
}

template <typename T, typename D>
void knn_greater_better_alone_T (const T* x, const T* y, size_t d,
        size_t nx, size_t ny, float_minheap_array_t* res, D& distance) {
    size_t k = res->k;
    size_t check_period = InterruptCallback::get_period_hint (ny * d);
    check_period *= omp_get_max_threads ();
    for (size_t i0 = 0; i0 < nx; i0 += check_period) {
        size_t i1 = std::min (i0 + check_period, nx);
#pragma omp parallel for
        for (size_t i = i0; i < i1; i++) {
            const T* x_i = x + i * d;
            const T* y_j = y;
            float* simi = res->get_val (i);
            int64_t* idxi = res->get_ids (i);
            minheap_heapify (k, simi, idxi);
            for (size_t j = 0; j < ny; j++) {
                float dis = distance (i, j, x_i, y_j, d);
                if (dis > simi[0]) {
                    minheap_pop (k, simi, idxi);
                    minheap_push (k, simi, idxi, dis, j);
                }
                y_j += d;
            }
            minheap_reorder (k, simi, idxi);
        }
        InterruptCallback::check ();
    }
}

template <typename T>
inline void knn_inner_product_alone_T (const T* x, const T* y, size_t d,
        size_t nx, size_t ny, float_minheap_array_t* res) {

    struct IP {

        inline float operator () (size_t /*ix*/, size_t /*jy*/,
                const T* xi, const T* yj, size_t d) const {
            return vec_IP_T (xi, yj, d);
        }

    }
    distance;
    knn_greater_better_alone_T (x, y, d, nx, ny, res, distance);
}

template <typename T>
inline void knn_L2Sqr_alone_T (const T* x, const T* y, size_t d,
        size_t nx, size_t ny, float_maxheap_array_t* res) {

    struct L2Sqr {

        inline float operator () (size_t /*ix*/, size_t /*jy*/,
                const T* xi, const T* yj, size_t d) const {
            return vec_L2Sqr_T (xi, yj, d);
        }

    }
    distance;
    knn_less_better_alone_T (x, y, d, nx, ny, res, distance);
}

template <typename T>
inline void knn_L2Sqr_expand_alone_T (const T* x, const T* y, size_t d,
        size_t nx, size_t ny, float_maxheap_array_t* res,
        const float* y_norm) {

    struct L2SqrExpand {

        const float* y_norm_sqr;

        inline float operator () (size_t /*ix*/, size_t jy,
                const T* xi, const T* yj, size_t d) const {
            return y_norm_sqr[jy] - 2 * vec_IP_T (xi, yj, d);
        }

    }
    distance = {
        .y_norm_sqr = y_norm,
    };
    knn_less_better_alone_T (x, y, d, nx, ny, res, distance);
}

template <typename T>
inline void knn_inner_product_batch_T (const T* x, const T* y, size_t d,
        size_t nx, size_t ny, float_minheap_array_t* res) {
    knn_inner_product_alone_T (x, y, d, nx, ny, res);
}

template <typename T>
inline void knn_L2Sqr_batch_T (const T* x, const T* y, size_t d,
        size_t nx, size_t ny, float_maxheap_array_t* res) {
    knn_L2Sqr_alone_T (x, y, d, nx, ny, res);
}

template <typename T>
inline void knn_L2Sqr_expand_batch_T (const T* x, const T* y, size_t d,
        size_t nx, size_t ny, float_maxheap_array_t* res,
        const float* y_norm) {
    knn_L2Sqr_expand_alone_T (x, y, d, nx, ny, res, y_norm);
}

template <typename H, typename D>
void knn_batch_T (const float* x, const float* y,
        size_t d, size_t nx, size_t ny, H* heap, D& distance) {
    heap->heapify ();
    if (nx == 0 || ny == 0) {
        return;
    }
    float* distances = new float [nx * ny];
    distance (x, y, d, nx, ny, distances);
    heap->addn (ny, distances, 0, 0, nx);
    delete[] distances;
    InterruptCallback::check ();
    heap->reorder ();
}

inline void knn_inner_product_batch_T (const float* x, const float* y,
        size_t d, size_t nx, size_t ny, float_minheap_array_t* res) {

    struct IP {

        inline void operator () (const float* x, const float* y,
                size_t d, size_t nx, size_t ny, float* distances) const {
            cblas_sgemm (CblasRowMajor, CblasNoTrans, CblasTrans, nx, ny, d,
                    1.0f, x, d, y, d, 0.0f, distances, ny);
        }

    }
    distance;
    knn_batch_T (x, y, d, nx, ny, res, distance);
}

inline void knn_L2Sqr_expand_batch_T (const float* x, const float* y,
        size_t d, size_t nx, size_t ny, float_maxheap_array_t* res,
        const float* y_norm) {

    struct L2SqrExpand {

        const float* y_norm;

        inline void operator () (const float* x, const float* y,
                size_t d, size_t nx, size_t ny, float* distances) const {
            float* distances_i = distances;
            size_t step = ny * sizeof(float);
            for (size_t i = 0; i < nx; i++) {
                memcpy (distances_i, y_norm, step);
                distances_i += ny;
            }
            cblas_sgemm (CblasRowMajor, CblasNoTrans, CblasTrans, nx, ny, d,
                    -2.0f, x, d, y, d, 1.0f, distances, ny);
            }
    }
    distance = {
        .y_norm = y_norm,
    };
    knn_batch_T (x, y, d, nx, ny, res, distance);
}

template <typename T>
inline void knn_inner_product_T (const T* x, const T* y, size_t d,
        size_t nx, size_t ny, float_minheap_array_t* res) {
    if (nx < FLAT_BATCH_THRESHOLD) {
        knn_inner_product_alone_T (x, y, d, nx, ny, res);
    }
    else {
        knn_inner_product_batch_T (x, y, d, nx, ny, res);
    }
}

template <typename T>
inline void knn_L2Sqr_T (const T* x, const T* y, size_t d,
        size_t nx, size_t ny, float_maxheap_array_t* res) {
    if (nx < FLAT_BATCH_THRESHOLD) {
        knn_L2Sqr_alone_T (x, y, d, nx, ny, res);
    }
    else {
        knn_L2Sqr_batch_T (x, y, d, nx, ny, res);
    }
}

template <typename T>
inline void knn_L2Sqr_expand_T (const T* x, const T* y, size_t d,
        size_t nx, size_t ny, float_maxheap_array_t* res,
        const float* y_norm) {
    if (nx < FLAT_BATCH_THRESHOLD) {
        knn_L2Sqr_expand_alone_T (x, y, d, nx, ny, res, y_norm);
    }
    else {
        knn_L2Sqr_expand_batch_T (x, y, d, nx, ny, res, y_norm);
    }
}

}

#endif

#ifdef OPT_IVFFLAT_DTYPE

#define SCANNER_USE_BATCH       false

namespace faiss {

//===========================Inverted List Scanner============================

template <typename T>
class InvertedListScanner_T : public InvertedListScanner {

    using idx_t = InvertedListScanner::idx_t;

protected:
    size_t d;
    size_t code_size;
    bool store_pairs;
    const T* converted_x;
    idx_t list_no;

public:
    InvertedListScanner_T (size_t d, bool store_pairs):
            d (d), code_size (sizeof(T) * d), store_pairs (store_pairs),
            converted_x(nullptr), list_no(-1) {
    }

    virtual ~InvertedListScanner_T () {
        if (converted_x) {
            del_converted_x_T (d, converted_x);
        }
    }

    virtual void set_query (const float* query) override {
        if (converted_x) {
            del_converted_x_T (d, converted_x);
        }
        converted_x = convert_x_T<T> (d, query);
    }

    virtual void set_list (idx_t lidx, float) override {
        list_no = lidx;
    }

    virtual float distance_to_code (const uint8_t*) const override {
        FAISS_THROW_MSG ("not implemented");
    }

    virtual size_t scan_codes (size_t list_size, const uint8_t* codes,
            const idx_t* ids, float* simi, idx_t* idxi, size_t k)
            const = 0;

};

template <typename T, typename C, typename D>
class AloneInvertedListScanner_T : public InvertedListScanner_T<T> {

    using idx_t = InvertedListScanner::idx_t;
    using Scanner = InvertedListScanner_T<T>;

private:
    D* distance;

public:
    AloneInvertedListScanner_T (size_t d, bool store_pairs, D* distance):
            Scanner (d, store_pairs), distance (distance) {
    }

    virtual ~AloneInvertedListScanner_T () {
        delete distance;
    }

    virtual size_t scan_codes (size_t list_size, const uint8_t* codes,
            const idx_t* ids, float* simi, idx_t* idxi,
            size_t k) const override {
        size_t nup = 0;
        for (size_t i = 0; i < list_size; i++) {
            float dis = (*distance) (Scanner::list_no, i,Scanner::converted_x,
                    (const T*)codes, Scanner::d);
            codes += Scanner::code_size;
            if (C::cmp (simi[0], dis)) {
                heap_pop<C> (k, simi, idxi);
                int64_t id = Scanner::store_pairs ?
                        lo_build (Scanner::list_no, i) :
                        ids[i];
                heap_push<C> (k, simi, idxi, dis, id);
                nup++;
            }
        }
        return nup;
    }

};

template <typename T>
InvertedListScanner* get_IP_alone_scanner_T (size_t d, bool store_pairs) {

    struct IP {

        inline float operator () (size_t /*ilist*/, size_t /*jy*/,
                const T* x, const T* yj, size_t d) const {
            return vec_IP_T (x, yj, d);
        }

    }
    *distance = new IP;
    return new AloneInvertedListScanner_T<T, CMin<float, int64_t>, IP> (d,
            store_pairs, distance);
}

template <typename T>
InvertedListScanner* get_L2Sqr_alone_scanner_T (size_t d, bool store_pairs) {

    struct L2Sqr {

        inline float operator () (size_t /*ilist*/, size_t /*jy*/,
                const T* x, const T* yj, size_t d) const {
            return vec_L2Sqr_T (x, yj, d);
        }

    }
    *distance = new L2Sqr;
    return new AloneInvertedListScanner_T<T, CMax<float, int64_t>, L2Sqr> (d,
            store_pairs, distance);
}

template <typename T, typename TNorm>
InvertedListScanner* get_L2Sqr_expand_alone_scanner_T (size_t d,
        bool store_pairs, const TNorm y_norm) {

    struct L2SqrExpand {

        const TNorm y_norm;

        inline float operator () (size_t ilist, size_t jy,
                const T* x, const T* yj, size_t d) {
            return y_norm [ilist] [jy] - 2 * vec_IP_T (x, yj, d);
        }

    }
    *distance = new L2SqrExpand {
        .y_norm = y_norm,
    };
    return new AloneInvertedListScanner_T<T, CMax<float, int64_t>,
            L2SqrExpand> (d, store_pairs, distance);
}

template <typename T, typename C, typename D>
class BatchInvertedListScanner_T : public InvertedListScanner_T<T> {

    using idx_t = InvertedListScanner::idx_t;
    using Scanner = InvertedListScanner_T<T>;

private:
    D* distance;

public:
    BatchInvertedListScanner_T (size_t d, bool store_pairs, D* distance):
            Scanner (d, store_pairs), distance (distance) {
    }

    virtual ~BatchInvertedListScanner_T () {
        delete distance;
    }

    virtual size_t scan_codes (size_t list_size, const uint8_t* codes,
            const idx_t* ids, float* simi, idx_t* idxi,
            size_t k) const override {
        float* distances = new float [list_size];
        (*distance) (Scanner::converted_x, Scanner::list_no, list_size,
                (const T*)codes, Scanner::d, distances);
        size_t nup = 0;
        for (size_t i = 0; i < list_size; i++) {
            float dis = distances [i];
            if (C::cmp (simi[0], dis)) {
                heap_pop<C> (k, simi, idxi);
                int64_t id = Scanner::store_pairs ?
                        lo_build (Scanner::list_no, i) : ids[i];
                heap_push<C> (k, simi, idxi, dis, id);
                nup++;
            }
        }
        delete[] distances;
        return nup;
    }

};

template <typename T>
inline InvertedListScanner* get_IP_batch_scanner_T (size_t d, bool store_pairs,
        T*) {
    return get_IP_alone_scanner_T<T> (d, store_pairs);
}

inline InvertedListScanner* get_IP_batch_scanner_T (size_t d, bool store_pairs,
        float*) {

    struct IP {

        inline void operator () (const float* x, size_t /*ilist*/,
                size_t list_size, const float* y, size_t d, float* distances)
                const {
            cblas_sgemv (CblasRowMajor, CblasNoTrans, list_size, d, 1.0f,
                    y, d, x, 1, 0.0f, distances, 1);
        }

    }
    *distance = new IP;
    return new BatchInvertedListScanner_T<float, CMin<float, int64_t>, IP> (d,
            store_pairs, distance);
}

template <typename T>
inline InvertedListScanner* get_L2Sqr_batch_scanner_T (size_t d,
        bool store_pairs, T*) {
    return get_L2Sqr_alone_scanner_T<T> (d, store_pairs);
}

template <typename T, typename TNorm>
inline InvertedListScanner* get_L2Sqr_expand_batch_scanner_T (size_t d,
        bool store_pairs, const TNorm y_norm, T*) {
    return get_L2Sqr_expand_alone_scanner_T<T> (d, store_pairs,
            y_norm);
}

template <typename TNorm>
inline InvertedListScanner* get_L2Sqr_expand_batch_scanner_T (size_t d,
        bool store_pairs, const TNorm y_norm, float*) {

    struct L2SqrExpand {

        const TNorm y_norm;

        inline void operator () (const float* x, size_t ilist,
                size_t list_size, const float* y, size_t d, float* distances)
                const {
            memcpy (distances, &(y_norm [ilist] [0]),
                    list_size * sizeof(float));
            cblas_sgemv (CblasRowMajor, CblasNoTrans, list_size, d, -2.0f,
                    y, d, x, 1, 1.0f, distances, 1);
        }

    }
    *distance = new L2SqrExpand {
        .y_norm = y_norm,
    };
    return new BatchInvertedListScanner_T<float, CMax<float, int64_t>,
            L2SqrExpand> (d, store_pairs, distance);
}

template <typename T>
inline InvertedListScanner* get_IP_scanner_T (size_t d, bool store_pairs) {
    if (!SCANNER_USE_BATCH) {
        return get_IP_alone_scanner_T<T> (d, store_pairs);
    }
    else {
        return get_IP_batch_scanner_T (d, store_pairs, (T*)nullptr);
    }
}

template <typename T>
inline InvertedListScanner* get_L2Sqr_scanner_T (size_t d, bool store_pairs) {
    if (!SCANNER_USE_BATCH) {
        return get_L2Sqr_alone_scanner_T<T> (d, store_pairs);
    }
    else {
        return get_L2Sqr_batch_scanner_T (d, store_pairs, (T*)nullptr);
    }
}

template <typename T, typename TNorm>
inline InvertedListScanner* get_L2Sqr_expand_scanner_T (size_t d,
        bool store_pairs, const TNorm y_norm) {
    if (!SCANNER_USE_BATCH) {
        return get_L2Sqr_expand_alone_scanner_T<T> (d, store_pairs, y_norm);
    }
    else {
        return get_L2Sqr_expand_batch_scanner_T (d, store_pairs, y_norm,
                (T*)nullptr);
    }
}

}

#endif


// Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
// reserved. See files LICENSE and NOTICE for details.
//
// This file is part of CEED, a collection of benchmarks, miniapps, software
// libraries and APIs for efficient high-order finite element and spectral
// element discretizations for exascale applications. For more information and
// source code availability see http://github.com/ceed.
//
// The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
// a collaborative effort of two U.S. Department of Energy organizations (Office
// of Science and the National Nuclear Security Administration) responsible for
// the planning and preparation of a capable exascale ecosystem, including
// software, applications, hardware, advanced system engineering and early
// testbed platforms, in support of the nation's exascale computing imperative.

#ifndef setup_h
#define setup_h

#include <ceed.h>
#include <petsc.h>
#include <petscdmplex.h>
#include <petscfe.h>
#include <petscksp.h>
#include <stdbool.h>
#include <string.h>

#if PETSC_VERSION_LT(3,14,0)
#  define DMAddBoundary(a,b,c,d,e,f,g,h,i,j,k,l,m,n) DMAddBoundary(a,b,c,e,h,i,j,k,f,g,m)
#elif PETSC_VERSION_LT(3,16,0)
#  define DMAddBoundary(a,b,c,d,e,f,g,h,i,j,k,l,m,n) DMAddBoundary(a,b,c,e,h,i,j,k,l,f,g,m)
#else
#  define DMAddBoundary(a,b,c,d,e,f,g,h,i,j,k,l,m,n) DMAddBoundary(a,b,c,d,f,g,h,i,j,k,l,m,n)
#endif

#ifndef PHYSICS_STRUCT
#define PHYSICS_STRUCT
typedef struct Physics_private *Physics;
struct Physics_private {
  CeedScalar   nu;      // Poisson's ratio
  CeedScalar   E;       // Young's Modulus
};
#endif

// -----------------------------------------------------------------------------
// Command Line Options
// -----------------------------------------------------------------------------
// Problem options
typedef enum {
  ELAS_LINEAR = 0, ELAS_SS_NH = 1, ELAS_FSInitial_NH1 = 2, ELAS_FSInitial_NH2 = 3,
  ELAS_FSCurrent_NH1 = 4, ELAS_FSCurrent_NH2 = 5
} problemType;
static const char *const problemTypes[] = {"Linear",
                                           "SS-NH",
                                           "FSInitial-NH1",
                                           "FSInitial-NH2",
                                           "FSCurrent-NH1",
                                           "FSCurrent-NH2",
                                           "problemType","ELAS_",0
                                          };
static const char *const problemTypesForDisp[] = {"Linear elasticity",
                                                  "Hyperelasticity small strain, Neo-Hookean",
                                                  "Hyperelasticity finite strain Initial config Neo-Hookean w/ dXref_dxinit, Grad(u) storage",
                                                  "Hyperelasticity finite strain Initial config Neo-Hookean w/ dXref_dxinit, Grad(u), C_inv, constant storage",
                                                  "Hyperelasticity finite strain Current config Neo-Hookean w/ dXref_dxinit, Grad(u) storage",
                                                  "Hyperelasticity finite strain Current config Neo-Hookean w/ dXref_dxcurr, tau, constant storage",
                                                 };

// Forcing function options
typedef enum {
  FORCE_NONE = 0, FORCE_CONST = 1, FORCE_MMS = 2
} forcingType;
static const char *const forcing_types[] = {"none",
                                            "constant",
                                            "mms",
                                            "forcingType","FORCE_",0
                                           };
static const char *const forcing_types_for_disp[] = {"None",
                                                     "Constant",
                                                     "Manufactured solution"
                                                    };

// Multigrid options
typedef enum {
  MULTIGRID_LOGARITHMIC = 0, MULTIGRID_UNIFORM = 1, MULTIGRID_NONE = 2
} multigridType;
static const char *const multigrid_types [] = {"logarithmic",
                                               "uniform",
                                               "none",
                                               "multigridType","MULTIGRID",0
                                              };
static const char *const multigrid_types_for_disp[] = {"P-multigrid, logarithmic coarsening",
                                                       "P-multigrind, uniform coarsening",
                                                       "No multigrid"
                                                      };

typedef PetscErrorCode BCFunc(PetscInt, PetscReal, const PetscReal *, PetscInt,
                              PetscScalar *, void *);
// Note: These variables should be updated if additional boundary conditions
//         are added to boundary.c.
BCFunc BCMMS, BCZero, BCClamp;

// -----------------------------------------------------------------------------
// Structs
// -----------------------------------------------------------------------------
// Units
typedef struct Units_private *Units;
struct Units_private {
  // Fundamental units
  PetscScalar meter;
  PetscScalar kilogram;
  PetscScalar second;
  // Derived unit
  PetscScalar Pascal;
};

// Application context from user command line options
typedef struct AppCtx_private *AppCtx;
struct AppCtx_private {
  char          ceed_resource[PETSC_MAX_PATH_LEN];     // libCEED backend
  char          mesh_file[PETSC_MAX_PATH_LEN];         // exodusII mesh file
  char          output_dir[PETSC_MAX_PATH_LEN];
  PetscBool     test_mode;
  PetscBool     view_soln;
  PetscBool     view_final_soln;
  problemType   problem_choice;
  forcingType   forcing_choice;
  multigridType multigrid_choice;
  PetscScalar   nu_smoother;
  PetscInt      degree;
  PetscInt      q_extra;
  PetscInt      num_levels;
  PetscInt      *level_degrees;
  PetscInt      num_increments;                        // Number of steps
  PetscInt      bc_clamp_count;
  PetscInt      bc_clamp_faces[16];
  // [translation; 3] [rotation axis; 3] [rotation magnitude c_0, c_1]
  // The rotations are (c_0 + c_1 s) \pi, where s = x · axis
  PetscScalar   bc_clamp_max[16][8];
  PetscInt      bc_traction_count;
  PetscInt      bc_traction_faces[16];
  PetscScalar   bc_traction_vector[16][3];
  PetscScalar   forcing_vector[3];
};

// Problem specific data
// *INDENT-OFF*
typedef struct {
  CeedInt           q_data_size;
  CeedQFunctionUser setup_geo, apply, jacob, energy, diagnostic;
  const char        *setup_geo_loc, *apply_loc, *jacob_loc, *energy_loc,
                    *diagnostic_loc;
  CeedQuadMode      quad_mode;
} problemData;
// *INDENT-ON*

// Data specific to each problem option
extern problemData problem_options[6];

// Forcing function data
typedef struct {
  CeedQFunctionUser setup_forcing;
  const char        *setup_forcing_loc;
} forcingData;

extern forcingData forcing_options[3];

// Data for PETSc Matshell
typedef struct UserMult_private *UserMult;
struct UserMult_private {
  MPI_Comm        comm;
  DM              dm;
  Vec             X_loc, Y_loc, neumann_bcs;
  CeedVector      x_ceed, y_ceed;
  CeedOperator    op;
  CeedQFunction   qf;
  Ceed            ceed;
  PetscScalar     load_increment;
  CeedQFunctionContext ctx_phys, ctx_phys_smoother;
};

// Data for Jacobian setup routine
typedef struct FormJacobCtx_private *FormJacobCtx;
struct FormJacobCtx_private {
  UserMult     *jacob_ctx;
  PetscInt     num_levels;
  SNES         snes_coarse;
  Mat          *jacob_mat, jacob_mat_coarse;
  Vec          u_coarse;
};

// Data for PETSc Prolongation/Restriction Matshell
typedef struct UserMultProlongRestr_private *UserMultProlongRestr;
struct UserMultProlongRestr_private {
  MPI_Comm     comm;
  DM           dm_c, dm_f;
  Vec          loc_vec_c, loc_vec_f;
  CeedVector   ceed_vec_c, ceed_vec_f;
  CeedOperator op_prolong, op_restrict;
  Ceed         ceed;
};

// libCEED data struct for level
typedef struct CeedData_private *CeedData;
struct CeedData_private {
  Ceed                ceed;
  CeedBasis           basis_x, basis_u, basis_c_to_f, basis_energy,
                      basis_diagnostic;
  CeedElemRestriction elem_restr_x, elem_restr_u, elem_restr_qd_i,
                      elem_restr_gradu_i,
                      elem_restr_energy, elem_restr_diagnostic,
                      elem_restr_dXdx, elem_restr_tau,
                      elem_restr_C_inv, elem_restr_lam_log_J, elem_restr_qd_diagnostic_i;
  CeedQFunction       qf_apply, qf_jacob, qf_energy, qf_diagnostic;
  CeedOperator        op_apply, op_jacob, op_restrict, op_prolong, op_energy,
                      op_diagnostic;
  CeedVector          q_data, q_data_diagnostic, grad_u, x_ceed,
                      y_ceed, true_soln, dXdx, tau, C_inv, lam_log_J;
};

// Translate PetscMemType to CeedMemType
static inline CeedMemType MemTypeP2C(PetscMemType mem_type) {
  return PetscMemTypeDevice(mem_type) ? CEED_MEM_DEVICE : CEED_MEM_HOST;
}

// -----------------------------------------------------------------------------
// Process command line options
// -----------------------------------------------------------------------------
// Process general command line options
PetscErrorCode ProcessCommandLineOptions(MPI_Comm comm, AppCtx app_ctx);

// Process physics options
PetscErrorCode ProcessPhysics(MPI_Comm comm, Physics phys, Units units);

// -----------------------------------------------------------------------------
// 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);

// -----------------------------------------------------------------------------
// libCEED Functions
// -----------------------------------------------------------------------------
// Destroy libCEED objects
PetscErrorCode CeedDataDestroy(CeedInt level, CeedData data);

// Utility function - essential BC dofs are encoded in closure indices as -(i+1)
PetscInt Involute(PetscInt i);

// Utility function to create local CEED restriction from DMPlex
PetscErrorCode CreateRestrictionFromPlex(Ceed ceed, DM dm, CeedInt P,
    CeedInt height, DMLabel domain_label, CeedInt value,
    CeedElemRestriction *elem_restr);

// Utility function to get Ceed Restriction for each domain
PetscErrorCode GetRestrictionForDomain(Ceed ceed, DM dm, CeedInt height,
                                       DMLabel domain_label, PetscInt value, CeedInt P, CeedInt Q, CeedInt q_data_size,
                                       CeedElemRestriction *elem_restr_q, CeedElemRestriction *elem_restr_x,
                                       CeedElemRestriction *elem_restr_qd_i);

// Set up libCEED for a given degree
PetscErrorCode SetupLibceedFineLevel(DM dm, DM dm_energy, DM dm_diagnostic,
                                     Ceed ceed, AppCtx app_ctx,
                                     CeedQFunctionContext phys_ctx,
                                     CeedData *data, PetscInt fine_level,
                                     PetscInt num_comp_u, PetscInt U_g_size,
                                     PetscInt u_loc_size, CeedVector force_ceed,
                                     CeedVector neumann_ceed);

// Set up libCEED multigrid level for a given degree
PetscErrorCode SetupLibceedLevel(DM dm, Ceed ceed, AppCtx app_ctx,
                                 CeedData *data, PetscInt level,
                                 PetscInt num_comp_u, PetscInt U_g_size,
                                 PetscInt u_loc_size, CeedVector fine_mult);

// Setup context data for Jacobian evaluation
PetscErrorCode SetupJacobianCtx(MPI_Comm comm, AppCtx app_ctx, DM dm, Vec V,
                                Vec V_loc, CeedData ceed_data, Ceed ceed,
                                CeedQFunctionContext ctx_phys,
                                CeedQFunctionContext ctx_phys_smoother,
                                UserMult jacobian_ctx);

// Setup context data for prolongation and restriction operators
PetscErrorCode SetupProlongRestrictCtx(MPI_Comm comm, AppCtx app_ctx, DM dm_c,
                                       DM dm_f, Vec V_f, Vec V_loc_c, Vec V_loc_f,
                                       CeedData ceed_data_c, CeedData ceed_data_f,
                                       Ceed ceed,
                                       UserMultProlongRestr prolong_restr_ctx);

// -----------------------------------------------------------------------------
// Jacobian setup
// -----------------------------------------------------------------------------
PetscErrorCode FormJacobian(SNES snes, Vec U, Mat J, Mat J_pre, void *ctx);

// -----------------------------------------------------------------------------
// Solution output
// -----------------------------------------------------------------------------
PetscErrorCode ViewSolution(MPI_Comm comm, AppCtx app_ctx, Vec U,
                            PetscInt increment,
                            PetscScalar load_increment);

PetscErrorCode ViewDiagnosticQuantities(MPI_Comm comm, DM dm_U,
                                        UserMult user, AppCtx app_ctx, Vec U,
                                        CeedElemRestriction elem_restr_diagnostic);

// -----------------------------------------------------------------------------
// libCEED Operators for MatShell
// -----------------------------------------------------------------------------
// This function uses libCEED to compute the local action of an operator
PetscErrorCode ApplyLocalCeedOp(Vec X, Vec Y, UserMult user);

// This function uses libCEED to compute the non-linear residual
PetscErrorCode FormResidual_Ceed(SNES snes, Vec X, Vec Y, void *ctx);

// This function uses libCEED to apply the Jacobian for assembly via a SNES
PetscErrorCode ApplyJacobianCoarse_Ceed(SNES snes, Vec X, Vec Y, void *ctx);

// This function uses libCEED to compute the action of the Jacobian
PetscErrorCode ApplyJacobian_Ceed(Mat A, Vec X, Vec Y);

// This function uses libCEED to compute the action of the prolongation operator
PetscErrorCode Prolong_Ceed(Mat A, Vec X, Vec Y);

// This function uses libCEED to compute the action of the restriction operator
PetscErrorCode Restrict_Ceed(Mat A, Vec X, Vec Y);

// This function returns the computed diagonal of the operator
PetscErrorCode GetDiag_Ceed(Mat A, Vec D);

// This function calculates the strain energy in the final solution
PetscErrorCode ComputeStrainEnergy(DM dm_energy, UserMult user,
                                   CeedOperator op_energy, Vec X,
                                   PetscReal *energy);

// -----------------------------------------------------------------------------
// Boundary Functions
// -----------------------------------------------------------------------------
// Note: If additional boundary conditions are added, an update is needed in
//         elasticity.h for the boundaryOptions variable.

// BCMMS - boundary function
// Values on all points of the mesh is set based on given solution below
// for u[0], u[1], u[2]
PetscErrorCode BCMMS(PetscInt dim, PetscReal load_increment,
                     const PetscReal coords[], PetscInt num_comp_u,
                     PetscScalar *u, void *ctx);

// BCClamp - fix boundary values with affine transformation at fraction of load
//   increment
PetscErrorCode BCClamp(PetscInt dim, PetscReal load_increment,
                       const PetscReal coords[], PetscInt num_comp_u,
                       PetscScalar *u, void *ctx);

#endif //setup_h


/*
 * BRAINS
 * (B)LR (R)everberation-mapping (A)nalysis (I)n AGNs with (N)ested (S)ampling
 * Yan-Rong Li, liyanrong@ihep.ac.cn
 * Thu, Aug 4, 2016
 */

/*!
 *  \file reconstruct_con.c
 *  \brief reconstruct continuum.
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gsl/gsl_cblas.h>
#include <gsl/gsl_interp.h>
#include <gsl/gsl_randist.h>

#include "brains.h"

void *best_model_con;   /*!< best model */
void *best_model_std_con;  /*!< standard deviation of the best model */

/*!
 *  this function does postprocess for continuum.
 */
void postprocess_con()
{
  char posterior_sample_file[BRAINS_MAX_STR_LENGTH];
  double *pm, *pmstd;
  int num_ps, i, j;
  void *posterior_sample, *post_model;
  int size_of_modeltype = num_params * sizeof(double);

  best_model_con = malloc(size_of_modeltype);
  best_model_std_con = malloc(size_of_modeltype);

  if(thistask == roottask)
  {
    char fname[200];
    FILE *fp, *fcon;

    /* get file name of posterior sample file */
    //get_posterior_sample_file(dnest_options_file, posterior_sample_file);
    dnest_get_posterior_sample_file(posterior_sample_file);

    /* open file for posterior sample */
    fp = fopen(posterior_sample_file, "r");
    if(fp == NULL)
    {
      fprintf(stderr, "# Error: Cannot open file %s.\n", posterior_sample_file);
      exit(0);
    }

    /* open file for continuum reconstruction */
    sprintf(fname, "%s/%s", parset.file_dir, "data/con_rec.txt");
    fcon = fopen(fname, "w");
    if(fcon == NULL)
    {
      fprintf(stderr, "# Error: Cannot open file %s.\n", fname);
      exit(0);
    }

    /* read number of points in posterior sample */
    if(fscanf(fp, "# %d", &num_ps) < 1)
    {
      fprintf(stderr, "# Error: Cannot read file %s.\n", posterior_sample_file);
      exit(0);
    }
    printf("# Number of points in posterior sample: %d\n", num_ps);

    post_model = malloc(size_of_modeltype);
    posterior_sample = malloc(num_ps * size_of_modeltype);

    which_parameter_update = -1;
    which_particle_update = 0;

    for(i=0; i<num_ps; i++)
    {
      for(j=0; j<num_params; j++)
      {
        if(fscanf(fp, "%lf", (double *)post_model + j) < 1)
        {
          fprintf(stderr, "# Error: Cannot read file %s.\n", posterior_sample_file);
          exit(0);
        }
      }
      fscanf(fp, "\n");

      memcpy(posterior_sample+i*size_of_modeltype, post_model, size_of_modeltype);

      /* not output reconstruction when flag_dim = -1 */
      if(parset.flag_dim != -1)
      {
        calculate_con_from_model_semiseparable(post_model);

        if(gsl_rng_uniform(gsl_r) < 1.0)
        {
          for(j=0; j<parset.n_con_recon; j++)
          {
            fprintf(fcon, "%e %e %f\n", Tcon[j]*(1.0+parset.redshift), Fcon[j]/con_scale, Fcerrs[j]/con_scale);
          }
          fprintf(fcon, "\n");
        }
      }
    }

    fclose(fp);
    fclose(fcon);

    /* calcaulte mean and standard deviation of posterior samples. */
    pm = (double *)best_model_con;
    pmstd = (double *)best_model_std_con;
    for(j=0; j<num_params; j++)
    {
      pm[j] = pmstd[j] = 0.0;
    }
    for(i=0; i<num_ps; i++)
    {
      for(j =0; j<num_params; j++)
        pm[j] += *((double *)posterior_sample + i*num_params + j );
    }

    for(j=0; j<num_params; j++)
      pm[j] /= num_ps;

    for(i=0; i<num_ps; i++)
    {
      for(j=0; j<num_params; j++)
        pmstd[j] += pow( *((double *)posterior_sample + i*num_params + j ) - pm[j], 2.0 );
    }

    for(j=0; j<num_params; j++)
    {
      if(num_ps > 1)
        pmstd[j] = sqrt(pmstd[j]/(num_ps-1.0));
      else
        pmstd[j] = 0.0;
    }

    for(j = 0; j<num_params_var; j++)
      printf("Best params %d %f +- %f\n", j, *((double *)best_model_con + j),
                                             *((double *)best_model_std_con+j) );

    free(post_model);
    free(posterior_sample);
  }
  return;
}

/*!
 * This function runs dnest sampling and postprocessing, reconstructs the continuum
 *  using the best estimates of model parameters.
 */
void reconstruct_con()
{
  int i, argc=0;
  char **argv;

  // configure restart of dnest
  argv = malloc(9*sizeof(char *));
  for(i=0; i<9; i++)
  {
    argv[i] = malloc(BRAINS_MAX_STR_LENGTH*sizeof(char));
  }
  //setup argc and argv
  strcpy(argv[argc++], "dnest");
  strcpy(argv[argc++], "-s");
  strcpy(argv[argc], parset.file_dir);
  strcat(argv[argc++], "/data/restart_dnest.txt");

  if(parset.flag_restart == 1 && parset.flag_dim == 0)
  {
    strcpy(argv[argc++], "-r");
    strcpy(argv[argc], parset.file_dir);
    strcat(argv[argc], "/");
    strcat(argv[argc++], "data/restart_dnest.txt");
  }
  if(parset.flag_postprc == 1 || (parset.flag_dim !=0 && parset.flag_restart == 1))
  {
    strcpy(argv[argc++], "-p");
  }
  if(parset.flag_temp == 1)
  {
    sprintf(argv[argc++], "-t%f", parset.temperature);
  }
  if(parset.flag_sample_info == 1)
  {
    strcpy(argv[argc++], "-c");
  }

  //level-dependent sampling
  {
    strcpy(argv[argc++], "-l");
  }

  reconstruct_con_init();
  logz_con = dnest_con(argc, argv);

  if(parset.flag_para_name != 1)
  {
    if(parset.flag_exam_prior != 1)
    {
      postprocess_con();

      if(thistask == roottask)
      {
        which_parameter_update = -1;
        which_particle_update = 0;

        calculate_con_from_model_semiseparable(best_model_con);

        FILE *fp;
        char fname[200];
        int i;
        sprintf(fname, "%s/%s", parset.file_dir, parset.pcon_out_file);
        fp = fopen(fname, "w");
        if(fp == NULL)
        {
          fprintf(stderr, "# Error: Cannot open file %s\n", fname);
          exit(-1);
        }

        for(i=0; i<parset.n_con_recon; i++)
        {
          fprintf(fp, "%e %e %e\n", Tcon[i]*(1.0+parset.redshift), Fcon[i] / con_scale, Fcerrs[i]/con_scale);
        }
        fclose(fp);

        memcpy(var_param, best_model_con, num_params_var*sizeof(double));
        memcpy(var_param_std, best_model_std_con, num_params_var*sizeof(double));
      }
    }
    else
    {
      for(i=0; i<num_params_var; i++)
      {
        var_param[i] = 0.5*(par_range_model[i][0] + par_range_model[i][1]);
        var_param_std[i] =0.5*(par_range_model[i][1] - par_range_model[i][0])/2.35;
      }
    }

    //use the posterior mean and standard variance as the prior for the 1d and 2d RM.
    //only for variability parameters.
    MPI_Bcast(var_param, num_params_var, MPI_DOUBLE, roottask, MPI_COMM_WORLD);
    MPI_Bcast(var_param_std, num_params_var, MPI_DOUBLE, roottask, MPI_COMM_WORLD);
  }

  reconstruct_con_end();

  //clear up argv
  for(i=0; i<9; i++)
  {
    free(argv[i]);
  }
  free(argv);

  return;
}

/*!
 *  this function calculates continuum light curves from model parameters
 *  and stores it into Fcon.
 */
void calculate_con_from_model(const void *model)
{
  double *ybuf, *y, *yu, *Cq, *yq, *yuq;
  double syserr;

  double *pm = (double *)model;
  double sigma, tau, alpha;
  int i, info;

  syserr = (exp(pm[0]) - 1.0) * con_error_mean;  // systematic error
  tau = exp(pm[2]);
  sigma = exp(pm[1]) * sqrt(tau);
  alpha = 1.0;

  ybuf = workspace;
  y = ybuf + n_con_data*nq;
  Cq = y + n_con_data;
  yq = Cq + nq*nq;
  yu = yq + nq;
  yuq = yu + parset.n_con_recon;

  set_covar_Pmat_data(sigma, tau, alpha, syserr);
  set_covar_Umat(sigma, tau, alpha);

  inverse_pomat(PCmat_data, n_con_data, &info);

  // Cq^-1 = L^TxC^-1xL
  multiply_mat_MN(PCmat_data, Larr_data, ybuf, n_con_data, nq, n_con_data);
  multiply_mat_MN_transposeA(Larr_data, ybuf, Cq, nq, nq, n_con_data);

  // L^TxC^-1xy
  multiply_matvec(PCmat_data, Fcon_data, n_con_data, ybuf);
  multiply_mat_MN_transposeA(Larr_data, ybuf, yq, nq, 1, n_con_data);

  // (hat q) = Cqx(L^TxC^-1xy)
  inverse_pomat(Cq, nq, &info);
  multiply_mat_MN(Cq, yq, ybuf, nq, 1, nq);

  // q = uq + (hat q)
  Chol_decomp_L(Cq, nq, &info);
  multiply_matvec(Cq, &pm[3], nq, yq);
  for(i=0; i<nq; i++)
    yq[i] += ybuf[i];

  // y = yc - Lxq
  multiply_matvec_MN(Larr_data, n_con_data, nq, yq, ybuf);
  for(i=0; i<n_con_data; i++)
  {
    y[i] = Fcon_data[i] - ybuf[i];
  }

  // (hat s) = SxC^-1xy
  multiply_matvec(PCmat_data, y, n_con_data, ybuf);
  multiply_matvec_MN(USmat, parset.n_con_recon, n_con_data, ybuf, Fcon);

  // SxC^-1xS^T
  multiply_mat_MN(USmat, PCmat_data, PEmat1, parset.n_con_recon, n_con_data, n_con_data);
  multiply_mat_MN_transposeB(PEmat1, USmat, PEmat2, parset.n_con_recon, parset.n_con_recon, n_con_data);

  // set DRW covariance at reconstructed points
  set_covar_Pmat(sigma, tau, alpha);

  // S - SxC^-1xS^T; covariance of reconstructed points
  for(i=0; i<parset.n_con_recon*parset.n_con_recon; i++)
    PQmat[i] = PSmat[i] - PEmat2[i];

  // assign errors
  for(i=0; i<parset.n_con_recon; i++)
  {
    Fcerrs[i] = sqrt(PQmat[i*parset.n_con_recon + i]);
  }

  Chol_decomp_L(PQmat, parset.n_con_recon, &info);
  multiply_matvec(PQmat, &pm[num_params_var], parset.n_con_recon, yu);

  // add back long-term trend of continuum
  multiply_matvec_MN(Larr_rec, parset.n_con_recon, nq, yq, yuq);

  for(i=0; i<parset.n_con_recon; i++)
  {
    Fcon[i] += yu[i] + yuq[i];
  }

  return;
}

/*!
 *  this function calculates continuum light curves from model parameters
 *  and stores it into Fcon.
 *
 *  using fast algorithm for semiseparable matrices
 */
void calculate_con_from_model_semiseparable(const void *model)
{
  double *Lbuf, *ybuf, *y, *Cq, *yq, *W, *D, *phi, *u, *v;
  double syserr;

  double *pm = (double *)model;
  double sigma, sigma2, tau, alpha;
  int i, info;

  syserr = (exp(pm[0]) - 1.0) * con_error_mean;  // systematic error
  tau = exp(pm[2]);
  sigma = exp(pm[1]) * sqrt(tau);
  sigma2 = sigma*sigma;
  alpha = 1.0;

  Lbuf = workspace;
  ybuf = Lbuf + n_con_data*nq;
  y = ybuf + n_con_max;
  Cq = y + n_con_max;
  yq = Cq + nq*nq;
  W = yq + nq;
  D = W + n_con_max;
  phi = D + n_con_max;
  u = phi + n_con_max;
  v = u + parset.n_con_recon;

  set_covar_Umat(sigma, tau, alpha);

  compute_semiseparable_drw(Tcon_data, n_con_data, sigma2, 1.0/tau, Fcerrs_data, syserr, W, D, phi);
  // Cq^-1 = L^TxC^-1xL
  multiply_mat_semiseparable_drw(Larr_data, W, D, phi, n_con_data, nq, sigma2, Lbuf);
  multiply_mat_MN_transposeA(Larr_data, Lbuf, Cq, nq, nq, n_con_data);

  // L^TxC^-1xy
  multiply_matvec_semiseparable_drw(Fcon_data, W, D, phi, n_con_data, sigma2, ybuf);
  multiply_mat_MN_transposeA(Larr_data, ybuf, yq, nq, 1, n_con_data);

  // (hat q) = Cqx(L^TxC^-1xy)
  inverse_pomat(Cq, nq, &info);
  multiply_mat_MN(Cq, yq, ybuf, nq, 1, nq);

  // q = uq + (hat q)
  Chol_decomp_L(Cq, nq, &info);
  multiply_matvec(Cq, &pm[3], nq, yq);
  for(i=0; i<nq; i++)
    yq[i] += ybuf[i];

  // y = yc - Lxq
  multiply_matvec_MN(Larr_data, n_con_data, nq, yq, ybuf);
  for(i=0; i<n_con_data; i++)
  {
    y[i] = Fcon_data[i] - ybuf[i];
  }

  // (hat s) = SxC^-1xy
  multiply_matvec_semiseparable_drw(y, W, D, phi, n_con_data, sigma2, ybuf);
  multiply_matvec_MN(USmat, parset.n_con_recon, n_con_data, ybuf, Fcon);

  // SxC^-1xS^T
  multiply_mat_transposeB_semiseparable_drw(USmat, W, D, phi, n_con_data, parset.n_con_recon, sigma2, PEmat1);
  multiply_mat_MN(USmat, PEmat1, PEmat2, parset.n_con_recon, parset.n_con_recon, n_con_data);

  // set DRW covariance at reconstructed points
  set_covar_Pmat(sigma, tau, alpha);

  // S - SxC^-1xS^T; covariance of reconstructed points
  for(i=0; i<parset.n_con_recon*parset.n_con_recon; i++)
    PQmat[i] = PSmat[i] - PEmat2[i];

  // assign errors
  for(i=0; i<parset.n_con_recon; i++)
  {
    Fcerrs[i] = sqrt(PQmat[i*parset.n_con_recon + i]);
  }

  Chol_decomp_L(PQmat, parset.n_con_recon, &info);
  multiply_matvec(PQmat, &pm[num_params_var], parset.n_con_recon, y);

  // add back long-term trend of continuum
  multiply_matvec_MN(Larr_rec, parset.n_con_recon, nq, yq, ybuf);

  for(i=0; i<parset.n_con_recon; i++)
  {
    Fcon[i] += y[i] + ybuf[i];
  }
  return;
}

/*!
 * This function calculate continuum light curves from variability parameters.
 */
void reconstruct_con_from_varmodel(double sigma_hat, double tau, double alpha, double syserr)
{
  double *Lbuf, *ybuf, *y, *yq, *yuq, *Cq, sigma;
  int i, info;
  double *PEmat3, *PEmat4;

  sigma = sigma_hat * sqrt(tau);

  Lbuf = workspace;
  ybuf = Lbuf + n_con_data * nq;
  y = ybuf + n_con_data;
  Cq = y + n_con_data;
  yq = Cq + nq*nq;
  yuq = yq + nq;

  PEmat3 = malloc(parset.n_con_recon * nq * sizeof(double));
  PEmat4 = malloc(parset.n_con_recon * parset.n_con_recon * sizeof(double));

  set_covar_Pmat_data(sigma, tau, alpha, syserr);
  set_covar_Umat(sigma, tau, alpha);

  inverse_pomat(PCmat_data, n_con_data, &info);

  multiply_mat_MN(PCmat_data, Larr_data, Lbuf, n_con_data, nq, n_con_data);
  multiply_mat_MN_transposeA(Larr_data, Lbuf, Cq, nq, nq, n_con_data);

  multiply_matvec(PCmat_data, Fcon_data, n_con_data, ybuf);
  multiply_mat_MN_transposeA(Larr_data, ybuf, yq, nq, 1, n_con_data);

  inverse_pomat(Cq, nq, &info);
  multiply_mat_MN(Cq, yq, ybuf, nq, 1, nq);

  memcpy(yq, ybuf, nq*sizeof(double));

  multiply_matvec_MN(Larr_data, n_con_data, nq, yq, ybuf);
  for(i=0; i<n_con_data; i++)
  {
    y[i] = Fcon_data[i] - ybuf[i];
  }

  multiply_matvec(PCmat_data, y, n_con_data, ybuf);
  multiply_matvec_MN(USmat, parset.n_con_recon, n_con_data, ybuf, Fcon);

  multiply_matvec_MN(Larr_rec, parset.n_con_recon, nq, yq, yuq);

  for(i=0; i<parset.n_con_recon; i++)
    Fcon[i] += yuq[i];

  // SxC^-1xS^T
  multiply_mat_MN(USmat, PCmat_data, PEmat1, parset.n_con_recon, n_con_data, n_con_data);
  multiply_mat_MN_transposeB(PEmat1, USmat, PEmat2, parset.n_con_recon, parset.n_con_recon, n_con_data);

  multiply_mat_MN(PEmat1, Larr_data, PEmat3, parset.n_con_recon, nq, n_con_data);
  for(i=0; i<parset.n_con_recon*nq; i++)PEmat3[i] -= Larr_rec[i];
  multiply_mat_MN(PEmat3, Cq, PEmat1, parset.n_con_recon, nq, nq);
  multiply_mat_MN_transposeB(PEmat1, PEmat3, PEmat4, parset.n_con_recon, parset.n_con_recon, nq);

  for(i=0; i<parset.n_con_recon; i++)
  {
    Fcerrs[i] = sqrt(sigma*sigma - PEmat2[i*parset.n_con_recon+i] + PEmat4[i*parset.n_con_recon + i]);
  }

  free(PEmat3);
  free(PEmat4);
  return;
}

/*!
 * this function calculates likelihood
 */
double prob_con_variability(const void *model)
{
  double prob = 0.0;
  int i, info;
  double *pm = (double *)model;
  double tau, sigma, alpha, lndet, syserr;
  double *Lbuf, *ybuf, *y, *yq, *Cq;

  which_particle_update = dnest_get_which_particle_update();

  if( which_parameter_update < num_params_var)
  {
    syserr = (exp(pm[0])-1.0)*con_error_mean;
    tau = exp(pm[2]);
    sigma = exp(pm[1]) * sqrt(tau);
    alpha = 1.0;

    Lbuf = workspace;
    ybuf = Lbuf + n_con_data*nq;
    y = ybuf + n_con_data;
    yq = y + n_con_data;
    Cq = yq + nq;

    set_covar_Pmat_data(sigma, tau, alpha, syserr);
    memcpy(IPCmat_data, PCmat_data, n_con_data*n_con_data*sizeof(double));

    lndet = lndet_mat(PCmat_data, n_con_data, &info);

    inverse_pomat(IPCmat_data, n_con_data, &info); /* calculate C^-1 */

    /* calculate L^T*C^-1*L */
    multiply_mat_MN(IPCmat_data, Larr_data, Lbuf, n_con_data, nq, n_con_data);
    multiply_mat_MN_transposeA(Larr_data, Lbuf, Cq, nq, nq, n_con_data);

    /* calculate L^T*C^-1*y */
    multiply_matvec(IPCmat_data, Fcon_data, n_con_data, ybuf);
    multiply_mat_MN_transposeA(Larr_data, ybuf, yq, nq, 1, n_con_data);

    /* calculate (L^T*C^-1*L)^-1 * L^T*C^-1*y */
    inverse_pomat(Cq, nq, &info);
    multiply_mat_MN(Cq, yq, ybuf, nq, 1, nq);

    Chol_decomp_L(Cq, nq, &info);
    multiply_matvec(Cq, &pm[3], nq, yq);
    for(i=0; i<nq; i++)
      yq[i] += ybuf[i];

    multiply_matvec_MN(Larr_data, n_con_data, nq, yq, ybuf);
    for(i=0; i<n_con_data; i++)
    {
      y[i] = Fcon_data[i] - ybuf[i];
    }

    multiply_matvec(IPCmat_data, y, n_con_data, ybuf);
    prob = -0.5 * cblas_ddot(n_con_data, y, 1, ybuf, 1);
    prob += -0.5*lndet;

    prob_con_particles_perturb[which_particle_update] = prob;
  }
  else
  {
    prob = prob_con_particles[which_particle_update];
  }

  return prob;
}

/*!
 * this function calculates likelihood
 *
 * using fast algorithm for semiseparable matrices
 */
double prob_con_variability_semiseparable(const void *model)
{
  double prob = 0.0;
  int i, info;
  double *pm = (double *)model;
  double tau, sigma2, lndet, syserr;
  double *Lbuf, *ybuf, *y, *yq, *Cq, *W, *D, *phi;

  which_particle_update = dnest_get_which_particle_update();

  if( which_parameter_update < num_params_var)
  {

    syserr = (exp(pm[0])-1.0)*con_error_mean;
    tau = exp(pm[2]);
    sigma2 = exp(2.0*pm[1]) * tau;

    Lbuf = workspace;
    ybuf = Lbuf + n_con_data*nq;
    y = ybuf + n_con_data;
    yq = y + n_con_data;
    Cq = yq + nq;
    W = Cq + nq*nq;
    D = W + n_con_data;
    phi = D + n_con_data;

    compute_semiseparable_drw(Tcon_data, n_con_data, sigma2, 1.0/tau, Fcerrs_data, syserr, W, D, phi);
    lndet = 0.0;
    for(i=0; i<n_con_data; i++)
      lndet += log(D[i]);

    /* calculate L^T*C^-1*L */
    multiply_mat_semiseparable_drw(Larr_data, W, D, phi, n_con_data, nq, sigma2, Lbuf);
    multiply_mat_MN_transposeA(Larr_data, Lbuf, Cq, nq, nq, n_con_data);

    /* calculate L^T*C^-1*y */
    multiply_matvec_semiseparable_drw(Fcon_data, W, D, phi, n_con_data, sigma2, ybuf);
    multiply_mat_MN_transposeA(Larr_data, ybuf, yq, nq, 1, n_con_data);

    /* calculate (L^T*C^-1*L)^-1 * L^T*C^-1*y */
    inverse_pomat(Cq, nq, &info);
    multiply_mat_MN(Cq, yq, ybuf, nq, 1, nq);

    Chol_decomp_L(Cq, nq, &info);
    multiply_matvec(Cq, &pm[3], nq, yq);
    for(i=0; i<nq; i++)
      yq[i] += ybuf[i];

    multiply_matvec_MN(Larr_data, n_con_data, nq, yq, ybuf);
    for(i=0; i<n_con_data; i++)
    {
      y[i] = Fcon_data[i] - ybuf[i];
    }

    /* y^T x C^-1 x y*/
    multiply_matvec_semiseparable_drw(y, W, D, phi, n_con_data, sigma2, ybuf);
    prob = -0.5 * cblas_ddot(n_con_data, y, 1, ybuf, 1);
    prob += -0.5*lndet;

    prob_con_particles_perturb[which_particle_update] = prob;
  }
  else
  {
    prob = prob_con_particles[which_particle_update];
  }

  return prob;
}

/*!
 * this function calculates likelihood at initital step.
 */
double prob_con_variability_initial(const void *model)
{
  double prob = 0.0;
  int i, info;
  double *pm = (double *)model;
  double tau, sigma, alpha, lndet, syserr;
  double *Lbuf, *ybuf, *y, *yq, *Cq;

  syserr = (exp(pm[0])-1.0)*con_error_mean;
  tau = exp(pm[2]);
  sigma = exp(pm[1]) * sqrt(tau);
  alpha = 1.0;

  Lbuf = workspace;
  ybuf = Lbuf + n_con_data*nq;
  y = ybuf + n_con_data;
  yq = y + n_con_data;
  Cq = yq + nq;

  set_covar_Pmat_data(sigma, tau, alpha, syserr);
  memcpy(IPCmat_data, PCmat_data, n_con_data*n_con_data*sizeof(double));

  lndet = lndet_mat(PCmat_data, n_con_data, &info);

  inverse_pomat(IPCmat_data, n_con_data, &info);

  multiply_mat_MN(IPCmat_data, Larr_data, Lbuf, n_con_data, nq, n_con_data);
  multiply_mat_MN_transposeA(Larr_data, Lbuf, Cq, nq, nq, n_con_data);

  multiply_matvec(IPCmat_data, Fcon_data, n_con_data, ybuf);
  multiply_mat_MN_transposeA(Larr_data, ybuf, yq, nq, 1, n_con_data);

  inverse_pomat(Cq, nq, &info);
  multiply_mat_MN(Cq, yq, ybuf, nq, 1, nq);

  Chol_decomp_L(Cq, nq, &info);
  multiply_matvec(Cq, &pm[3], nq, yq);
  for(i=0; i<nq; i++)
    yq[i] += ybuf[i];

  multiply_matvec_MN(Larr_data, n_con_data, nq, yq, ybuf);
  for(i=0; i<n_con_data; i++)
  {
    y[i] = Fcon_data[i] - ybuf[i];
  }

  multiply_matvec(IPCmat_data, y, n_con_data, ybuf);
  prob = -0.5 * cblas_ddot(n_con_data, y, 1, ybuf, 1);
  prob += -0.5*lndet;

  prob_con_particles[which_particle_update] = prob;
  return prob;
}

/*!
 * this function calculates likelihood at initital step.
 *
 * using fast algorithm for semiseparable matrices
 */
double prob_con_variability_initial_semiseparable(const void *model)
{
  double prob = 0.0;
  int i, info;
  double *pm = (double *)model;
  double tau, sigma2, lndet, syserr;
  double *Lbuf, *ybuf, *y, *yq, *Cq, *W, *D, *phi;

  syserr = (exp(pm[0])-1.0)*con_error_mean;
  tau = exp(pm[2]);
  sigma2 = exp(2.0*pm[1]) * tau;

  Lbuf = workspace;
  ybuf = Lbuf + n_con_data*nq;
  y = ybuf + n_con_data;
  yq = y + n_con_data;
  Cq = yq + nq;
  W = Cq + nq*nq;
  D = W + n_con_data;
  phi = D + n_con_data;

  compute_semiseparable_drw(Tcon_data, n_con_data, sigma2, 1.0/tau, Fcerrs_data, syserr, W, D, phi);
  lndet = 0.0;
  for(i=0; i<n_con_data; i++)
  lndet += log(D[i]);

  /* calculate L^T*C^-1*L */
  multiply_mat_semiseparable_drw(Larr_data, W, D, phi, n_con_data, nq, sigma2, Lbuf);
  multiply_mat_MN_transposeA(Larr_data, Lbuf, Cq, nq, nq, n_con_data);

  /* calculate L^T*C^-1*y */
  multiply_matvec_semiseparable_drw(Fcon_data, W, D, phi, n_con_data, sigma2, ybuf);
  multiply_mat_MN_transposeA(Larr_data, ybuf, yq, nq, 1, n_con_data);

  inverse_pomat(Cq, nq, &info);
  multiply_mat_MN(Cq, yq, ybuf, nq, 1, nq);

  Chol_decomp_L(Cq, nq, &info);
  multiply_matvec(Cq, &pm[3], nq, yq);
  for(i=0; i<nq; i++)
    yq[i] += ybuf[i];

  multiply_matvec_MN(Larr_data, n_con_data, nq, yq, ybuf);
  for(i=0; i<n_con_data; i++)
  {
    y[i] = Fcon_data[i] - ybuf[i];
  }

  /* y^T x C^-1 x y*/
  multiply_matvec_semiseparable_drw(y, W, D, phi, n_con_data, sigma2, ybuf);
  prob = -0.5 * cblas_ddot(n_con_data, y, 1, ybuf, 1);
  prob += -0.5*lndet;

  prob_con_particles[which_particle_update] = prob;
  return prob;
}

/*!
 * this function sets the covariance matrix at time points for reconstruction.
 * store into PSmat.
 */
void set_covar_Pmat(double sigma, double tau, double alpha)
{
  double t1, t2;
  int i, j;

  for(i=0; i<parset.n_con_recon; i++)
  {
    t1 = Tcon[i];
    for(j=0; j<=i; j++)
    {
      t2 = Tcon[j];
      PSmat[i*parset.n_con_recon+j] = sigma*sigma* exp (- pow (fabs(t1-t2) / tau, alpha));
      PSmat[j*parset.n_con_recon+i] = PSmat[i*parset.n_con_recon+j];
    }
  }
  return;
}

/*!
 * this function sets the covariance matrix at data time points
 */
void set_covar_Pmat_data(double sigma, double tau, double alpha, double syserr)
{
  double t1, t2, err2;
  int i, j;

  for(i=0; i<n_con_data; i++)
  {
    t1 = Tcon_data[i];

    for(j=0; j<i; j++)
    {
      t2 = Tcon_data[j];
      PSmat_data[i*n_con_data+j] = sigma*sigma* exp (- pow (fabs(t1-t2) / tau, alpha));
      PSmat_data[j*n_con_data+i] = PSmat_data[i*n_con_data+j];

      //PNmat_data[i*n_con_data+j] = PNmat_data[j*n_con_data+i] = 0.0;

      PCmat_data[i*n_con_data+j] = PCmat_data[j*n_con_data+i] = PSmat_data[i*n_con_data+j];
    }

    PSmat_data[i*n_con_data+i] = sigma * sigma;
    //PNmat_data[i*n_con_data+i] = Fcerrs_data[i]*Fcerrs_data[i] + syserr*syserr;
    //PCmat_data[i*n_con_data+i] = PSmat_data[i*n_con_data+i] + PNmat_data[i*n_con_data+i];
    err2 = Fcerrs_data[i]*Fcerrs_data[i] + syserr*syserr;
    PCmat_data[i*n_con_data+i] = PSmat_data[i*n_con_data+i] + err2;
  }
  return;
}

/*!
 * this function sets the covariance matrix at time of data points and reconstruction points
 */
void set_covar_Umat(double sigma, double tau, double alpha)
{
  double t1, t2;
  int i, j;

  for(i=0; i<parset.n_con_recon; i++)
  {
    t1 = Tcon[i];
    for(j=0; j<n_con_data; j++)
    {
      t2 = Tcon_data[j];
      USmat[i*n_con_data+j] = sigma*sigma * exp (- pow (fabs(t1-t2) / tau, alpha) );
    }
  }
  return;
}

/*!
 * this function initializes the continuum reconstruction.
 */
void reconstruct_con_init()
{
  int i, j;
  double dT, Tspan;

  Tspan = Tcon_data[n_con_data -1] - Tcon_data[0];

  /* set time array for continuum */
  if(parset.time_back > 0.0)
    Tcon_min = Tcon_data[0] - parset.time_back;
  else
    Tcon_min = Tcon_data[0] - fmax(0.05*Tspan, 10.0);

  Tcon_max = Tcon_data[n_con_data-1] + fmax(0.05*Tspan, 10.0);

  if(thistask == roottask)
    printf("Tcon_min_max: %f %f\n", Tcon_min - Tcon_data[0], Tcon_max - Tcon_data[n_con_data-1]);

  dT = (Tcon_max - Tcon_min)/(parset.n_con_recon -1);

  for(i=0; i<parset.n_con_recon; i++)
  {
    Tcon[i] = Tcon_min + i*dT;
  }

  /* set Larr_rec */
  for(i=0;i<parset.n_con_recon;i++)
  {
    Larr_rec[i*nq + 0]=1.0;
    for(j=1; j<nq; j++)
      Larr_rec[i*nq + j] = pow(Tcon[i], j);
  }

  sprintf(dnest_options_file, "%s/%s", parset.file_dir, "src/OPTIONSCON");
  if(thistask == roottask)
  {
    get_num_particles(dnest_options_file);
  }
  MPI_Bcast(&parset.num_particles, 1, MPI_INT, roottask, MPI_COMM_WORLD);

  Fcon = malloc(parset.n_con_recon * sizeof(double *));
  prob_con_particles = malloc(parset.num_particles * sizeof(double));
  prob_con_particles_perturb = malloc(parset.num_particles * sizeof(double));

  return;
}

/*!
 * this function finalize the continuum reconstruction.
 */
void reconstruct_con_end()
{
  int i;

  for(i=0; i<num_params; i++)
  {
    free(par_range_model[i]);
    free(par_prior_gaussian[i]);
  }
  free(par_range_model);
  free(par_prior_gaussian);
  free(par_prior_model);

  free(Fcon);

  free(prob_con_particles);
  free(prob_con_particles_perturb);

  free(best_model_con);
  free(best_model_std_con);

  return;
}

/*!
 *  create continuum from random number generate based on damped random walk model.
 */

void create_con_from_random(double sigma_hat, double tau, double alpha, double syserr)
{
  int i, info;
  double *Prandvec;
  double sigma = sigma_hat * sqrt(tau);
  double mean;

  Prandvec = malloc(parset.n_con_recon*sizeof(double));

  set_covar_Pmat(sigma, tau, alpha);

  for(i=0; i<parset.n_con_recon; i++)
  {
    Prandvec[i] = gsl_ran_ugaussian(gsl_r);
  }

  Chol_decomp_L(PSmat, parset.n_con_recon, &info);

  multiply_matvec(PSmat, Prandvec, parset.n_con_recon, Fcon);

  mean = 0.0;
  for(i=0; i<parset.n_con_recon; i++)
  {
    Fcon[i] += sigma*3.0; /* shift light curve to have positive fluxes*/
    mean += Fcon[i];
  }
  mean /= parset.n_con_recon;

  for(i=0; i<parset.n_con_recon; i++)
  {
    Fcon[i] /= mean;
  }

  for(i=0; i<parset.n_con_recon; i++)
  {
    Fcon[i] +=  gsl_ran_ugaussian(gsl_r) * con_error_mean;
    Fcerrs[i] = con_error_mean;
  }

  free(Prandvec);
  return;
}


/* linalg/invtri.c
 *
 * Copyright (C) 2016, 2017, 2018, 2019 Patrick Alken
 *
 * This 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 3, or (at your option) any
 * later version.
 *
 * This source 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.
 *
 * This module contains code to invert triangular matrices
 */

#include <config.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>

#include "recurse.h"

static int triangular_inverse_L2(CBLAS_UPLO_t Uplo, CBLAS_DIAG_t Diag, gsl_matrix * T);
static int triangular_inverse_L3(CBLAS_UPLO_t Uplo, CBLAS_DIAG_t Diag, gsl_matrix * T);
static int triangular_singular(const gsl_matrix * T);

int
gsl_linalg_tri_invert(CBLAS_UPLO_t Uplo, CBLAS_DIAG_t Diag, gsl_matrix * T)
{
  const size_t N = T->size1;

  if (N != T->size2)
    {
      GSL_ERROR ("matrix must be square", GSL_ENOTSQR);
    }
  else
    {
      int status;

      status = triangular_singular(T);
      if (status)
        return status;

      return triangular_inverse_L3(Uplo, Diag, T);
    }
}

/*
triangular_inverse_L2()
  Invert a triangular matrix T

Inputs: Uplo - CblasUpper or CblasLower
        Diag - unit triangular?
        T    - on output the upper (or lower) part of T
               is replaced by its inverse

Return: success/error

Notes:
1) Based on LAPACK routine DTRTI2 using Level 2 BLAS
*/

static int
triangular_inverse_L2(CBLAS_UPLO_t Uplo, CBLAS_DIAG_t Diag, gsl_matrix * T)
{
  const size_t N = T->size1;

  if (N != T->size2)
    {
      GSL_ERROR ("matrix must be square", GSL_ENOTSQR);
    }
  else
    {
      gsl_matrix_view m;
      gsl_vector_view v;
      size_t i;

      if (Uplo == CblasUpper)
        {
          for (i = 0; i < N; ++i)
            {
              double aii;

              if (Diag == CblasNonUnit)
                {
                  double *Tii = gsl_matrix_ptr(T, i, i);
                  *Tii = 1.0 / *Tii;
                  aii = -(*Tii);
                }
              else
                {
                  aii = -1.0;
                }

              if (i > 0)
                {
                  m = gsl_matrix_submatrix(T, 0, 0, i, i);
                  v = gsl_matrix_subcolumn(T, i, 0, i);

                  gsl_blas_dtrmv(CblasUpper, CblasNoTrans, Diag,
                                 &m.matrix, &v.vector);

                  gsl_blas_dscal(aii, &v.vector);
                }
            } /* for (i = 0; i < N; ++i) */
        }
      else
        {
          for (i = 0; i < N; ++i)
            {
              double ajj;
              size_t j = N - i - 1;

              if (Diag == CblasNonUnit)
                {
                  double *Tjj = gsl_matrix_ptr(T, j, j);
                  *Tjj = 1.0 / *Tjj;
                  ajj = -(*Tjj);
                }
              else
                {
                  ajj = -1.0;
                }

              if (j < N - 1)
                {
                  m = gsl_matrix_submatrix(T, j + 1, j + 1,
                                           N - j - 1, N - j - 1);
                  v = gsl_matrix_subcolumn(T, j, j + 1, N - j - 1);

                  gsl_blas_dtrmv(CblasLower, CblasNoTrans, Diag,
                                 &m.matrix, &v.vector);

                  gsl_blas_dscal(ajj, &v.vector);
                }
            } /* for (i = 0; i < N; ++i) */
        }

      return GSL_SUCCESS;
    }
}

/*
triangular_inverse_L3()
  Invert a triangular matrix T

Inputs: Uplo - CblasUpper or CblasLower
        Diag - unit triangular?
        T    - on output the upper (or lower) part of T
               is replaced by its inverse

Return: success/error

Notes:
1) Based on ReLAPACK routine DTRTRI using Level 3 BLAS
*/

static int
triangular_inverse_L3(CBLAS_UPLO_t Uplo, CBLAS_DIAG_t Diag, gsl_matrix * T)
{
  const size_t N = T->size1;

  if (N != T->size2)
    {
      GSL_ERROR ("matrix must be square", GSL_ENOTSQR);
    }
  else if (N <= CROSSOVER_INVTRI)
    {
      /* use Level 2 BLAS code */
      return triangular_inverse_L2(Uplo, Diag, T);
    }
  else
    {
      /*
       * partition matrix:
       *
       * T11 T12
       * T21 T22
       *
       * where T11 is N1-by-N1
       */
      int status;
      const size_t N1 = GSL_LINALG_SPLIT(N);
      const size_t N2 = N - N1;
      gsl_matrix_view T11 = gsl_matrix_submatrix(T, 0, 0, N1, N1);
      gsl_matrix_view T12 = gsl_matrix_submatrix(T, 0, N1, N1, N2);
      gsl_matrix_view T21 = gsl_matrix_submatrix(T, N1, 0, N2, N1);
      gsl_matrix_view T22 = gsl_matrix_submatrix(T, N1, N1, N2, N2);

      /* recursion on T11 */
      status = triangular_inverse_L3(Uplo, Diag, &T11.matrix);
      if (status)
        return status;

      if (Uplo == CblasLower)
        {
          /* T21 = - T21 * T11 */
          gsl_blas_dtrmm(CblasRight, Uplo, CblasNoTrans, Diag, -1.0, &T11.matrix, &T21.matrix);

          /* T21 = T22 * T21^{-1} */
          gsl_blas_dtrsm(CblasLeft, Uplo, CblasNoTrans, Diag, 1.0, &T22.matrix, &T21.matrix);
        }
      else
        {
          /* T12 = - T11 * T12 */
          gsl_blas_dtrmm(CblasLeft, Uplo, CblasNoTrans, Diag, -1.0, &T11.matrix, &T12.matrix);

          /* T12 = T12 * T22^{-1} */
          gsl_blas_dtrsm(CblasRight, Uplo, CblasNoTrans, Diag, 1.0, &T22.matrix, &T12.matrix);
        }

      /* recursion on T22 */
      status = triangular_inverse_L3(Uplo, Diag, &T22.matrix);
      if (status)
        return status;

      return GSL_SUCCESS;
    }
}

static int
triangular_singular(const gsl_matrix * T)
{
  size_t i;

  for (i = 0; i < T->size1; ++i)
    {
      double Tii = gsl_matrix_get(T, i, i);
      if (Tii == 0.0)
        return GSL_ESING;
    }

  return GSL_SUCCESS;
}

#ifndef GSL_DISABLE_DEPRECATED

int
gsl_linalg_tri_upper_invert(gsl_matrix * T)
{
  int status = triangular_singular(T);
  if (status)
    return status;

  return triangular_inverse_L3(CblasUpper, CblasNonUnit, T);
}

int
gsl_linalg_tri_lower_invert(gsl_matrix * T)
{
  int status = triangular_singular(T);
  if (status)
    return status;

  return triangular_inverse_L3(CblasLower, CblasNonUnit, T);
}

int
gsl_linalg_tri_upper_unit_invert(gsl_matrix * T)
{
  int status = triangular_singular(T);
  if (status)
    return status;

  return triangular_inverse_L3(CblasUpper, CblasUnit, T);
}

int
gsl_linalg_tri_lower_unit_invert(gsl_matrix * T)
{
  int status = triangular_singular(T);
  if (status)
    return status;

  return triangular_inverse_L3(CblasLower, CblasUnit, T);
}

#endif


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>

#ifdef GSL_FOUND
#include <gsl/gsl_integration.h>
#endif

#include "core_allvars.h"
#include "core_init.h"
#include "core_mymalloc.h"
#include "core_cool_func.h"
//#include "core_metal_yield.h"

/* These functions do not need to be exposed externally */
double integrand_time_to_present(const double a, void *param);
void set_units(void);
void read_snap_list(const int ThisTask);
double time_to_present(const double z);
struct table{
        double tbl[1000][17];
        int nr;
};
struct table read_table(char *fname, int ncols);
void read_metal_yield();

#ifdef HDF5
#include "io/io_save_hdf5.h"
#endif

void init(const int ThisTask)
{
    run_params.Age = mymalloc(ABSOLUTEMAXSNAPS*sizeof(*(run_params.Age)));

    set_units();

    read_snap_list(ThisTask);

    //Hack to fix deltaT for snapshot 0
    //This way, galsnapnum = -1 will not segfault.
    run_params.Age[0] = time_to_present(1000.0);//lookback time from z=1000
    run_params.Age++;

    for(int i = 0; i < run_params.Snaplistlen; i++) {
        run_params.ZZ[i] = 1 / run_params.AA[i] - 1;
        run_params.Age[i] = time_to_present(run_params.ZZ[i]);  //this is actually a lookback time
	run_params.lbtime[i] = (run_params.Age[0] - run_params.Age[i]) * run_params.UnitTime_in_s / SEC_PER_MEGAYEAR; //age of universe since bigbang
    }

    run_params.a0 = 1.0 / (1.0 + run_params.Reionization_z0);
    run_params.ar = 1.0 / (1.0 + run_params.Reionization_zr);

    read_cooling_functions();
    if(ThisTask == 0) {
        printf("cooling functions read\n");
    }

    read_metal_yield();
    if(ThisTask == 0){
	printf("metal yields read\n\n");
    }

#if 0
#ifdef HDF5
    if(HDF5Output) {
        calc_hdf5_props();
    }
#endif
#endif

}

void set_units(void)
{

    run_params.UnitTime_in_s = run_params.UnitLength_in_cm / run_params.UnitVelocity_in_cm_per_s;
    run_params.UnitTime_in_Megayears = run_params.UnitTime_in_s / SEC_PER_MEGAYEAR;
    run_params.G = GRAVITY / pow(run_params.UnitLength_in_cm, 3) * run_params.UnitMass_in_g * pow(run_params.UnitTime_in_s, 2);
    run_params.UnitDensity_in_cgs = run_params.UnitMass_in_g / pow(run_params.UnitLength_in_cm, 3);
    run_params.UnitPressure_in_cgs = run_params.UnitMass_in_g / run_params.UnitLength_in_cm / pow(run_params.UnitTime_in_s, 2);
    run_params.UnitCoolingRate_in_cgs = run_params.UnitPressure_in_cgs / run_params.UnitTime_in_s;
    run_params.UnitEnergy_in_cgs = run_params.UnitMass_in_g * pow(run_params.UnitLength_in_cm, 2) / pow(run_params.UnitTime_in_s, 2);

    run_params.EnergySNcode = run_params.EnergySN / run_params.UnitEnergy_in_cgs * run_params.Hubble_h;
    run_params.EtaSNcode = run_params.EtaSN * (run_params.UnitMass_in_g / SOLAR_MASS) / run_params.Hubble_h;

    // convert some physical input parameters to internal units
    run_params.Hubble = HUBBLE * run_params.UnitTime_in_s;

    // compute a few quantitites
    run_params.RhoCrit = 3.0 * run_params.Hubble * run_params.Hubble / (8 * M_PI * run_params.G);
}

void read_snap_list(const int ThisTask)
{
    char fname[MAX_STRING_LEN+1];

    snprintf(fname, MAX_STRING_LEN, "%s", run_params.FileWithSnapList);
    FILE *fd = fopen(fname, "r");
    if(fd == NULL) {
        printf("can't read output list in file '%s'\n", fname);
        ABORT(0);
    }

    run_params.Snaplistlen = 0;
    do {
        if(fscanf(fd, " %lg ", &(run_params.AA[run_params.Snaplistlen])) == 1) {
            run_params.Snaplistlen++;
        } else {
            break;
        }
    } while(run_params.Snaplistlen < run_params.MAXSNAPS);
    fclose(fd);

    if(ThisTask == 0) {
        printf("found %d defined times in snaplist\n", run_params.Snaplistlen);
    }
}

double time_to_present(const double z)
{
    const double end_limit = 1.0;
    const double start_limit = 1.0/(1 + z);
    double result=0.0;
#ifdef GSL_FOUND
#define WORKSIZE 1000
    gsl_function F;
    gsl_integration_workspace *workspace;
    double abserr;

    workspace = gsl_integration_workspace_alloc(WORKSIZE);
    F.function = &integrand_time_to_present;

    gsl_integration_qag(&F, start_limit, end_limit, 1.0 / run_params.Hubble,
                        1.0e-9, WORKSIZE, GSL_INTEG_GAUSS21, workspace, &result, &abserr);

    gsl_integration_workspace_free(workspace);

#undef WORKSIZE
#else
    /* Do not have GSL - let's integrate numerically ourselves */
    const double step  = 1e-7;
    const int64_t nsteps = (end_limit - start_limit)/step;
    result = 0.0;
    const double y0 = integrand_time_to_present(start_limit + 0*step, NULL);
    const double yn = integrand_time_to_present(start_limit + nsteps*step, NULL);
    for(int64_t i=1; i<nsteps; i++) {
        result  += integrand_time_to_present(start_limit + i*step, NULL);
    }

    result = (step*0.5)*(y0 + yn + 2.0*result);
#endif

    /* convert into Myrs/h (I think -> MS 23/6/2018) */
    const double time = 1.0 / run_params.Hubble * result;

    // return time to present as a function of redshift
    return time;
}

double integrand_time_to_present(const double a, void *param)
{
    (void) param;
    return 1.0 / sqrt(run_params.Omega / a + (1.0 - run_params.Omega - run_params.OmegaLambda) + run_params.OmegaLambda * a * a);
}

void read_metal_yield(void)
{
        char fname[MAX_STRING_LEN];
	int cols=0;
	int i, j, rows;

	/* READ AGB YIELD */
	if (run_params.AGBYields == 0)
	{
		double Z_std[7] = {0.0, 1e-4, 4e-4, 4e-3, 8e-3, 0.02, 0.05}; //metal grid from Karakas 10
		strcpy(fname, "src/auxdata/yields/table2d.dat");
		cols = 13; //number of columns in the file

        	struct table data;
        	data = read_table(fname, cols);
        	rows = data.nr;
		double Z[rows];

        	//count how many rows necessary for each column based on Z
        	int count = 0;
        	for (i=0; i<rows; i++)
        	{
                	Z[i] = data.tbl[i][0];
                	if (Z[i] == Z_std[0]) {
                        	count++;
			}
        	}

        	//now assign
		run_params.countagb = count;

        	for (j=0; j<7; j++){
                	int index = 0;
                	for (i=0; i<rows; i++){
                        	if (Z[i] == Z_std[j])
                        	{
                                	run_params.magb[index] = data.tbl[i][1]; //1 is the column number
                                	run_params.qCagb[index][j] = data.tbl[i][6] + data.tbl[i][7] + data.tbl[i][11];
                                	run_params.qNagb[index][j] = data.tbl[i][8] + data.tbl[i][12];
                                	run_params.qOagb[index][j] = data.tbl[i][9];
                                	run_params.Qagb[index][j] = run_params.qCagb[index][j] + run_params.qNagb[index][j] + run_params.qOagb[index][j];
                                	index++;
                        	}
                	}
        	}
	}

	/* READ SN II YIELD */
	if (run_params.SNIIYields == 0)
        {
		double Z_std[METALGRID] = {0.0, 1e-4, 4e-4, 4e-3, 8e-3, 0.02, 0.05}; //metal grid Woosley & Weaver 1995
		strcpy(fname, "src/auxdata/yields/table4a.dat");
                cols = 17; //number of columns in the file

		struct table data;
		data = read_table(fname, cols);
		rows = data.nr;
		double Z[rows];

		//count how many rows necessary for each column based on Z
		int count = 0;
        	for (i=0; i<rows; i++)
        	{
                	Z[i] = data.tbl[i][0];
                	if (Z[i] == Z_std[0]) {
                        	count++;}
        	}

		run_params.countsn = count;

        	for (j=0; j<7; j++){
                	int index = 0;
                	for (i=0; i<rows; i++){
                	        if (Z[i] == Z_std[j])
                        	{
                                	run_params.msn[index] = data.tbl[i][1];
                                	run_params.qCsn[index][j] = data.tbl[i][6] + data.tbl[i][15];
                                	run_params.qOsn[index][j] = data.tbl[i][7];
                                	run_params.qMgsn[index][j] = data.tbl[i][9];
                                	run_params.qSisn[index][j] = data.tbl[i][10];
                                	run_params.qSsn[index][j] = data.tbl[i][11];
                                	run_params.qCasn[index][j] = data.tbl[i][12];
                                	run_params.qFesn[index][j] = data.tbl[i][13];
                                	run_params.Qsn[index][j] = run_params.qCsn[index][j] + run_params.qOsn[index][j] + run_params.qMgsn[index][j] + run_params.qSisn[index][j] + run_params.qSsn[index][j] + run_params.qCasn[index][j] + run_params.qFesn[index][j];
					index++;
                        	}
                	}
        	}
	}

	else if (run_params.SNIIYields == 1) //from Nomoto et al. 2006
	{
		double Z_std[METALGRID] = {0.0, 0.001, 0.004, 0.02};
		strcpy(fname, "src/auxdata/yields/Nomoto.dat");
		cols = 13; //number of columns in the file

		struct table data;
		data = read_table(fname, cols);
		rows = data.nr;
		double Z[rows];

		//count how many rows necessary for each column based on Z
		int count = 0;
		for (i=0; i<rows; i++)
		{
			Z[i] = data.tbl[i][0];
			if (Z[i] == Z_std[0]) {
				count++;}
		}

		run_params.countsn = count;

		for (j=0; j<4; j++){
			int index = 0;
			for (i=0; i<rows; i++){
				if (Z[i] == Z_std[j])
				{
					run_params.msn[index] = data.tbl[i][1];
					run_params.qCsn[index][j] = data.tbl[i][2] + data.tbl[i][11];
					run_params.qOsn[index][j] = data.tbl[i][3];
					run_params.qMgsn[index][j] = data.tbl[i][5];
					run_params.qSisn[index][j] = data.tbl[i][6];
					run_params.qSsn[index][j] = data.tbl[i][7];
					run_params.qCasn[index][j] = data.tbl[i][8];
					run_params.qFesn[index][j] = data.tbl[i][9];
					run_params.Qsn[index][j] = run_params.qCsn[index][j] + run_params.qOsn[index][j] + run_params.qMgsn[index][j] + run_params.qSisn[index][j] + run_params.qSsn[index][j] + run_params.qCasn[index][j] + run_params.qFesn[index][j];
					index++;
				}
			}
		}
	}

        /* SNIa YIELD */
	if (run_params.SNIaYields == 0)
	{
		run_params.qCrsnia = 0.0168; //yields from Iwamoto 1999
		run_params.qFesnia = 0.587;
		run_params.qNisnia = 0.0314;
	}

	else if (run_params.SNIaYields == 1) //yields from Seitenzahl et al. 2013
	{
		run_params.qCrsnia = 0.00857;
		run_params.qFesnia = 0.622;
		run_params.qNisnia = 0.069;
	}
	printf("ends reading yieldi \n");
}

struct table read_table(char *fname, int ncols)
{
        int i, j;
        struct table dt;
        FILE *file;

        file = fopen(fname,"r");
	fscanf(file, "%*[^\n]"); //skips the first line

        //start reading the file
        i=0;
        while (!feof(file)) //while it is not at the end of the file
    {
        for (j=0; j<ncols; j++)
        {
            fscanf(file, " %lf", &dt.tbl[i][j]); //notice the space char in front of %lf to handle any number of spaces as delimiter
        }

        fscanf(file,"%*[^\n]"); //ignore the rest of the line (if ncols is less than the file's column size), and go to the next line
        i++;
    }

        fclose(file);

        //store number of rows
        dt.nr = i-1; //i is at the line AFTER the last line, so it's must be subtracted by 1

    return dt;
}


/**
 *
 * @file core_stsmlq_sytra1.c
 *
 *  PLASMA core_blas kernel
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Hatem Ltaief
 * @author Mathieu Faverge
 * @author Azzam Haidar
 * @date 2010-11-15
 * @generated s Tue Jan  7 11:44:48 2014
 *
 **/
#include <lapacke.h>
#include "common.h"
#undef COMPLEX
#define REAL

/***************************************************************************//**
 *
 * @ingroup CORE_float
 *
 * CORE_stsmlq_sytra1: see CORE_stsmlq
 *
 * This kernel applies a Right transformation on | A1' A2 |
 * and does not handle the transpose of A1.
 * Needs therefore to make the explicit transpose of A1 before
 * and after the application of the block of reflectors
 * Can be further optimized by changing accordingly the underneath
 * kernel ztsrfb!
 *
 *******************************************************************************
 *
 * @param[in] side
 *         @arg PlasmaLeft  : apply Q or Q**T from the Left;
 *         @arg PlasmaRight : apply Q or Q**T from the Right.
 *
 * @param[in] trans
 *         @arg PlasmaNoTrans   :  No transpose, apply Q;
 *         @arg PlasmaTrans :  ConjTranspose, apply Q**T.
 *
 * @param[in] m1
 *         The number of rows of the tile A1. m1 >= 0.
 *
 * @param[in] n1
 *         The number of columns of the tile A1. n1 >= 0.
 *
 * @param[in] m2
 *         The number of rows of the tile A2. m2 >= 0.
 *         m2 = m1 if side == PlasmaRight.
 *
 * @param[in] n2
 *         The number of columns of the tile A2. n2 >= 0.
 *         n2 = n1 if side == PlasmaLeft.
 *
 * @param[in] k
 *         The number of elementary reflectors whose product defines
 *         the matrix Q.
 *
 * @param[in] ib
 *         The inner-blocking size.  ib >= 0.
 *
 * @param[in,out] A1
 *         On entry, the m1-by-n1 tile A1.
 *         On exit, A1 is overwritten by the application of Q.
 *
 * @param[in] lda1
 *         The leading dimension of the array A1. lda1 >= max(1,m1).
 *
 * @param[in,out] A2
 *         On entry, the m2-by-n2 tile A2.
 *         On exit, A2 is overwritten by the application of Q.
 *
 * @param[in] lda2
 *         The leading dimension of the tile A2. lda2 >= max(1,m2).
 *
 * @param[in] V
 *         The i-th row must contain the vector which defines the
 *         elementary reflector H(i), for i = 1,2,...,k, as returned by
 *         CORE_STSLQT in the first k rows of its array argument V.
 *!
 * @param[in] ldv
 *         The leading dimension of the array V. ldv >= max(1,K).
 *
 * @param[in] T
 *         The IB-by-n1 triangular factor T of the block reflector.
 *         T is upper triangular by block (economic storage);
 *         The rest of the array is not referenced.
 *
 * @param[in] ldt
 *         The leading dimension of the array T. ldt >= IB.
 *
 * @param[out] WORK
 *         Workspace array of size
 *             LDWORK-by-m1 if side == PlasmaLeft
 *             LDWORK-by-IB if side == PlasmaRight
 *
 * @param[in] ldwork
 *         The leading dimension of the array WORK.
 *             LDWORK >= max(1,IB) if side == PlasmaLeft
 *             LDWORK >= max(1,n1) if side == PlasmaRight
 *
 *******************************************************************************
 *
 * @return
 *          \retval PLASMA_SUCCESS successful exit
 *          \retval <0 if -i, the i-th argument had an illegal value
 *
 ******************************************************************************/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_stsmlq_sytra1 = PCORE_stsmlq_sytra1
#define CORE_stsmlq_sytra1 PCORE_stsmlq_sytra1
#define CORE_stsmlq PCORE_stsmlq
int  CORE_stsmlq(PLASMA_enum side, PLASMA_enum trans,
                 int m1, int n1, int m2, int n2, int K, int IB,
                 float *A1, int lda1,
                 float *A2, int lda2,
                 const float *V, int ldv,
                 const float *T, int ldt,
                 float *WORK, int LDWORK);
#endif
int CORE_stsmlq_sytra1( PLASMA_enum side, PLASMA_enum trans,
                        int m1, int n1, int m2, int n2,
                        int k, int ib,
                        float *A1, int lda1,
                        float *A2, int lda2,
                        const float *V, int ldv,
                        const float *T, int ldt,
                        float *WORK, int ldwork)
{
    int i, j;

    if ( (m1 != n1) ) {
        coreblas_error(3, "Illegal value of M1, N1");
        return -3;
    }

    /* in-place transposition of A1 */
    for (j = 0; j < n1; j++){
        A1[j + j*lda1] = (A1[j + j*lda1]);

        for (i = j+1; i < m1; i++){
            *WORK = *(A1 + i + j*lda1);
            *(A1 + i + j*lda1) = (*(A1 + j + i*lda1));
            *(A1 + j + i*lda1) = (*WORK);
        }
    }

    CORE_stsmlq(side, trans, m1, n1, m2, n2, k, ib,
                A1, lda1, A2, lda2,
                V,  ldv,  T,  ldt,
                WORK, ldwork);

    /* in-place transposition of A1 */
    for (j = 0; j < n1; j++){
        A1[j + j*lda1] = (A1[j + j*lda1]);

        for (i = j+1; i < m1; i++){
            *WORK = *(A1 + i + j*lda1);
            *(A1 + i + j*lda1) = (*(A1 + j + i*lda1));
            *(A1 + j + i*lda1) = (*WORK);
        }
    }

    return PLASMA_SUCCESS;
}


#ifndef __DUMMY_H__
#define __DUMMY_H__
#include<stdio.h>
//#define NDEBUG
#include<stdint.h>
#include<omp.h>
#include<float.h>
#include<assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <math.h>
#include<string.h>
#include<dirent.h>
#include<sys/stat.h>
#include<sys/param.h>
#include<sys/types.h>
#include<signal.h>
//GSL library
#include <gsl/gsl_randist.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_cblas.h>
#include <gsl/gsl_blas.h>
#include<gsl/gsl_matrix.h>
#include <utility>
#include <string>
#include <iostream>
#include<iterator>
#include<map>
#include<vector>
#include <cctype>
#include<algorithm>
using namespace std ;

const uint32_t loop_max=400;
uint32_t loop=0;
const double  epsilon= 0.05; //error associated with user variable !
const double  epsilon_m=    0.000001; //error associated with user variable !
const double epsilon_phi=   0.000001; //error associated with user variable !
const double epsilon_theta= 0.000001; //error associated with user variable !
const double lower_limit_theta= 0.0000000000001;
const double lower_limit_m =    0.0000000000001;
const double lower_limit_phi =    0.00000000001;
clock_t  start;
clock_t  end;
gsl_rng *rand_obj ;

double maxima_phi_i_possible=0.00000000000000000000000000000000000001;
double log_Fx_maxima_phi_i_possible=-DBL_MAX;

//double * diversity;
uint32_t *freq_single_den; //  freqency of theta_w in denominator

typedef struct {
  bool* word_caps; // keeps tag if word at this pos was
  bool* comma_present_before;
  bool* period_present_before;
  vector<string> word_list ;
  uint32_t *int_word_list ; // Its an array to words
  uint32_t  *topic_usage;
  int real_word_count ; //Number of words in document
  int true_label ;
  int sampled_label;
  uint8_t *topic_list ; //Topic associated with each word
  char* document_name ;
  bool* priority_flag ;
  bool  hasPriorityWord;
  int mygamma;
}document ;

document *d_ptr ;

//Number of topics defined in begining
int updated_doc=0 ;

//GIBBS  samples count
//
int gibbs_counter=0;
const int gibbs_sample_count =25 ;
double *M_gibbs  ;
double *THETA_gibbs  ;
double *PHI_gibbs ;
const double  log_ratio=log(1000);

#define topic_count 5  // 1 to 30 !
#define document_count 790
#define total_words_count 10000

double *M ;
double *THETA ;
double *PHI;

//File names

const char* M_file="M.bin";
const char* M_IJ_CW_file="m_ij_cw.bin";
const char* THETA_file="THETA.bin";
const char* PHI_file="PHI.bin";
const char* TOPIC_file="topics_list.bin";

//For keeping last maxima , this will  speed up  search
double *last_maxima_theta_iw;
double *last_maxima_m_ij;
double *last_maxima_phi ;

//Thread info
const  int m_thread_cnt=4;
const  int theta_thread_cnt=8;

//Priors
const double m_diag=1;
const double m_nondiag=1;
const double m_beta=1;

const double theta_k=1;
const double theta_beta=1;
/*
double phi_sigma_square=10000;
*/
double phi_sigma_square=1;
double phi_mu=0;
//FUNCTIONS
//INIT  functions
void init(); //wrapper   for  all the inits
void init_model_parameters() ; //Inits model params
void init_document_params(); //INIT  document structure
void init_variables();
void init_topic_assignment(document* current_doc);
void test_model();
void free_things();

//Topic update for each document
inline void  update_topic_usage(document* current_doc_ptr, const uint8_t  new_topic,const int old_topic  );
void topic_update(document* current_doc_ptr);
void update_TOPIC();
double get_factor12(const uint32_t word_curr,const int word_next,const uint8_t t_prime,const int t_last,const int t_next,document* doc,const uint32_t mypos);
inline double get_factor2_t_i(document* current_doc_ptr, double sum);
double get_current_sum(document* doc,const uint32_t N_d, int sz);
inline void scale_pdf(double* pdf ,const int size,double sum );
inline void make_cdf_from_pdf(double* pdf ,double* cdf , const int size );
void update_m_i_j_count_word(const uint32_t word_curr,const int word_next,const int t_prime,const int t_last,const int t_next,document* doc,const int  mypos);

//update m_i_j
void  update_M();
void  update_m_ij(const int row,const int col,double * sum_den,uint32_t* freq_den,uint32_t*  freq_num);
void get_max_f_m_ij(const uint8_t i, const uint8_t j,double*log_F_max,double* maxima,double*sum_den,uint32_t*freq_den,uint32_t* freq_num,double *maxima_possible,double *value_possible);
double evaluate_m_ij_ll(double x0,const uint8_t i,const uint8_t j,double* sum_den,uint32_t* freq_den,uint32_t* freq_num,double *max, double* val_at_maxi);
void m_compute_double_term_word_stat(const uint8_t i,double* sum_den ,uint32_t* freq_den,uint32_t* freq_num_i);

//UPDATE THETA matrix
void update_THETA();
void update_theta_iw(const uint8_t i, const uint32_t w,double* den_single_term_header,double*  sum_den,uint32_t * freq_den,const  int freq_single_num);
void   get_max_f_theta_iw(const uint8_t i, const uint32_t w,double* log_Fx,double* maxima,const uint32_t freq_single_den_w);
double evaluate_theta_iw_ll(double x0,const uint8_t i,const uint32_t w,const double den_single_term_header,double * sum_den,uint32_t * freq_den,const uint32_t freq_single_num,uint32_t* freq_num,const  uint32_t freq_single_den_w,double *maxima_theta_iw_possible,double *log_Fx_maxima_theta_iw_possible);
double get_den_theta_jw(const uint8_t j , const uint32_t w);
double compute_theta_single_term_denominator(const uint32_t  w );
void theta_compute_double_term_word_stat(const uint32_t w,double* theta_double_term_sum_den,uint32_t* theta_double_term_freq_den,uint32_t* freq_single_num_w,uint32_t* freq_double_num);

//Common codes
double inline  get_number_between_two_numbers(double low, double high);
inline double rand_0_1();
void ctrl_c_handler(int single_num);
void  write_model();
double timetaken(clock_t start,clock_t  end , const char* mesaage);
double gamma_approx_sampler(double r,double log_F_max,double log_f_r,double maxima);
void   guess_lebel(document* current_doc_ptr);
double m_diagonistics(const int i,const int j,double* sum_den,uint32_t* freq_den,uint32_t* freq_num ,double* possible_maxima ,double * value_possible,const char*msg ,double maxima=-1);

double theta_diagonistics(const int i, const int w, const double den_single_term_header,double * sum_den,uint32_t* freq_den,const uint32_t freq_single_num,uint32_t *  freq_num,const uint32_t freq_single_den_w,double *maxima1,double * val_at_max1,const char*msg,double maxima=-1) ;

int linear_scan(double*cdf , const int size,double data);
void load_model_params();
void load_topics();

//update PHI
double  evaluate_phi_i(const double x0,const uint8_t i );
void update_PHI();
void   get_max_f_phi_i(const uint8_t i, double* log_Fx,double* maxima ) ; // calculate maximum of function of log !

void print_topic_corpus();
void print_model_params();
void print_system_config();

void reset_topic_usage();

uint32_t  get_key_3D_m(int x,int y,int z,int x_size,int y_size);
void print_time();
void init_gibbs_params();
void write_model_gibbs();
void do_gibbs_expectation();

const string highlight_tag_start="<:t:>";
const string highlight_tag_end="<:/t:>";
const string comma=",";
const string period=".";
void make_word_id(string word);
std::map<string , int > word_id_map;
std::map<int , string > id_word_map;
string  get_word_from_id(int id);

int  get_id_from_word(string word);
//while we read document we will silently populate  this map for furtehr uses !
void  create_word_id_id_word_map(string word);
//Makes word  of document
string make_lowercase_string(string word);
void make_int_words(document* doc,vector<string>& word_list );
bool doesWordStartsUpper(string data);
bool isThisDocumentUnderLined(string name);

vector<string> dictionary;
const char* dict_file="/data1/rahul/projects/text_mining/branch/punctuation/chris_data/word_list.txt" ;
const char* data="/data1/rahul/projects/text_mining/branch/punctuation/chris_data/AD";
//const char* dict_file="/home/rkumar2/final_test/data/word_list.txt" ;
//const char* data="/home/rkumar2/final_test/data/AE";

void  make_Dictionary();
void inline set_d_2D_m(double *ptr,int row,int col,double value,int row_length);
double inline get_d_2D_m(double* ptr,int row,int col,int row_length);
void  set_i_3D_m(uint32_t *ptr,int x,int y,int z,int  value,int y_size,int z_size);
void inline set_i_2D_m(uint32_t *ptr,int row,int col,int value,int row_length);
uint32_t inline get_i_2D_m(uint32_t* ptr,int row,int col,int row_length);
#endif