#ifndef KGS_NULLSPACEQR_H
#define KGS_NULLSPACEQR_H

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <string>

#include "math/QR.h"
#include "math/Nullspace.h"
#include "TransposeQR.h"

/**
 * An implementation of Nullspace backed by a QR decomposition
 */
class NullspaceQR: public Nullspace {
 public:
  /** Will construct a nullspace using a QR (transpose) decomposition */
  NullspaceQR(TransposeQR* qr);

  ~NullspaceQR();

  /** Update the Nullspace (and underlying SVD) to reflect an updated state of the matrix */
  void updateFromMatrix() override;

private:
  TransposeQR* m_qr;                  ///< SVD underlying this nullspace

  friend class Configuration;
};

#endif //KGS_nullptrSPACE_H


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

#pragma once

#ifndef rbtree_ebcaa429_edae_4ac6_b9a5_2fe774778822_h
#define rbtree_ebcaa429_edae_4ac6_b9a5_2fe774778822_h

#include <assert.h>
#include <gslib/std.h>

__gslib_begin__

struct _rbtree_trait_copy {};
struct _rbtree_trait_detach {};

enum rbtree_color
{
    rb_red,
    rb_black,
};

template<class _ty>
struct _rbtreenode_cpy_wrapper
{
    typedef _ty value;
    typedef _rbtreenode_cpy_wrapper<_ty> myref;
    typedef _rbtree_trait_copy tsf_behavior;
    typedef rbtree_color color;

    value               _value;
    myref*              _left;
    myref*              _right;
    myref*              _parent;
    color               _color;

    myref()
    {
        _left = _right = _parent = nullptr;
        _color = rb_black;
    }
    value* get_ptr() { return &_value; }
    const value* const_ptr() const { return &_value; }
    value& get_ref() { return _value; }
    const value& const_ref() const { return _value; }
    void born() {}
    void kill() {}
    template<class _ctor>
    void born() {}
    template<class _ctor>
    void kill() {}
    void copy(const myref* a) { get_ref() = a->const_ref(); }
    void attach(myref* a) { assert(0); }
    void swap_data(myref* a) { std::swap(_value, a->_value); }
    void set_color(color c) { _color = c; }
    color get_color() const { return _color; }
    bool is_red() const { return _color == rb_red; }
};

template<class _ty>
struct _rbtreenode_wrapper
{
    typedef _ty value;
    typedef _rbtreenode_wrapper<_ty> myref;
    typedef _rbtree_trait_detach tsf_behavior;
    typedef rbtree_color color;

    value*              _value;
    myref*              _left;
    myref*              _right;
    myref*              _parent;
    color               _color;

    myref()
    {
        _left = _right = _parent = nullptr;
        _value = nullptr;
        _color = rb_black;
    }
    value* get_ptr() { return _value; }
    const value* const_ptr() const { return _value; }
    value& get_ref() { return *_value; }
    const value& const_ref() const { return *_value; }
    void copy(const myref* a) { get_ref() = a->const_ref(); }
    void born() { !! }
    template<class _ctor>
    void born() { _value = new _ctor; }
    void kill() { if(_value) { delete _value; _value = nullptr; } }
    template<class _ctor>
    void kill() { if(_value) { delete _value; _value = nullptr; } }
    void attach(myref* a)
    {
        assert(a && a->_value);
        kill();
        _value = a->_value;
        a->_value = nullptr;
    }
    void swap_data(myref* a) { gs_swap(_value, a->_value); }
    void set_color(color c) { _color = c; }
    color get_color() const { return _color; }
    bool is_red() const { return _color == rb_red; }
};

template<class _wrapper>
struct _rbtree_allocator
{
    typedef _wrapper wrapper;
    static wrapper* born() { return new wrapper; }
    static void kill(wrapper* w) { delete w; }
};

template<class _val>
struct _rbtreenode_val
{
    typedef _val value;
    typedef const _val const_value;
    typedef _rbtreenode_val<_val> myref;
    typedef rbtree_color color;

    union
    {
        value*          _vptr;
        const_value*    _cvptr;
    };

    myref() { _vptr = nullptr; }
    value* get_wrapper() const { return _vptr; }
    operator bool() const { return _vptr != nullptr; }
    bool is_left() const { return (_cvptr && _cvptr->_parent) ? _cvptr->_parent->_left == _cvptr : false; }
    bool is_right() const { return (_cvptr && _cvptr->_parent) ? _cvptr->_parent->_right == _cvptr : false; }
    bool is_root() const { return _cvptr ? (!_cvptr->_parent) : false; }
    bool is_leaf() const { return _cvptr ? (!_cvptr->_left && !_cvptr->_right) : false; }
    int up_depth() const
    {
        int depth = 0;
        for(value* p = _vptr; p; p = p->_parent, depth ++);
        return depth;
    }
    int down_depth() const { return _down_depth(_vptr, 0); }
    bool operator==(const value* v) const { return _vptr == v; }
    bool operator!=(const value* v) const { return _vptr != v; }
    color get_color() const { return _vptr->_color; }
    void set_color(color c) { _vptr->_color = c; }
    void swap_data(myref& a) { _vptr->swap_data(a._vptr); }

public:
    static void connect_left_child(value* p, value* l)
    {
        assert(p);
        p->_left = l;
        if(l)
            l->_parent = p;
    }
    static void connect_right_child(value* p, value* r)
    {
        assert(p);
        p->_right = r;
        if(r)
            r->_parent = p;
    }
    static bool disconnect_parent_child(value* p, value* c)
    {
        assert(p && c && (c->_parent == p));
        if(p->_left == c) {
            p->_left = c->_parent = nullptr;
            return true;
        }
        assert(p->_right == c);
        p->_right = c->_parent = nullptr;
        return false;
    }

private:
    static int _down_depth(value* v, int ctr)
    {
        if(v == nullptr)
            return ctr;
        ctr ++;
        return gs_max(_down_depth(v->_left, ctr),
            _down_depth(v->_right, ctr)
            );
    }

protected:
    value* vleft() const { return _vptr ? _vptr->_left : nullptr; }
    value* vright() const { return _vptr ? _vptr->_right : nullptr; }
    value* vparent() const { return _vptr ? _vptr->_parent : nullptr; }
    value* vsibling() const
    {
        if(!_vptr || !_vptr->_parent)
            return nullptr;
        if(is_left())
            return _vptr->_right;
        else if(is_right())
            return _vptr->_left;
        assert(!"unexpected.");
        return nullptr;
    }
    value* vroot() const
    {
        if(!_vptr)
            return nullptr;
        value* p = _vptr;
        for( ; p->_parent; p = p->_parent);
        return p;
    }

protected:
    template<class _lambda, class _value>
    static void preorder_traversal(_lambda lam, _value* v)
    {
        assert(v);
        lam(v);
        if(v->_left)
            preorder_traversal(lam, v->_left);
        if(v->_right)
            preorder_traversal(lam, v->_right);
    }
    template<class _lambda, class _value>
    static void inorder_traversal(_lambda lam, _value* v)
    {
        assert(v);
        if(v->_left)
            inorder_traversal(lam, v->_left);
        lam(v);
        if(v->_right)
            inorder_traversal(lam, v->_right);
    }
    template<class _lambda, class _value>
    static void postorder_traversal(_lambda lam, _value* v)
    {
        assert(v);
        if(v->_left)
            postorder_traversal(lam, v->_left);
        if(v->_right)
            postorder_traversal(lam, v->-right);
        lam(v);
    }

public:
    template<class _lambda>
    void inorder_traversal(_lambda lam) { if(_vptr) inorder_traversal(lam, _vptr); }
    template<class _lambda>
    void inorder_traversal(_lambda lam) const { if(_cvptr) inorder_traversal(lam, _cvptr); }
    template<class _lambda>
    void preorder_traversal(_lambda lam) { if(_vptr) preorder_traversal(lam, _vptr); }
    template<class _lambda>
    void preorder_traversal(_lambda lam) const { if(_cvptr) preorder_traversal(lam, _cvptr); }
    template<class _lambda>
    void postorder_traversal(_lambda lam) { if(_vptr) postorder_traversal(lam, _vptr); }
    template<class _lambda>
    void postorder_traversal(_lambda lam) const { if(_cvptr) postorder_traversal(lam, _cvptr); }
};

template<class _ty,
    class _wrapper = _rbtreenode_cpy_wrapper<_ty> >
class _rbtree_const_iterator:
    public _rbtreenode_val<_wrapper>
{
public:
    typedef _ty value;
    typedef _wrapper wrapper;
    typedef _rbtree_const_iterator<_ty, _wrapper> iterator;

public:
    iterator(const wrapper* w = nullptr) { _cvptr = w; }
    bool is_valid() const { return _cvptr != nullptr; }
    const value* get_ptr() const { return _cvptr->const_ptr(); }
    const value* operator->() const { return _cvptr->const_ptr(); }
    const value& operator*() const { return _cvptr->const_ref(); }
    iterator left() const { return iterator(vleft()); }
    iterator right() const { return iterator(vright()); }
    iterator parent() const { return iterator(vparent()); }
    iterator sibling() const { return iterator(vsibling()); }
    iterator root() const { return iterator(vroot()); }
    bool operator==(const iterator& that) const { return _cvptr == that._cvptr; }
    bool operator!=(const iterator& that) const { return _cvptr != that._cvptr; }
};

template<class _ty,
    class _wrapper = _rbtreenode_cpy_wrapper<_ty> >
class _rbtree_iterator:
    public _rbtree_const_iterator<_ty, _wrapper>
{
public:
    typedef _ty value;
    typedef _wrapper wrapper;
    typedef _rbtree_const_iterator<_ty, _wrapper> const_iterator;
    typedef _rbtree_const_iterator<_ty, _wrapper> superref;
    typedef _rbtree_iterator<_ty, _wrapper> iterator;

public:
    iterator(wrapper* w): superref(w) {}
    value* get_ptr() const { return _vptr->get_ptr(); }
    value* operator->() const { return _vptr->get_ptr(); }
    value& operator*() const { return _vptr->get_ref(); }
    bool operator==(const iterator& that) const { return _vptr == that._vptr; }
    bool operator!=(const iterator& that) const { return _vptr != that._vptr; }
    bool operator==(const const_iterator& that) const { return _vptr == that._vptr; }
    bool operator!=(const const_iterator& that) const { return _vptr != that._vptr; }
    operator const_iterator() { return const_iterator(_cvptr); }
    void to_root() { _vptr = vroot(); }
    void to_left() { _vptr = vleft(); }
    void to_right() { _vptr = vright(); }
    void to_sibling() { _vptr = vsibling(); }
    void to_parent() { _vptr = vparent(); }
    iterator left() const { return iterator(vleft()); }
    iterator right() const { return iterator(vright()); }
    iterator parent() const { return iterator(vparent()); }
    iterator sibling() const { return iterator(vsibling()); }
    iterator root() const { return iterator(vroot()); }
};

template<class _ty,
    class _wrapper = _rbtreenode_cpy_wrapper<_ty>,
    class _alloc = _rbtree_allocator<_wrapper> >
class rbtree:
    public _rbtreenode_val<_wrapper>
{
public:
    typedef _ty value;
    typedef _wrapper wrapper;
    typedef _alloc alloc;
    typedef rbtree<value, wrapper, alloc> myref;
    typedef _rbtree_const_iterator<_ty, _wrapper> const_iterator;
    typedef _rbtree_iterator<_ty, _wrapper> iterator;
    typedef rbtree_color color;

public:
    rbtree() { _vptr = nullptr; }
    ~rbtree() { clear(); }
    void clear() { destroy(get_root()); }
    void destroy(iterator i)
    {
        if(!i.is_valid())
            return;
        if(iterator p = i.parent())
            disconnect_parent_child(p.get_wrapper(), i.get_wrapper());
        else {
            assert(is_root(i));
            _vptr = nullptr;
        }
        _destroy(i);
    }
    void adopt(wrapper* w)
    {
        assert(!_vptr && "use attach method.");
        _vptr = w;
    }
    iterator get_root() const { return iterator(_vptr); }
    const_iterator const_root() const { return const_iterator(_cvptr); }
    bool is_root(iterator i) const { return i ? (_vptr == i.get_wrapper()) : false; }
    bool is_valid() const { return _cvptr != nullptr; }
    bool is_mine(iterator i) const
    {
        if(!i.is_valid())
            return false;
        i.to_root();
        return i.get_wrapper() == _vptr;
    }
    int depth() const { return _cvptr->down_depth(); }
    void swap(myref& that) { gs_swap(_vptr, that._vptr); }
    iterator find(const value& v) const { return find(get_root(), v); }
    iterator find(iterator i, const value& v) const
    {
        if(!i) {
            if(!is_valid())
                return i;
            i = get_root();
        }
        assert(i);
        wrapper* f = _find(i.get_wrapper(), v);
        assert(f);
        if(!(f->const_ref() == v))
            f = nullptr;
        return iterator(f);
    }
    template<class _ctor = value>
    iterator insert(const value& v)
    {
        wrapper* f = nullptr;
        if(_vptr) {
            f = _find(_vptr, v);
            assert(f);
            if(f->const_ref() == v)
                return iterator(f);
        }
        wrapper* n = initval<_ctor, wrapper::tsf_behavior>::run(alloc::born(), v);
        n->set_color(rb_red);
        if(!f)
            _vptr = n;
        else {
            (v < f->const_ref()) ? connect_left_child(f, n) :
                connect_right_child(f, n);
        }
        _fix_insert(n);
        return iterator(n);
    }
    void erase(iterator i)
    {
        assert(i);
        wrapper* n = i.get_wrapper();
        wrapper *c, *p;
        rbtree_color cr;
        if(!n->_left)
            c = n->_right;
        else if(!n->_right)
            c = n->_left;
        else {
            wrapper* b = n, *l;
            n = n->_right;
            for(; l = n->_left; n = l);
            if(!b->_parent)
                _vptr = n;
            else {
                (b->_parent->_left == b) ? b->_parent->_left = n :
                    b->_parent->_right = n;
            }
            c = n->_right;
            p = n->_parent;
            cr = n->_color;
            if(p == b)
                p = n;
            else {
                if(c)
                    c->_parent = p;
                p->_left = c;
                n->_right = b->_right;
                b->_right->_parent = n;
            }
            n->_parent = b->_parent;
            n->_color = b->_color;
            n->_left = b->_left;
            b->_left->_parent = n;
            if(cr == rb_black)
                _fix_erase(c, p);
            return;
        }
        p = n->_parent;
        cr = n->_color;
        if(c)
            c->_parent = p;
        if(!p)
            _vptr = c;
        else {
            (p->_left == n) ? p->_left = c :
                p->_right = c;
        }
        if(cr == rb_black)
            _fix_erase(c, p);
    }
    void erase(const value& v)
    {
        if(iterator i = find(v))
            erase(i);
    }

    /* The detach and attach methods, provide subtree operations */
    myref& detach(myref& subtree, iterator i)
    {
        assert(i && is_mine(i));
        if(subtree.is_valid())
            subtree.clear();
        detach<myref>(subtree, i);
        return subtree;
    }
    template<class _cont>
    void detach(_cont& cont)
    {
        cont.adopt(_vptr);
        _vptr = nullptr;
    }
    template<class _cont>
    void detach(_cont& cont, iterator i)
    {
        assert(i && is_mine(i));
        if(i == get_root())
            return detach(cont);
        iterator p = i.parent();
        assert(p);
        disconnect_parent_child(p.get_wrapper(), i.get_wrapper());
        cont.adopt(i.get_wrapper());
    }
    iterator attach(myref& subtree, iterator i)
    {
        assert(i && is_mine(i) && i.is_leaf());
        if(i.is_root()) {
            swap(subtree);
            return get_root();
        }
        iterator p = i.parent();
        assert(p);
        bool leftp = disconnect_parent_child(p.get_wrapper(), i.get_wrapper());
        gs_swap(subtree._vptr, i._vptr);
        leftp ? connect_left_child(p.get_wrapper(), i.get_wrapper()) :
            connect_right_child(p.get_wrapper(), i.get_wrapper());
        subtree.clear();
        return i;
    }

public:
    template<class _lambda>
    void preorder_for_each(_lambda lam) { preorder_traversal([](wrapper* w) { lam(w->get_ptr()); }); }
    template<class _lambda>
    void preorder_const_for_each(_lambda lam) const { preorder_traversal([](const wrapper* w) { lam(w->const_ptr()); }); }
    template<class _lambda>
    void inorder_for_each(_lambda lam) { inorder_traversal([](wrapper* w) { lam(w->get_ptr()); }); }
    template<class _lambda>
    void inorder_const_for_each(_lambda lam) const { inorder_traversal([](const wrapper* w) { lam(w->const_ptr()); }); }
    template<class _lambda>
    void postorder_for_each(_lambda lam) { postorder_traversal([](wrapper* w) { lam(w->get_ptr()); }); }
    template<class _lambda>
    void postorder_const_for_each(_lambda lam) const { postorder_traversal([](const wrapper* w) { lam(w->const_ptr()); }); }

protected:
    void _destroy(iterator i)
    {
        if(!i.is_valid())
            return;
        _destroy(i.left());
        _destroy(i.right());
        wrapper* w = i.get_wrapper();
        w->kill();
        alloc::kill(w);
    }
    wrapper* _find(wrapper* w, const value& v) const
    {
        assert(w);
        wrapper* n = w;
        for(;;) {
            wrapper* m = nullptr;
            const value& r = n->const_ref();
            if(v < r)
                m = n->_left;
            else if(r < v)
                m = n->_right;
            if(!m)
                return n;
            n = m;
        }
        return nullptr;
    }
    void _left_rotate(wrapper* n)
    {
        assert(n);
        wrapper* r = n->_right;
        if(n->_right = r->_left)
            r->_left->_parent = n;
        r->_left = n;
        if(!(r->_parent = n->_parent))
            _vptr = r;
        else {
            (n == n->_parent->_right) ? n->_parent->_right = r :
                n->_parent->_left = r;
        }
        n->_parent = r;
    }
    void _right_rotate(wrapper* n)
    {
        assert(n);
        wrapper* l = n->_left;
        if(n->_left = l->_right)
            l->_right->_parent = n;
        l->_right = n;
        if(!(l->_parent = n->_parent))
            _vptr = l;
        else {
            (n == n->_parent->_right) ? n->_parent->_right = l :
                n->_parent->_left = l;
        }
        n->_parent = l;
    }
    void _fix_insert(wrapper* n)
    {
        assert(n);
        wrapper *p, *g, *u;
        while((p = n->_parent) && p->is_red()) {
            g = p->_parent;
            if(p == g->_left) {
                u = g->_right;
                if(u && u->is_red()) {
                    u->set_color(rb_black);
                    p->set_color(rb_black);
                    g->set_color(rb_red);
                    n = g;
                }
                else {
                    if(p->_right == n) {
                        _left_rotate(p);
                        gs_swap(p, n);
                    }
                    p->set_color(rb_black);
                    g->set_color(rb_red);
                    _right_rotate(g);
                }
            }
            else {
                u = g->_left;
                if(u && u->is_red()) {
                    u->set_color(rb_black);
                    p->set_color(rb_black);
                    g->set_color(rb_red);
                    n = g;
                }
                else {
                    if(p->_left == n) {
                        _right_rotate(p);
                        gs_swap(p, n);
                    }
                    p->set_color(rb_black);
                    g->set_color(rb_red);
                    _left_rotate(g);
                }
            }
        }
        _vptr->set_color(rb_black);
    }
    void _fix_erase(wrapper* n, wrapper* p)
    {
        wrapper *s, *sl, *sr;
        while((!n || !n->is_red()) && n != _vptr) {
            if(p->_left == n) {
                s = p->_right;
                if(s->is_red()) {
                    s->set_color(rb_black);
                    p->set_color(rb_red);
                    _left_rotate(p);
                    s = p->_right;
                }
                if((!s->_left || !s->_left->is_red()) &&
                    (!s->_right || !s->_right->is_red())
                    ) {
                    s->set_color(rb_red);
                    n = p;
                    p = n->_parent;
                }
                else {
                    if(!s->_right || !s->_right->is_red()) {
                        if(sl = s->_left)
                            sl->set_color(rb_black);
                        s->set_color(rb_red);
                        _right_rotate(s);
                        s = p->_right;
                    }
                    s->set_color(p->get_color());
                    p->set_color(rb_black);
                    if(sr = s->_right)
                        sr->set_color(rb_black);
                    _left_rotate(p);
                    n = _vptr;
                    break;
                }
            }
            else {
                s = p->_left;
                if(s->is_red()) {
                    s->set_color(rb_black);
                    p->set_color(rb_red);
                    _right_rotate(p);
                    s = p->_left;
                }
                if((!s->_left || !s->_left->is_red()) &&
                    (!s->_right || !s->_right->is_red())
                    ) {
                    s->set_color(rb_red);
                    n = p;
                    p = n->_parent;
                }
                else {
                    if(!s->_left || !s->_left->is_red()) {
                        if(sr = s->_right)
                            sr->set_color(rb_black);
                        s->set_color(rb_red);
                        _left_rotate(s);
                        s = p->_left;
                    }
                    s->set_color(p->get_color());
                    p->set_color(rb_black);
                    if(sl = s->_left)
                        sl->set_color(rb_black);
                    _right_rotate(p);
                    n = _vptr;
                    break;
                }
            }
        }
        if(n)
            n->set_color(rb_black);
    }

protected:
    friend struct initval;
    template<class _ctor, class _tsftrait>
    struct initval;
    template<class _ctor>
    struct initval<_ctor, _rbtree_trait_copy>
    {
        static wrapper* run(wrapper* w, const value& v)
        {
            assert(w);
            w->get_ref() = v;
            return w;
        }
    };
    template<class _ctor>
    struct initval<_ctor, _rbtree_trait_detach>
    {
        static wrapper* run(wrapper* w, const value& v)
        {
            assert(w);
            w->_value = &v;     /* or duplicate? */
            return w;
        }
    };
};

__gslib_end__

#endif


/*
 * Barcode
 * Copyright E.G.P. Bos and F.S. Kitaura
 *
 * Distributed under the terms of the MIT License.
 * The full license is in the file LICENSE, distributed with this software.
 */

#pragma once

#include "struct_main.h"
#include <gsl/gsl_rng.h>
#include "define_opt.h"

void barcoderunner(struct DATA *data,gsl_rng * gBaseRand);
void load_initial_fields(struct DATA *data, int resnum, gsl_rng *gBaseRand);
unsigned int initial_iteration_number(struct DATA *data);
void setup_random_test(struct DATA *data, real_prec *delta_lag, real_prec *delta_eul, unsigned int facL,
                       real_prec *posx, real_prec *posy, real_prec *posz, gsl_rng *gBaseRand);
void make_initial_guess(struct DATA *data, gsl_rng *gBaseRand);


/* -----------------------------------------------------------------------------
 * Copyright 2021 Jonathan Haigh
 * SPDX-License-Identifier: MIT
 * ---------------------------------------------------------------------------*/

#ifndef SQ_INCLUDE_GUARD_core_narrow_h_
#define SQ_INCLUDE_GUARD_core_narrow_h_

#include "core/typeutil.h"

#include <concepts>
#include <cstddef>
#include <gsl/gsl>
#include <string_view>

namespace sq {

/**
 * Wrapper around gsl::narrow that provides better diags on failure.
 *
 * @param value value to convert
 * @param format_args optional arguments, passed to fmt::format that describe
 *        what kind of thing is being converted.
 *
 * E.g.
 *    narrow<int>(0.5, "number of words in file {}", path)
 * might throw an exception with a message like:
 *    number of words in file "/some/file" (0.5) does not fit in type int
 *
 * If the optional format_args args aren't given then the value's type is used
 * as a description instead. E.g.
 *    narrow<int>(0.5)
 * might throw an exception with a message like:
 *    float (0.5) does not fit in type int
 */
template <typename T, typename U, typename... FormatArgs>
SQ_ND constexpr T narrow(U value, FormatArgs &&...format_args);

/**
 * Convert an integral value to a gsl::index.
 *
 * Throws if the value can't be represented by a gsl::index.
 */
SQ_ND constexpr gsl::index to_index(auto value, auto &&...format_args);

/**
 * Convert an integral value to a std::size_t.
 *
 * Throws if the value can't be represented by a std::size_t.
 */
SQ_ND constexpr std::size_t to_size(auto value, auto &&...format_args);

} // namespace sq

#include "core/narrow.inl.h"

#endif // SQ_INCLUDE_GUARD_core_narrow_h_


#include <gsl/gsl_combination.h>

#include <caml/alloc.h>
#include <caml/bigarray.h>
#include <caml/memory.h>

static void combi_of_val(gsl_combination *c, value vc)
{
    c->n = Int_val(Field(vc, 0));
    c->k = Int_val(Field(vc, 1));
    c->data = Data_bigarray_val(Field(vc, 2));
}

CAMLprim value ml_gsl_combination_init_first(value vc)
{
    gsl_combination c;
    combi_of_val(&c, vc);
    gsl_combination_init_first(&c);
    return Val_unit;
}

CAMLprim value ml_gsl_combination_init_last(value vc)
{
    gsl_combination c;
    combi_of_val(&c, vc);
    gsl_combination_init_last(&c);
    return Val_unit;
}

CAMLprim value ml_gsl_combination_valid(value vc)
{
    int r;
    gsl_combination c;
    combi_of_val(&c, vc);
    r = gsl_combination_valid(&c);
    return Val_not(Val_bool(r));
}

CAMLprim value ml_gsl_combination_next(value vc)
{
    gsl_combination c;
    combi_of_val(&c, vc);
    gsl_combination_next(&c);
    return Val_unit;
}

CAMLprim value ml_gsl_combination_prev(value vc)
{
    gsl_combination c;
    combi_of_val(&c, vc);
    gsl_combination_prev(&c);
    return Val_unit;
}


#ifndef _EM_CTX_H_
#define _EM_CTX_H_ 1

#include "em_defs.h"

#include <petsc.h>

#include <set>
#include <map>
#include <memory>
#include <vector>

class Mesh;

struct EMContext {
  MPI_Comm world_comm, group_comm;
  int world_size, world_rank, group_size, group_rank, group_id;

  std::vector<Point> rx;
  std::vector<double> tx;
  std::vector<double> freqs;

  Eigen::VectorXcd rsp, obs, obserr;
  Eigen::VectorXi otype, fidx, tidx, ridx;

  Point top_corners[4];
  Eigen::VectorXd ztop[4], lsig[4];

  std::shared_ptr<Mesh> original_mesh, mesh;

  std::vector<int> relevant_edges;
  std::pair<int, int> local_vertices, local_edges;

  std::set<int> bdr_cells;

  int aniso_form;
  Eigen::MatrixXd rho;
  Eigen::VectorXd lb, ub;
  std::vector<std::string> rho_name;

  Vec w;
  Mat C, M, A, B;
  KSP A_ksp, B_ksp;
  PETScBlockVector s, csem_e, dual_e;
  std::map<int, PETScBlockVector> mt_e;

  PetscViewer LS_log;

  Eigen::VectorXd csem_error;
  std::map<int, Eigen::VectorXd> mt_error;

  Mat G;
  std::vector<PetscReal> v_coords;

  PetscBool use_ams;
  char iprefix[256], oprefix[256];
  PetscReal max_rx_edge_length, refine_fraction, e_rtol, dual_rtol;
  PetscInt max_adaptive_refinements, max_dofs, refine_strategy, n_groups, K_max_it, pc_threshold, inner_pc_type, direct_solver_type;

  PetscClassId EMCTX_ID;
  PetscLogEvent CreateLS, AssembleMat, AssembleRHS, SetupAMS, CreatePC, SolveLS, EstimateError,
      RefineMesh, CalculateRSP;
};

PetscErrorCode create_context(EMContext *);
PetscErrorCode destroy_context(EMContext *);
PetscErrorCode process_options(EMContext *);

#endif


#include <bindings.cmacros.h>
#include <gsl/gsl_multimin.h>

BC_INLINE2(GSL_MULTIMIN_FN_EVAL,gsl_multimin_function*,gsl_vector*,double)
BC_INLINE2(GSL_MULTIMIN_FN_EVAL_F,gsl_multimin_function_fdf*,gsl_vector*,double)
BC_INLINE3VOID(GSL_MULTIMIN_FN_EVAL_DF,gsl_multimin_function_fdf*,gsl_vector*,gsl_vector*)
BC_INLINE4VOID(GSL_MULTIMIN_FN_EVAL_F_DF,gsl_multimin_function_fdf*,gsl_vector*,double*,gsl_vector*)


/*
Copyright 2017 Jiawei Chiu

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once

#include <algorithm>
#include <array>
#include <chrono>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <memory>
#include <random>
#include <sstream>
#include <string>
#include <vector>

#include <gflags/gflags.h>
#include <glog/logging.h>

#include <cublas_v2.h>
#include <cuda_runtime.h>
#include <curand.h>
#include <cusolverDn.h>
#include <cusparse.h>

// When BLAS functions are missing, we fallback on Eigen.
#include <eigen3/Eigen/Core>
#include <eigen3/Eigen/Dense>
#include <eigen3/Eigen/SparseCore>

#include <cublas_v2.h>
// #include <magma_lapack.h>
#include <magma_v2.h>

#include <lapacke.h>

namespace gi {

using std::abs;
using std::cout;
using std::ifstream;
using std::istream;
using std::istringstream;
using std::max;
using std::min;
using std::ostream;
using std::ofstream;
using std::ostringstream;
using std::sqrt;
using std::string;
using std::unique_ptr;
using std::vector;

void MainInit(int argc, char **argv);

class Timer {
public:
  Timer();
  void reset();
  double elapsed() const;

private:
  typedef std::chrono::high_resolution_clock clock_;
  typedef std::chrono::duration<double, std::ratio<1> > second_;
  std::chrono::time_point<clock_> beg_;
};

struct EngineOptions {
  int omp_num_threads = 1;
  int device = 0;      // For GPU device.
  int num_streams = 4; // Number of CUDA streams.
  int rng_seed = 56150941;
};

#define CUDA_CALL(x)                                                           \
  { CHECK_EQ(x, cudaSuccess) << "CUDA error: " << x; }

#define CURAND_CALL(x)                                                         \
  { CHECK_EQ(x, CURAND_STATUS_SUCCESS) << "CURAND error: " << x; }

#define CUBLAS_CALL(x)                                                         \
  { CHECK_EQ(x, CUBLAS_STATUS_SUCCESS) << "CUBLAS error: " << x; }

#define CUSOLVER_CALL(x)                                                       \
  { CHECK_EQ(x, CUSOLVER_STATUS_SUCCESS) << "CUSOLVER error: " << x; }

#define CUSPARSE_CALL(x)                                                       \
  { CHECK_EQ(x, CUSPARSE_STATUS_SUCCESS) << "CUSPARSE error: " << x; }

class Engine {
public:
  Engine(const EngineOptions &opt);
  ~Engine();

  static Engine *instance() { return instance_; }
  static cudaStream_t stream(int i) { return instance()->stream_[i]; }
  static curandGenerator_t curand() { return instance()->curand_; }
  static cublasHandle_t cublas() { return instance()->cublas_; }
  static cusparseHandle_t cusparse() { return instance()->cusparse_; }
  static cusparseMatDescr_t cusparse_desc() {
    return instance()->cusparse_desc_;
  }
  static cusolverDnHandle_t cusolver_dn() { return instance()->cusolver_dn_; }
  static std::mt19937 &rng() { return instance()->rng_; }
  static float *device_s_one() { return instance()->device_s_one_; }
  static float *device_s_zero() { return instance()->device_s_zero_; }

private:
  vector<cudaStream_t> stream_;
  curandGenerator_t curand_;
  cublasHandle_t cublas_;
  cusparseHandle_t cusparse_;
  cusparseMatDescr_t cusparse_desc_;
  cusolverDnHandle_t cusolver_dn_;
  std::mt19937 rng_;
  float *device_s_one_;
  float *device_s_zero_;

  static Engine *instance_;
};

// Some convenience functions for GPU kernels.

// Returns number of blocks and number of threads given n things to work on.
void BlocksThreads(int max_blocks, int max_threads, int n, int *num_blocks,
                   int *num_threads);
}


/* matrix/gsl_matrix_double.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_MATRIX_DOUBLE_H__
#define __GSL_MATRIX_DOUBLE_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_double.h>

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

__BEGIN_DECLS

typedef struct
{
  size_t size1;
  size_t size2;
  size_t tda;
  double * data;
  gsl_block * block;
  int owner;
} gsl_matrix;

typedef struct
{
  gsl_matrix matrix;
} _gsl_matrix_view;

typedef _gsl_matrix_view gsl_matrix_view;

typedef struct
{
  gsl_matrix matrix;
} _gsl_matrix_const_view;

typedef const _gsl_matrix_const_view gsl_matrix_const_view;

/* Allocation */

GSL_EXPORT
gsl_matrix *
gsl_matrix_alloc (const size_t n1, const size_t n2);

GSL_EXPORT
gsl_matrix *
gsl_matrix_calloc (const size_t n1, const size_t n2);

GSL_EXPORT
gsl_matrix *
gsl_matrix_alloc_from_block (gsl_block * b,
                                   const size_t offset,
                                   const size_t n1,
                                   const size_t n2,
                                   const size_t d2);

GSL_EXPORT
gsl_matrix *
gsl_matrix_alloc_from_matrix (gsl_matrix * m,
                                    const size_t k1,
                                    const size_t k2,
                                    const size_t n1,
                                    const size_t n2);

GSL_EXPORT
gsl_vector *
gsl_vector_alloc_row_from_matrix (gsl_matrix * m,
                                        const size_t i);

GSL_EXPORT
gsl_vector *
gsl_vector_alloc_col_from_matrix (gsl_matrix * m,
                                        const size_t j);

GSL_EXPORT void gsl_matrix_free (gsl_matrix * m);

/* Views */

GSL_EXPORT
_gsl_matrix_view
gsl_matrix_submatrix (gsl_matrix * m,
                            const size_t i, const size_t j,
                            const size_t n1, const size_t n2);

GSL_EXPORT
_gsl_vector_view
gsl_matrix_row (gsl_matrix * m, const size_t i);

GSL_EXPORT
_gsl_vector_view
gsl_matrix_column (gsl_matrix * m, const size_t j);

GSL_EXPORT
_gsl_vector_view
gsl_matrix_diagonal (gsl_matrix * m);

GSL_EXPORT
_gsl_vector_view
gsl_matrix_subdiagonal (gsl_matrix * m, const size_t k);

GSL_EXPORT
_gsl_vector_view
gsl_matrix_superdiagonal (gsl_matrix * m, const size_t k);

GSL_EXPORT
_gsl_matrix_view
gsl_matrix_view_array (double * base,
                             const size_t n1,
                             const size_t n2);

GSL_EXPORT
_gsl_matrix_view
gsl_matrix_view_array_with_tda (double * base,
                                      const size_t n1,
                                      const size_t n2,
                                      const size_t tda);

GSL_EXPORT
_gsl_matrix_view
gsl_matrix_view_vector (gsl_vector * v,
                              const size_t n1,
                              const size_t n2);

GSL_EXPORT
_gsl_matrix_view
gsl_matrix_view_vector_with_tda (gsl_vector * v,
                                       const size_t n1,
                                       const size_t n2,
                                       const size_t tda);

GSL_EXPORT
_gsl_matrix_const_view
gsl_matrix_const_submatrix (const gsl_matrix * m,
                                  const size_t i, const size_t j,
                                  const size_t n1, const size_t n2);

GSL_EXPORT
_gsl_vector_const_view
gsl_matrix_const_row (const gsl_matrix * m,
                            const size_t i);

GSL_EXPORT
_gsl_vector_const_view
gsl_matrix_const_column (const gsl_matrix * m,
                               const size_t j);

GSL_EXPORT
_gsl_vector_const_view
gsl_matrix_const_diagonal (const gsl_matrix * m);

GSL_EXPORT
_gsl_vector_const_view
gsl_matrix_const_subdiagonal (const gsl_matrix * m,
                                    const size_t k);

GSL_EXPORT
_gsl_vector_const_view
gsl_matrix_const_superdiagonal (const gsl_matrix * m,
                                      const size_t k);

GSL_EXPORT
_gsl_matrix_const_view
gsl_matrix_const_view_array (const double * base,
                                   const size_t n1,
                                   const size_t n2);

GSL_EXPORT
_gsl_matrix_const_view
gsl_matrix_const_view_array_with_tda (const double * base,
                                            const size_t n1,
                                            const size_t n2,
                                            const size_t tda);

GSL_EXPORT
_gsl_matrix_const_view
gsl_matrix_const_view_vector (const gsl_vector * v,
                                    const size_t n1,
                                    const size_t n2);

GSL_EXPORT
_gsl_matrix_const_view
gsl_matrix_const_view_vector_with_tda (const gsl_vector * v,
                                             const size_t n1,
                                             const size_t n2,
                                             const size_t tda);

/* Operations */

GSL_EXPORT double   gsl_matrix_get(const gsl_matrix * m, const size_t i, const size_t j);
GSL_EXPORT void    gsl_matrix_set(gsl_matrix * m, const size_t i, const size_t j, const double x);

GSL_EXPORT double * gsl_matrix_ptr(gsl_matrix * m, const size_t i, const size_t j);
GSL_EXPORT const double * gsl_matrix_const_ptr(const gsl_matrix * m, const size_t i, const size_t j);

GSL_EXPORT void gsl_matrix_set_zero (gsl_matrix * m);
GSL_EXPORT void gsl_matrix_set_identity (gsl_matrix * m);
GSL_EXPORT void gsl_matrix_set_all (gsl_matrix * m, double x);

GSL_EXPORT int gsl_matrix_fread (FILE * stream, gsl_matrix * m) ;
GSL_EXPORT int gsl_matrix_fwrite (FILE * stream, const gsl_matrix * m) ;
GSL_EXPORT int gsl_matrix_fscanf (FILE * stream, gsl_matrix * m);
GSL_EXPORT int gsl_matrix_fprintf (FILE * stream, const gsl_matrix * m, const char * format);

GSL_EXPORT int gsl_matrix_memcpy(gsl_matrix * dest, const gsl_matrix * src);
GSL_EXPORT int gsl_matrix_swap(gsl_matrix * m1, gsl_matrix * m2);

GSL_EXPORT int gsl_matrix_swap_rows(gsl_matrix * m, const size_t i, const size_t j);
GSL_EXPORT int gsl_matrix_swap_columns(gsl_matrix * m, const size_t i, const size_t j);
GSL_EXPORT int gsl_matrix_swap_rowcol(gsl_matrix * m, const size_t i, const size_t j);
GSL_EXPORT int gsl_matrix_transpose (gsl_matrix * m);
GSL_EXPORT int gsl_matrix_transpose_memcpy (gsl_matrix * dest, const gsl_matrix * src);

GSL_EXPORT double gsl_matrix_max (const gsl_matrix * m);
GSL_EXPORT double gsl_matrix_min (const gsl_matrix * m);
GSL_EXPORT void gsl_matrix_minmax (const gsl_matrix * m, double * min_out, double * max_out);

GSL_EXPORT void gsl_matrix_max_index (const gsl_matrix * m, size_t * imax, size_t *jmax);
GSL_EXPORT void gsl_matrix_min_index (const gsl_matrix * m, size_t * imin, size_t *jmin);
GSL_EXPORT void gsl_matrix_minmax_index (const gsl_matrix * m, size_t * imin, size_t * jmin, size_t * imax, size_t * jmax);

GSL_EXPORT int gsl_matrix_isnull (const gsl_matrix * m);

GSL_EXPORT int gsl_matrix_add (gsl_matrix * a, const gsl_matrix * b);
GSL_EXPORT int gsl_matrix_sub (gsl_matrix * a, const gsl_matrix * b);
GSL_EXPORT int gsl_matrix_mul_elements (gsl_matrix * a, const gsl_matrix * b);
GSL_EXPORT int gsl_matrix_div_elements (gsl_matrix * a, const gsl_matrix * b);
GSL_EXPORT int gsl_matrix_scale (gsl_matrix * a, const double x);
GSL_EXPORT int gsl_matrix_add_constant (gsl_matrix * a, const double x);
GSL_EXPORT int gsl_matrix_add_diagonal (gsl_matrix * a, const double x);

/***********************************************************************/
/* The functions below are obsolete                                    */
/***********************************************************************/
GSL_EXPORT int gsl_matrix_get_row(gsl_vector * v, const gsl_matrix * m, const size_t i);
GSL_EXPORT int gsl_matrix_get_col(gsl_vector * v, const gsl_matrix * m, const size_t j);
GSL_EXPORT int gsl_matrix_set_row(gsl_matrix * m, const size_t i, const gsl_vector * v);
GSL_EXPORT int gsl_matrix_set_col(gsl_matrix * m, const size_t j, const gsl_vector * v);

/* inline functions if you are using GCC */

#ifdef HAVE_INLINE
extern inline
double
gsl_matrix_get(const gsl_matrix * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_VAL("first index out of range", GSL_EINVAL, 0) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_VAL("second index out of range", GSL_EINVAL, 0) ;
    }
#endif
  return m->data[i * m->tda + j] ;
}

extern inline
void
gsl_matrix_set(gsl_matrix * m, const size_t i, const size_t j, const double x)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_VOID("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_VOID("second index out of range", GSL_EINVAL) ;
    }
#endif
  m->data[i * m->tda + j] = x ;
}

extern inline
double *
gsl_matrix_ptr(gsl_matrix * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
    }
#endif
  return (double *) (m->data + (i * m->tda + j)) ;
}

extern inline
const double *
gsl_matrix_const_ptr(const gsl_matrix * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
    }
#endif
  return (const double *) (m->data + (i * m->tda + j)) ;
}

#endif

__END_DECLS

#endif /* __GSL_MATRIX_DOUBLE_H__ */


/* vector/gsl_vector_complex_long_double.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef __GSL_VECTOR_COMPLEX_LONG_DOUBLE_H__
#define __GSL_VECTOR_COMPLEX_LONG_DOUBLE_H__

#include <stdlib.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_vector_long_double.h>
#include <gsl/gsl_vector_complex.h>
#include <gsl/gsl_block_complex_long_double.h>

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

__BEGIN_DECLS

typedef struct
{
  size_t size;
  size_t stride;
  long double *data;
  gsl_block_complex_long_double *block;
  int owner;
} gsl_vector_complex_long_double;

typedef struct
{
  gsl_vector_complex_long_double vector;
} _gsl_vector_complex_long_double_view;

typedef _gsl_vector_complex_long_double_view gsl_vector_complex_long_double_view;

typedef struct
{
  gsl_vector_complex_long_double vector;
} _gsl_vector_complex_long_double_const_view;

typedef const _gsl_vector_complex_long_double_const_view gsl_vector_complex_long_double_const_view;

/* Allocation */

gsl_vector_complex_long_double *gsl_vector_complex_long_double_alloc (const size_t n);
gsl_vector_complex_long_double *gsl_vector_complex_long_double_calloc (const size_t n);

gsl_vector_complex_long_double *
gsl_vector_complex_long_double_alloc_from_block (gsl_block_complex_long_double * b,
                                           const size_t offset,
                                           const size_t n,
                                           const size_t stride);

gsl_vector_complex_long_double *
gsl_vector_complex_long_double_alloc_from_vector (gsl_vector_complex_long_double * v,
                                             const size_t offset,
                                             const size_t n,
                                             const size_t stride);

void gsl_vector_complex_long_double_free (gsl_vector_complex_long_double * v);

/* Views */

_gsl_vector_complex_long_double_view
gsl_vector_complex_long_double_view_array (long double *base,
                                     size_t n);

_gsl_vector_complex_long_double_view
gsl_vector_complex_long_double_view_array_with_stride (long double *base,
                                                 size_t stride,
                                                 size_t n);

_gsl_vector_complex_long_double_const_view
gsl_vector_complex_long_double_const_view_array (const long double *base,
                                           size_t n);

_gsl_vector_complex_long_double_const_view
gsl_vector_complex_long_double_const_view_array_with_stride (const long double *base,
                                                       size_t stride,
                                                       size_t n);

_gsl_vector_complex_long_double_view
gsl_vector_complex_long_double_subvector (gsl_vector_complex_long_double *base,
                                         size_t i,
                                         size_t n);

_gsl_vector_complex_long_double_view
gsl_vector_complex_long_double_subvector_with_stride (gsl_vector_complex_long_double *v,
                                                size_t i,
                                                size_t stride,
                                                size_t n);

_gsl_vector_complex_long_double_const_view
gsl_vector_complex_long_double_const_subvector (const gsl_vector_complex_long_double *base,
                                               size_t i,
                                               size_t n);

_gsl_vector_complex_long_double_const_view
gsl_vector_complex_long_double_const_subvector_with_stride (const gsl_vector_complex_long_double *v,
                                                      size_t i,
                                                      size_t stride,
                                                      size_t n);

_gsl_vector_long_double_view
gsl_vector_complex_long_double_real (gsl_vector_complex_long_double *v);

_gsl_vector_long_double_view
gsl_vector_complex_long_double_imag (gsl_vector_complex_long_double *v);

_gsl_vector_long_double_const_view
gsl_vector_complex_long_double_const_real (const gsl_vector_complex_long_double *v);

_gsl_vector_long_double_const_view
gsl_vector_complex_long_double_const_imag (const gsl_vector_complex_long_double *v);

/* Operations */

gsl_complex_long_double
gsl_vector_complex_long_double_get (const gsl_vector_complex_long_double * v, const size_t i);

void gsl_vector_complex_long_double_set (gsl_vector_complex_long_double * v, const size_t i,
                                   gsl_complex_long_double z);

gsl_complex_long_double
*gsl_vector_complex_long_double_ptr (gsl_vector_complex_long_double * v, const size_t i);

const gsl_complex_long_double
*gsl_vector_complex_long_double_const_ptr (const gsl_vector_complex_long_double * v, const size_t i);

void gsl_vector_complex_long_double_set_zero (gsl_vector_complex_long_double * v);
void gsl_vector_complex_long_double_set_all (gsl_vector_complex_long_double * v,
                                       gsl_complex_long_double z);
int gsl_vector_complex_long_double_set_basis (gsl_vector_complex_long_double * v, size_t i);

int gsl_vector_complex_long_double_fread (FILE * stream,
				    gsl_vector_complex_long_double * v);
int gsl_vector_complex_long_double_fwrite (FILE * stream,
				     const gsl_vector_complex_long_double * v);
int gsl_vector_complex_long_double_fscanf (FILE * stream,
				     gsl_vector_complex_long_double * v);
int gsl_vector_complex_long_double_fprintf (FILE * stream,
				      const gsl_vector_complex_long_double * v,
				      const char *format);

int gsl_vector_complex_long_double_memcpy (gsl_vector_complex_long_double * dest, const gsl_vector_complex_long_double * src);

int gsl_vector_complex_long_double_reverse (gsl_vector_complex_long_double * v);

int gsl_vector_complex_long_double_swap (gsl_vector_complex_long_double * v, gsl_vector_complex_long_double * w);
int gsl_vector_complex_long_double_swap_elements (gsl_vector_complex_long_double * v, const size_t i, const size_t j);

int gsl_vector_complex_long_double_isnull (const gsl_vector_complex_long_double * v);

extern int gsl_check_range;

#ifdef HAVE_INLINE

extern inline
gsl_complex_long_double
gsl_vector_complex_long_double_get (const gsl_vector_complex_long_double * v,
			      const size_t i)
{
#ifndef GSL_RANGE_CHECK_OFF
  if (i >= v->size)
    {
      const gsl_complex_long_double zero = {{0, 0}};
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, zero);
    }
#endif
  return *GSL_COMPLEX_LONG_DOUBLE_AT (v, i);
}

extern inline
void
gsl_vector_complex_long_double_set (gsl_vector_complex_long_double * v,
			      const size_t i, gsl_complex_long_double z)
{
#ifndef GSL_RANGE_CHECK_OFF
  if (i >= v->size)
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  *GSL_COMPLEX_LONG_DOUBLE_AT (v, i) = z;
}

extern inline
gsl_complex_long_double *
gsl_vector_complex_long_double_ptr (gsl_vector_complex_long_double * v,
			      const size_t i)
{
#ifndef GSL_RANGE_CHECK_OFF
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return GSL_COMPLEX_LONG_DOUBLE_AT (v, i);
}

extern inline
const gsl_complex_long_double *
gsl_vector_complex_long_double_const_ptr (const gsl_vector_complex_long_double * v,
                                    const size_t i)
{
#ifndef GSL_RANGE_CHECK_OFF
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return GSL_COMPLEX_LONG_DOUBLE_AT (v, i);
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_COMPLEX_LONG_DOUBLE_H__ */


///
/// @file
///
/// @author Angelika Schwarz (angies@cs.umu.se), Umeå University
/// @author Mirko Myllykoski (mirkom@cs.umu.se), Umeå University
///
/// @internal LICENSE
///
/// Copyright (c) 2019-2020, Umeå Universitet
///
/// Redistribution and use in source and binary forms, with or without
/// modification, are permitted provided that the following conditions are met:
///
/// 1. Redistributions of source code must retain the above copyright notice,
///    this list of conditions and the following disclaimer.
///
/// 2. Redistributions in binary form must reproduce the above copyright notice,
///    this list of conditions and the following disclaimer in the documentation
///    and/or other materials provided with the distribution.
///
/// 3. Neither the name of the copyright holder nor the names of its
///    contributors may be used to endorse or promote products derived from this
///    software without specific prior written permission.
///
/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
/// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
/// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
/// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
/// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
/// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
/// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
/// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
/// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
/// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
/// POSSIBILITY OF SUCH DAMAGE.
///

#include <starneig_config.h>
#include <starneig/configuration.h>
#include <starneig/error.h>
#include "core.h"
#include "typedefs.h"
#include "robust.h"
#include "cpu.h"
#include "partition.h"
#include "../../common/common.h"
#include "../../common/node_internal.h"
#include "../../common/matrix.h"
#include <starneig/sep_sm.h>
#include <cblas.h>
#include <stdlib.h>
#include <starpu.h>
#include <math.h>
#include <float.h>

static int search_multiplicities(int n, double *lambda, int *lambda_type)
{
    int multiplicity = 0;
    for (int i = 0; i < n; i++) {
        if (lambda_type[i] == 0) { // REAL
            double real = lambda[i];
            for (int j = i+1; j < n; j++) {
                if (lambda_type[j] == 0 && lambda[j] == real) {
                    multiplicity = 1;
                    break;
                }
            }
        }
        else { // CMPLX
            double real = lambda[i];
            double imag = lambda[i+1];
            for (int j = i+2; j < n; j++) {
                if (lambda_type[j] == 1 && lambda_type[j+1]) {
                    if (lambda[j] == real && lambda[j+1] == imag) {
                        multiplicity = 1;
                        break;
                    }

                    j++;
                }
            }
            i++;
        }
    }

    return multiplicity;
}

static starneig_error_t eigenvectors(
    struct starneig_eigenvectors_conf const *_conf,
    int n, int *selected,
    double *S, int ldS,
    double *Q, int ldQ,
    double *Y, int ldY)
{
#define S(i,j) S[(i) + (j) * (size_t)ldS]
#define Q(i,j) Q[(i) + (j) * (size_t)ldQ]
#define X(i,j) X[(i) + (j) * (size_t)ldX]
#define Y(i,j) Y[(i) + (j) * (size_t)ldY]

    // use default configuration if necessary
    struct starneig_eigenvectors_conf *conf;
    struct starneig_eigenvectors_conf local_conf;
    if (_conf == NULL)
        starneig_eigenvectors_init_conf(&local_conf);
    else
        local_conf = *_conf;
    conf = &local_conf;

    //
    // check mandatory arguments
    //

    if (selected == NULL) {
        starneig_error("Eigenvalue selection bitmap is NULL. Exiting...");
        return STARNEIG_INVALID_ARGUMENTS;
    }

    int num_selected = starneig_eigvec_std_count_selected(n, selected);
    if (num_selected == 0) {
        starneig_error("Eigenvalue selection bitmap does not have any "
                       "selected eigenvalues. Exiting...");
        return STARNEIG_INVALID_ARGUMENTS;
    }

    if (S == NULL) {
        starneig_error("Matrix S is NULL. Exiting...");
        return STARNEIG_INVALID_ARGUMENTS;
    }

    if (Q == NULL) {
        starneig_error("Matrix Q is NULL. Exiting...");
        return STARNEIG_INVALID_ARGUMENTS;
    }

    if (Y == NULL) {
        starneig_error("Eigenvector matrix is NULL. Exiting...");
        return STARNEIG_INVALID_ARGUMENTS;
    }

    //
    // check configuration
    //

    if (conf->tile_size == STARNEIG_EIGENVECTORS_DEFAULT_TILE_SIZE) {
        double select_ratio = (double) num_selected/n;
        conf->tile_size = MIN(MAX(240, sqrt(n)/sqrt(select_ratio)), 936);
        starneig_message("Setting tile size to %d.", conf->tile_size);
    }

    if (conf->tile_size <= 0) {
        starneig_error("Tile size is %d. Exiting...", conf->tile_size);
        return STARNEIG_INVALID_CONFIGURATION;
    }

    //
    // preprocess
    //

    // extract the eigenvalues and their type (1-by-1 or 2-by-2 block)
    double *lambda = (double *) malloc((size_t)n * sizeof(double));
    int *lambda_type = (int *) malloc((size_t)n * sizeof(int));
    lambda[n-1] = S(n-1,n-1);
    lambda_type[n-1] = 0;
    for (int i = 0; i < n - 1; i++) {
        if (S(i+1,i) != 0.0) {
            // A 2-by-2 block in canonical Schur form has the shape
            // [ S(i,i)      S(i,i+1)   ] = [ a b ]  or [ a -b ]
            // [ S(i+1,i)    S(i+1,i+1) ]   [-c a ]     [ c  a ].
            lambda_type[i] = 1;
            lambda_type[i+1] = 1;
            double real = S(i+1,i+1);
            double imag = sqrt(fabs(S(i+1,i)))*sqrt(fabs(S(i,i+1)));
            lambda[i] = real;
            lambda[i+1] = imag;
            i++;
        }
        else {
            // 1-by-1 block
            lambda_type[i] = 0;
            lambda[i] = S(i,i);
        }
    }

    //
    // sanity check
    //

    starneig_error_t ret = STARNEIG_SUCCESS;
    int illposed = search_multiplicities(n, lambda, lambda_type);
    if (illposed) {
        starneig_warning("Multiple eigenvalues detected.\n");
        ret = STARNEIG_CLOSE_EIGENVALUES;
    }

    //
    // overflow control
    //

    const double eps = DBL_EPSILON/2;
    const double smlnum = MAX(2*DBL_MIN, DBL_MIN*((double)n/eps));

    //
    // partition
    //

    int num_tiles = (n+conf->tile_size-1)/conf->tile_size;
    int *first_row = (int *) malloc((num_tiles+1)*sizeof(int));
    int *first_col = (int *) malloc((num_tiles+1)*sizeof(int));
    starneig_eigvec_std_partition(n, lambda_type, conf->tile_size, first_row);
    starneig_eigvec_std_partition_selected(n, first_row, selected, num_tiles, first_col);

    //
    // workspace
    //

    int ldX = ldY;
    double *X = (double *) malloc((size_t)ldX*num_selected*sizeof(double));

    size_t num_segments = (size_t) num_tiles*num_selected;

    double *Xnorms = (double *) malloc(num_segments*sizeof(double));
#define Xnorms(col, tilerow) Xnorms[(col) + (tilerow) * (size_t)num_selected]

    scaling_t *scales = (scaling_t*) malloc(num_segments*sizeof(scaling_t));
#define scales(col, tilerow) scales[(col) + (tilerow) * (size_t)num_selected]
    starneig_eigvec_std_init_scaling_factor(num_tiles*num_selected, scales);

    double *Snorms =
        (double *) malloc((size_t)num_tiles*num_tiles*sizeof(double));
#define Snorms(i,j) Snorms[(i) + (j) * (size_t)num_tiles]

    int *info = (int *) malloc((size_t)num_selected*sizeof(int));

    // Copy all selected eigenvalue types to a compact memory representation.
    int *selected_lambda_type = (int *) malloc((size_t)num_selected*sizeof(int));
    int idx = 0;
    for (int i = 0; i < n; i++) {
        if (selected[i]) {
            selected_lambda_type[idx] = lambda_type[i];
            idx++;
        }
    }

    //
    // register
    //

    starpu_data_handle_t **S_tiles;
    starpu_data_handle_t **Q_tiles;
    starpu_data_handle_t **X_tiles;
    starpu_data_handle_t **Y_tiles;
    starpu_data_handle_t *selected_tiles;
    starpu_data_handle_t *lambda_tiles;
    starpu_data_handle_t *lambda_type_tiles;
    starpu_data_handle_t *selected_lambda_type_tiles;
    starpu_data_handle_t **Xnorms_tiles;
    starpu_data_handle_t **scales_tiles;
    starpu_data_handle_t **S_tiles_norms;
    starpu_data_handle_t *info_tiles;
    S_tiles = malloc(num_tiles*sizeof(starpu_data_handle_t *));
    Q_tiles = malloc(num_tiles*sizeof(starpu_data_handle_t *));
    X_tiles = malloc(num_tiles*sizeof(starpu_data_handle_t *));
    Y_tiles = malloc(num_tiles*sizeof(starpu_data_handle_t *));
    selected_tiles = malloc(num_tiles*sizeof(starpu_data_handle_t));
    lambda_tiles = malloc(num_tiles*sizeof(starpu_data_handle_t));
    lambda_type_tiles = malloc(num_tiles*sizeof(starpu_data_handle_t));
    selected_lambda_type_tiles = malloc(num_tiles*sizeof(starpu_data_handle_t));
    Xnorms_tiles = malloc(num_tiles*sizeof(starpu_data_handle_t *));
    scales_tiles = malloc(num_tiles*sizeof(starpu_data_handle_t *));
    S_tiles_norms = malloc(num_tiles*sizeof(starpu_data_handle_t *));
    info_tiles = malloc(num_tiles*sizeof(starpu_data_handle_t));
    for (int i = 0; i < num_tiles; i++) {
        S_tiles[i] = malloc(num_tiles*sizeof(starpu_data_handle_t));
        Q_tiles[i] = malloc(num_tiles*sizeof(starpu_data_handle_t));
        X_tiles[i] = malloc(num_tiles*sizeof(starpu_data_handle_t));
        Y_tiles[i] = malloc(num_tiles*sizeof(starpu_data_handle_t));
        Xnorms_tiles[i] = malloc(num_tiles*sizeof(starpu_data_handle_t));
        scales_tiles[i] = malloc(num_tiles*sizeof(starpu_data_handle_t));
        S_tiles_norms[i] = malloc(num_tiles*sizeof(starpu_data_handle_t));

        starpu_vector_data_register(
            &selected_tiles[i],
            STARPU_MAIN_RAM,
            (uintptr_t)(&selected[first_row[i]]),
            first_row[i+1]-first_row[i],
            sizeof(int));

        starpu_vector_data_register(
            &lambda_tiles[i],
            STARPU_MAIN_RAM,
            (uintptr_t)(&lambda[first_row[i]]),
            first_row[i+1]-first_row[i],
            sizeof(double));

        starpu_vector_data_register(
            &lambda_type_tiles[i],
            STARPU_MAIN_RAM,
            (uintptr_t)(&lambda_type[first_row[i]]),
            first_row[i+1]-first_row[i],
            sizeof(int));

        starpu_vector_data_register(
            &selected_lambda_type_tiles[i],
            STARPU_MAIN_RAM,
            (uintptr_t)(&selected_lambda_type[first_col[i]]),
            first_col[i+1]-first_col[i],
            sizeof(int));

        starpu_vector_data_register(
            &info_tiles[i],
            STARPU_MAIN_RAM,
            (uintptr_t)(&info[first_col[i]]),
            first_col[i+1]-first_col[i],
            sizeof(int));

        for (int j = 0; j < num_tiles; j++) {
            if (i <= j) {
                starpu_matrix_data_register(
                    &S_tiles[i][j],
                    STARPU_MAIN_RAM,
                    (uintptr_t)(&S(first_row[i], first_row[j])),
                    ldS,
                    first_row[i+1]-first_row[i],
                    first_row[j+1]-first_row[j],
                    sizeof(double));

                starpu_variable_data_register(
                    &S_tiles_norms[i][j],
                    STARPU_MAIN_RAM,
                    (uintptr_t)(&Snorms(i,j)),
                    sizeof(double));

                starpu_matrix_data_register(
                    &X_tiles[i][j],
                    STARPU_MAIN_RAM,
                    (uintptr_t)(&X(first_row[i], first_col[j])),
                    ldX,
                    first_row[i+1]-first_row[i],
                    first_col[j+1]-first_col[j],
                    sizeof(double));

                starpu_vector_data_register(
                    &Xnorms_tiles[i][j],
                    STARPU_MAIN_RAM,
                    (uintptr_t)(&Xnorms(first_col[j],i)),
                    first_col[j+1]-first_col[j],
                    sizeof(double));

                starpu_vector_data_register(
                    &scales_tiles[i][j],
                    STARPU_MAIN_RAM,
                    (uintptr_t)(&scales(first_col[j],i)),
                    first_col[j+1]-first_col[j],
                    sizeof(scaling_t));
            }

            starpu_matrix_data_register(
                &Q_tiles[i][j],
                STARPU_MAIN_RAM,
                (uintptr_t)(&Q(first_row[i], first_row[j])),
                ldQ,
                first_row[i+1]-first_row[i],
                first_row[j+1]-first_row[j],
                sizeof(double));

            starpu_matrix_data_register(
                &Y_tiles[i][j],
                STARPU_MAIN_RAM,
                (uintptr_t)(&Y(first_row[i], first_col[j])),
                ldY,
                first_row[i+1]-first_row[i],
                first_col[j+1]-first_col[j],
                sizeof(double));
        }
    }

    //
    // insert tasks
    //

    starneig_eigvec_std_insert_backsolve_tasks(num_tiles,
        S_tiles, S_tiles_norms, lambda_tiles, lambda_type_tiles,
        X_tiles, scales_tiles, Xnorms_tiles, selected_tiles,
        selected_lambda_type_tiles, info_tiles, smlnum,
        STARPU_MAX_PRIO, STARPU_DEFAULT_PRIO);

    starpu_task_wait_for_all();

    starneig_eigvec_std_unify_scaling(num_tiles, first_row, first_col, scales, X, ldX,
        lambda_type, selected);

    starneig_eigvec_std_insert_backtransform_tasks(first_row, num_tiles,
        Q_tiles, X_tiles, Y_tiles);

    //
    // evaluate reliability
    //

    for (int i = 0; i < num_selected; i++) {
        if (info[i] != STARNEIG_SUCCESS) {
            starneig_warning("Eigenvector column X(:,%d) was perturbed and "
                             "cannot be trusted.", i);
            ret = STARNEIG_CLOSE_EIGENVALUES;
        }
    }

    starpu_task_wait_for_all();

    //
    // clean up
    //

    for (int i = 0; i < num_tiles; i++) {
        starpu_data_unregister(selected_tiles[i]);
        starpu_data_unregister(lambda_tiles[i]);
        starpu_data_unregister(lambda_type_tiles[i]);
        starpu_data_unregister(selected_lambda_type_tiles[i]);
        starpu_data_unregister(info_tiles[i]);
        for (int j = 0; j < num_tiles; j++) {
            if (i <= j) {
                starpu_data_unregister(S_tiles[i][j]);
                starpu_data_unregister(S_tiles_norms[i][j]);
                starpu_data_unregister(X_tiles[i][j]);
                starpu_data_unregister(Xnorms_tiles[i][j]);
                starpu_data_unregister(scales_tiles[i][j]);
            }
            starpu_data_unregister(Q_tiles[i][j]);
            starpu_data_unregister(Y_tiles[i][j]);
        }
        free(S_tiles[i]);
        free(Q_tiles[i]);
        free(X_tiles[i]);
        free(Y_tiles[i]);
        free(Xnorms_tiles[i]);
        free(scales_tiles[i]);
        free(S_tiles_norms[i]);
    }
    free(S_tiles);
    free(Q_tiles);
    free(X_tiles);
    free(Y_tiles);
    free(Xnorms_tiles);
    free(scales_tiles);
    free(selected_tiles);
    free(lambda_tiles);
    free(lambda_type_tiles);
    free(selected_lambda_type_tiles);
    free(info_tiles);
    free(X);
    free(Xnorms);
    free(Snorms);
    free(info);
    free(scales);
    free(lambda_type);
    free(selected_lambda_type);
    free(lambda);
    free(first_row);
    free(first_col);

#undef Xnorms
#undef Snorms
#undef scales
#undef S
#undef Q
#undef X
#undef Y

    return ret;
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

__attribute__ ((visibility ("default")))
void starneig_eigenvectors_init_conf(struct starneig_eigenvectors_conf *conf) {
    conf->tile_size = STARNEIG_EIGENVECTORS_DEFAULT_TILE_SIZE;
}

__attribute__ ((visibility ("default")))
int starneig_SEP_SM_Eigenvectors_expert(
    struct starneig_eigenvectors_conf *conf,
    int n,
    int selected[],
    double S[], int ldS,
    double Q[], int ldQ,
    double X[], int ldX)
{
    CHECK_INIT();

    starneig_node_set_blas_mode(STARNEIG_BLAS_MODE_SEQUENTIAL);
    starneig_node_set_mode(STARNEIG_MODE_SM);
    starneig_node_resume_starpu();

    starneig_error_t ret = eigenvectors(
        conf, n, selected, S, ldS, Q, ldQ, X, ldX);

    starpu_task_wait_for_all();
    starneig_node_pause_starpu();
    starneig_node_set_mode(STARNEIG_MODE_OFF);
    starneig_node_set_blas_mode(STARNEIG_BLAS_MODE_ORIGINAL);

    return ret;
}

__attribute__ ((visibility ("default")))
int starneig_SEP_SM_Eigenvectors(
    int n,
    int selected[],
    double S[], int ldS,
    double Q[], int ldQ,
    double X[], int ldX)
{
    CHECK_INIT();
    return starneig_SEP_SM_Eigenvectors_expert(
        NULL, n, selected, S, ldS, Q, ldQ, X, ldX);
}


/* Odeint solver */
#include <pygsl/utils.h>
#include <pygsl/block_helpers.h>
#include <pygsl/error_helpers.h>
#include <Python.h>
#include <gsl/gsl_odeiv.h>
#include <gsl/gsl_errno.h>

char odeiv_module_doc[] = "XXX odeiv module doc missing!\n";

static char this_file[] = __FILE__;
static PyObject *module = NULL; /* set by initodeiv */

static void			/* generic instance destruction */
generic_dealloc (PyObject *self)
{
  DEBUG_MESS(1, " *** generic_dealloc %p\n", (void *) self);
  PyMem_Free(self);
}

typedef struct {
     PyObject_HEAD
     gsl_odeiv_step * step;
     gsl_odeiv_system system;
     PyObject *py_func;
     PyObject *py_jac;
     PyObject *arguments;
     jmp_buf buffer;
}
PyGSL_odeiv_step;

typedef struct {
  PyObject_HEAD
  PyGSL_odeiv_step * step;
  gsl_odeiv_control * control;
} PyGSL_odeiv_control;

typedef struct {
  PyObject_HEAD
  PyGSL_odeiv_step * step;
  PyGSL_odeiv_control * control;
  gsl_odeiv_evolve * evolve;
} PyGSL_odeiv_evolve;

typedef struct {
  PyObject_HEAD
  gsl_odeiv_step_type * step_type;
} PyGSL_odeiv_step_type;

typedef struct {
  PyObject_HEAD
  gsl_odeiv_control_type * control_type;
} PyGSL_odeiv_control_type;

/*---------------------------------------------------------------------------
 * Declaration of the various Methods
 *---------------------------------------------------------------------------*/
/*
 * stepper
 */
static int
PyGSL_odeiv_func(double t, const double y[], double f[], void *params);
static int
PyGSL_odeiv_jac(double t, const double y[], double *dfdy, double dfdt[],
		void *params);
static PyObject *
PyGSL_odeiv_step_apply(PyGSL_odeiv_step *self, PyObject *args);
static PyObject *
PyGSL_odeiv_step_reset(PyGSL_odeiv_step *self, PyObject *args);
static void
PyGSL_odeiv_step_free(PyGSL_odeiv_step * self);
static PyObject *
PyGSL_odeiv_step_name(PyGSL_odeiv_step *self, PyObject *args);
static PyObject *
PyGSL_odeiv_step_order(PyGSL_odeiv_step *self, PyObject *args);

/*
 * control
 */
static PyObject *
PyGSL_odeiv_control_hadjust(PyGSL_odeiv_control *self, PyObject *args);
static void
PyGSL_odeiv_control_free(PyGSL_odeiv_control * self);
static PyObject *
PyGSL_odeiv_control_name(PyGSL_odeiv_control *self, PyObject *args);

/*
 * evolve
 */
static void
PyGSL_odeiv_evolve_free(PyGSL_odeiv_evolve * self);
static PyObject *
PyGSL_odeiv_evolve_apply(PyGSL_odeiv_evolve *self, PyObject *args);
static PyObject *
PyGSL_odeiv_evolve_reset(PyGSL_odeiv_evolve *self, PyObject *args);
/*---------------------------------------------------------------------------*/

static char PyGSL_odeiv_step_type_doc[] = "A odeiv step type\n";
static char PyGSL_odeiv_control_type_doc[] = "A odeiv control type\n";
static char PyGSL_odeiv_evolve_type_doc[] = "A odeiv evolve type\n";

#define PyGSL_ODEIV_GENERIC_TYPE_PYTYPE_ALL                               \
static PyTypeObject PyGSL_ODEIV_GENERIC_TYPE_PYTYPE = {		          \
  PyObject_HEAD_INIT(NULL)	/* fix up the type slot in initodeiv */	  \
  0,				/* ob_size */				  \
  PyGSL_ODEIV_GENERIC_TYPE_NAME,     	/* tp_name */			  \
  sizeof(PyGSL_ODEIV_GENERIC_TYPE),       /* tp_basicsize */		  \
  0,				/* tp_itemsize */			  \
									  \
  /* standard methods */						  \
  (destructor)  generic_dealloc,   /* tp_dealloc  ref-count==0  */	  \
  (printfunc)   0,		   /* tp_print    "print x"     */	  \
  (getattrfunc) 0,                 /* tp_getattr  "x.attr"      */	  \
  (setattrfunc) 0,		   /* tp_setattr  "x.attr=v"    */	  \
  (cmpfunc)     0,		   /* tp_compare  "x > y"       */	  \
  (reprfunc)    0,                 /* tp_repr     `x`, print x  */	  \
									  \
  /* type categories */							  \
  0,				/* tp_as_number   +,-,*,/,%,&,>>,pow...*/ \
  0,				/* tp_as_sequence +,[i],[i:j],len, ...*/  \
  0,				/* tp_as_mapping  [key], len, ...*/	  \
									  \
  /* more methods */							  \
  (hashfunc)     0,		/* tp_hash    "dict[x]" */		  \
  (ternaryfunc)  0,             /* tp_call    "x()"     */		  \
  (reprfunc)     0,             /* tp_str     "str(x)"  */		  \
  (getattrofunc) 0,		/* tp_getattro */			  \
  (setattrofunc) 0,		/* tp_setattro */			  \
  0,				/* tp_as_buffer */			  \
  0L,				/* tp_flags */				  \
  PyGSL_ODEIV_GENERIC_TYPE_DOC       /* tp_doc */                         \
};

#define PyGSL_ODEIV_GENERIC_TYPE        PyGSL_odeiv_step_type
#define PyGSL_ODEIV_GENERIC_TYPE_PYTYPE PyGSL_odeiv_step_type_pytype
#define PyGSL_ODEIV_GENERIC_TYPE_NAME   "PyGSL_odeiv_step_type"
#define PyGSL_ODEIV_GENERIC_TYPE_DOC    PyGSL_odeiv_step_type_doc
PyGSL_ODEIV_GENERIC_TYPE_PYTYPE_ALL

#undef PyGSL_ODEIV_GENERIC_TYPE
#undef PyGSL_ODEIV_GENERIC_TYPE_PYTYPE
#undef PyGSL_ODEIV_GENERIC_TYPE_NAME
#undef PyGSL_ODEIV_GENERIC_TYPE_DOC
#define PyGSL_ODEIV_GENERIC_TYPE        PyGSL_odeiv_control_type
#define PyGSL_ODEIV_GENERIC_TYPE_PYTYPE PyGSL_odeiv_control_type_pytype
#define PyGSL_ODEIV_GENERIC_TYPE_NAME   "PyGSL_odeiv_control_type"
#define PyGSL_ODEIV_GENERIC_TYPE_DOC    PyGSL_odeiv_control_type_doc
PyGSL_ODEIV_GENERIC_TYPE_PYTYPE_ALL

#define PyGSLOdeivStepType_Check(v)    ((v)->ob_type == &PyGSL_odeiv_step_type_pytype)
#define PyGSLOdeivControlType_Check(v) ((v)->ob_type == &PyGSL_odeiv_control_type_pytype)
#define PyGSLOdeivEvolveType_Check(v)  ((v)->ob_type == &PyGSL_odeiv_evolve_type_pytype)

#define PyGSL_ODEIV_GENERIC_ALL                                                           \
static PyObject *									  \
PyGSL_ODEIV_GENERIC_GETATTR(PyGSL_ODEIV_GENERIC *self, char *name)		          \
{											  \
     PyObject *tmp = NULL;								  \
											  \
     FUNC_MESS_BEGIN();									  \
 											  \
     tmp = Py_FindMethod(PyGSL_ODEIV_GENERIC_METHODS, (PyObject *) self, name);	          \
     if(NULL == tmp){	  								  \
	  PyGSL_add_traceback(module, __FILE__, "odeiv.__attr__", __LINE__ - 1);	  \
	  return NULL;									  \
     }											  \
     return tmp;                                                                          \
}                                                                                \
static PyTypeObject PyGSL_ODEIV_GENERIC_PYTYPE = {					  \
  PyObject_HEAD_INIT(NULL)	/* fix up the type slot in initcrng */			  \
  0,				/* ob_size */						  \
  PyGSL_ODEIV_GENERIC_NAME,			/* tp_name */			          \
  sizeof(PyGSL_ODEIV_GENERIC),  /* tp_basicsize */					  \
  0,				/* tp_itemsize */					  \
											  \
  /* standard methods */								  \
  (destructor)  PyGSL_ODEIV_GENERIC_DELETE,       /* tp_dealloc  ref-count==0  */	  \
  (printfunc)   0,		                      /* tp_print    "print x"     */	  \
  (getattrfunc) PyGSL_ODEIV_GENERIC_GETATTR,       /* tp_getattr  "x.attr"      */	  \
  (setattrfunc) 0,		   /* tp_setattr  "x.attr=v"    */			  \
  (cmpfunc)     0,		   /* tp_compare  "x > y"       */			  \
  (reprfunc)    0,                 /* tp_repr     `x`, print x  */			  \
											  \
  /* type categories */									  \
  0,				/* tp_as_number   +,-,*,/,%,&,>>,pow...*/		  \
  0,				/* tp_as_sequence +,[i],[i:j],len, ...*/		  \
  0,				/* tp_as_mapping  [key], len, ...*/			  \
											  \
  /* more methods */									  \
  (hashfunc)     0,		/* tp_hash    "dict[x]" */				  \
  (ternaryfunc)  0,             /* tp_call    "x()"     */				  \
  (reprfunc)     0,             /* tp_str     "str(x)"  */				  \
  (getattrofunc) 0,		/* tp_getattro */					  \
  (setattrofunc) 0,		/* tp_setattro */					  \
  0,				/* tp_as_buffer */					  \
  0L,				/* tp_flags */						  \
  PyGSL_ODEIV_GENERIC_DOC       /* doc */                                                 \
};

#define PyGSL_ODEIV_GENERIC            PyGSL_odeiv_step
#define PyGSL_ODEIV_GENERIC_NAME      "PyGSL_odeiv_step"
#define PyGSL_ODEIV_GENERIC_PYTYPE     PyGSL_odeiv_step_pytype
#define PyGSL_ODEIV_GENERIC_DOC        PyGSL_odeiv_step_doc
#define PyGSL_ODEIV_GENERIC_GETATTR    PyGSL_odeiv_step_getattr
#define PyGSL_ODEIV_GENERIC_METHODS    PyGSL_odeiv_step_methods
#define PyGSL_ODEIV_GENERIC_DELETE     PyGSL_odeiv_step_free
PyGSL_ODEIV_GENERIC_ALL
/**/;
#undef PyGSL_ODEIV_GENERIC
#undef PyGSL_ODEIV_GENERIC_NAME
#undef PyGSL_ODEIV_GENERIC_PYTYPE
#undef PyGSL_ODEIV_GENERIC_DOC
#undef PyGSL_ODEIV_GENERIC_GETATTR
#undef PyGSL_ODEIV_GENERIC_METHODS
#undef PyGSL_ODEIV_GENERIC_DELETE
#define PyGSL_ODEIV_GENERIC            PyGSL_odeiv_control
#define PyGSL_ODEIV_GENERIC_NAME      "PyGSL_odeiv_control"
#define PyGSL_ODEIV_GENERIC_PYTYPE     PyGSL_odeiv_control_pytype
#define PyGSL_ODEIV_GENERIC_DOC        PyGSL_odeiv_control_doc
#define PyGSL_ODEIV_GENERIC_GETATTR    PyGSL_odeiv_control_getattr
#define PyGSL_ODEIV_GENERIC_METHODS    PyGSL_odeiv_control_methods
#define PyGSL_ODEIV_GENERIC_DELETE     PyGSL_odeiv_control_free
PyGSL_ODEIV_GENERIC_ALL
/**/;
#undef PyGSL_ODEIV_GENERIC
#undef PyGSL_ODEIV_GENERIC_NAME
#undef PyGSL_ODEIV_GENERIC_PYTYPE
#undef PyGSL_ODEIV_GENERIC_DOC
#undef PyGSL_ODEIV_GENERIC_GETATTR
#undef PyGSL_ODEIV_GENERIC_METHODS
#undef PyGSL_ODEIV_GENERIC_DELETE
#define PyGSL_ODEIV_GENERIC            PyGSL_odeiv_evolve
#define PyGSL_ODEIV_GENERIC_NAME      "PyGSL_odeiv_evolve"
#define PyGSL_ODEIV_GENERIC_PYTYPE     PyGSL_odeiv_evolve_pytype
#define PyGSL_ODEIV_GENERIC_DOC        PyGSL_odeiv_evolve_doc
#define PyGSL_ODEIV_GENERIC_GETATTR    PyGSL_odeiv_evolve_getattr
#define PyGSL_ODEIV_GENERIC_METHODS    PyGSL_odeiv_evolve_methods
#define PyGSL_ODEIV_GENERIC_DELETE     PyGSL_odeiv_evolve_free
PyGSL_ODEIV_GENERIC_ALL
/**/;

static void
PyGSL_odeiv_step_free(PyGSL_odeiv_step * self)
{
     assert(PyGSL_ODEIV_STEP_Check(self));
     Py_DECREF(self->py_func);
     Py_XDECREF(self->py_jac);
     Py_DECREF(self->arguments);
     gsl_odeiv_step_free(self->step);
     PyMem_Free(self);
}

static PyObject *
PyGSL_odeiv_step_reset(PyGSL_odeiv_step *self, PyObject *args)
{
     assert(PyGSL_ODEIV_STEP_Check(self));
     gsl_odeiv_step_reset(self->step);
     Py_INCREF(Py_None);
     return Py_None;
}

static PyObject *
PyGSL_odeiv_step_name(PyGSL_odeiv_step *self, PyObject *args)
{
     assert(PyGSL_ODEIV_STEP_Check(self));
     return PyString_FromString(gsl_odeiv_step_name(self->step));
}

{
     assert(PyGSL_ODEIV_STEP_Check(self));
     return PyInt_FromLong((long) gsl_odeiv_step_order(self->step));
}

/* --------------------------------------------------------------------------- */
/* control_hadjust needs a few arrays */
/*
extern int
gsl_odeiv_control_hadjust (gsl_odeiv_control * c, gsl_odeiv_step * s,
			   const double y0[],  const double yerr[],
			   const double dydt[], double * h);
*/

static void
PyGSL_odeiv_control_free(PyGSL_odeiv_control * self)
{
     assert(PyGSL_ODEIV_CONTROL_Check(self));
     Py_DECREF(self->step);
     //gsl_odeiv_control_free(self->control);
     PyMem_Free(self);
}

static PyObject *
PyGSL_odeiv_control_name(PyGSL_odeiv_control *self, PyObject *args)
{
     assert(PyGSL_ODEIV_CONTROL_Check(self));
     return PyString_FromString(gsl_odeiv_control_name(self->control));
}

static void
PyGSL_odeiv_evolve_free(PyGSL_odeiv_evolve * self)
{
     assert(PyGSL_ODEIV_EVOLVE_Check(self));
     Py_DECREF(self->step);
     Py_DECREF(self->control);
     gsl_odeiv_evolve_free(self->evolve);
     PyMem_Free(self);
}

static PyObject *
PyGSL_odeiv_evolve_reset(PyGSL_odeiv_evolve *self, PyObject *args)
{
     assert(PyGSL_ODEIV_EVOLVE_Check(self));
     gsl_odeiv_evolve_reset(self->evolve);
     Py_INCREF(Py_None);
     return Py_None;
}

#if 0
static
void create_odeiv_step_types(PyObject *module_dict)
{

     PyGSL_odeiv_step_type *a_odeiv_step = NULL;
     PyObject *item=NULL;

     gsl_odeiv_step_type ** thistype;
     gsl_odeiv_step_type const * const step_types[]  ={
	  gsl_odeiv_step_rk2,
	  gsl_odeiv_step_rk4,
	  gsl_odeiv_step_rkf45,
	  gsl_odeiv_step_rkck,
	  gsl_odeiv_step_rk8pd,
	  gsl_odeiv_step_rk2imp,
	  gsl_odeiv_step_rk4imp,
	  gsl_odeiv_step_bsimp,
	  gsl_odeiv_step_gear1,
	  gsl_odeiv_step_gear2,
	  NULL
     };

     FUNC_MESS_BEGIN();

     thistype = (gsl_odeiv_step_type **) step_types;
     while((*thistype) != NULL){
	  a_odeiv_step =  PyObject_NEW(PyGSL_odeiv_step_type, &PyGSL_odeiv_step_type_pytype);
	  assert(a_odeiv_step);
	  a_odeiv_step->step_type = (gsl_odeiv_step_type *) *thistype;
	  item = PyString_FromString((*thistype)->name);
	  DEBUG_MESS(2, "Preparing step type -->%s<--", PyString_AsString(item));
	  PyGSL_clear_name(PyString_AsString(item), PyString_Size(item));
	  DEBUG_MESS(2, "Adding step type -->%s<--",  PyString_AsString(item));
	  assert(item);
	  PyDict_SetItem(module_dict, item, (PyObject *) a_odeiv_step);
	  /* Py_DECREF(item); */
	  item = NULL;
	  thistype++;

     }
     FUNC_MESS_END();
}
#endif

static PyMethodDef PyGSL_odeiv_module_functions[] = {
     {"step_rk2",    PyGSL_odeiv_step_init_rk2,    METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rk4",    PyGSL_odeiv_step_init_rk4,    METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rkf45",  PyGSL_odeiv_step_init_rkf45,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rkck",   PyGSL_odeiv_step_init_rkck,   METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rk8pd",  PyGSL_odeiv_step_init_rk8pd,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rk2imp", PyGSL_odeiv_step_init_rk2imp, METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rk4imp", PyGSL_odeiv_step_init_rk4imp, METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_bsimp",  PyGSL_odeiv_step_init_bsimp,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_gear1",  PyGSL_odeiv_step_init_gear1,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_gear2",  PyGSL_odeiv_step_init_gear2,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"control_standard_new", PyGSL_odeiv_control_init_standard_new, METH_VARARGS, NULL},
     {"control_y_new",        PyGSL_odeiv_control_init_y_new,        METH_VARARGS, NULL},
     {"control_yp_new",       PyGSL_odeiv_control_init_yp_new,       METH_VARARGS, NULL},
     {"evolve", PyGSL_odeiv_evolve_init, METH_VARARGS, NULL},
     {NULL, NULL, 0}        /* Sentinel */
};

void
initodeiv(void)
{
     PyObject *m=NULL, *item=NULL, *dict=NULL;

     FUNC_MESS_BEGIN();
     fprintf(stderr, "Compiled at %s %s\n", __DATE__, __TIME__);
     m = Py_InitModule("odeiv", PyGSL_odeiv_module_functions);
     assert(m);
     module = m;
     import_array();
     init_pygsl();

     PyGSL_odeiv_step_type_pytype.ob_type = &PyType_Type;
     PyGSL_odeiv_control_type_pytype.ob_type = &PyType_Type;

     PyGSL_odeiv_step_pytype.ob_type = &PyType_Type;
     PyGSL_odeiv_control_pytype.ob_type = &PyType_Type;
     PyGSL_odeiv_evolve_pytype.ob_type = &PyType_Type;

     dict = PyModule_GetDict(m);
     /* create_odeiv_step_types(dict); */
     if(!dict)
	  goto fail;

     if (!(item = PyString_FromString(odeiv_module_doc))){
	  PyErr_SetString(PyExc_ImportError,
			  "I could not generate module doc string!");
	  goto fail;
     }
     if (PyDict_SetItemString(dict, "__doc__", item) != 0){
	  PyErr_SetString(PyExc_ImportError,
			  "I could not init doc string!");
	  goto fail;
     }

     FUNC_MESS_END();
     return;
 fail:
     FUNC_MESS("Fail");
     fprintf(stderr, "Import of module odeiv failed!\n");
}