// Author: b1tank
// Email: b1tank@outlook.com
//=================================

// cpp essentials playground

#include <iostream>

#include <sstream> // stringstream, istringstream, ostringstream
#include <string> // to_string(), stoi()

#include <cctype> // isalnum, isalpha, isdigit, islower, isupper, isspace; toupper, tolower
#include <climits> // INT_MAX 2147483647
#include <cmath> // pow(3.0, 4.0); abs(-3.0)
#include <cstdlib> // rand() % 100 + 1; abs(-3)

#include <vector>
#include <forward_list> // singly-linked list
#include <list> // doubly-linked list
#include <stack>
#include <queue>
#include <deque>

#include <unordered_set> // unordered_set, unordered_multiset
#include <set> // set, multiset
#include <bitset> // bitset
#include <unordered_map> // unordered_map, unordered_multimap
#include <map> // map, multimap

#include <utility> // pair<>
#include <tuple> // tuple<>

#include <algorithm> // reverse, sort, transform, find, remove, count, count_if

#include <memory> // shared_ptr<>, make_shared<>

#include <stdexcept> // invalid_argument

using namespace std;

int main() {

    // cpp style ref: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es23-prefer-the--initializer-syntax

    // char and string
    char c = 'c';
    isalnum('2'); // #include<cctype>
    isalpha('a');
    isdigit('4');
    islower('z');
    isupper('Z');
    isspace(' ');
    char C = toupper(c);
    int d = 'c' - 'a';
    char e = 'c' + 2;

    string a("str");
    string b("str2");

    long i = 28;
    to_string(i); // "28"
    stoi("28"); // 28
    string str_dec = "2001, A Space Odyssey";
    string str_hex = "40c3";
    string str_bin = "-10010110001";
    string str_auto = "0x7f";
    int i_dec = stoi (str_dec, &sz);
    int i_hex = stoi (str_hex, nullptr, 16);
    int i_bin = stoi (str_bin, nullptr, 2);
    int i_auto = stoi (str_auto, nullptr, 0);

    auto c = a.at(2);
    a.front() = 'B';
    a.back() = 'E';

    a.substr(4, 2); // 2 is len
    auto pos = a.find("tr");
    a.substr(pos); // from pos to the end
    string token = s.substr(0, s.find(delimiter)); // split string

    a += "suffix";
    a.append(b);
    a.append(b, 3, 2);
    a.append("string", 3);
    a.append("string");
    a.append(10u, '.');
    a.append(b.begin()+5, b.end());

    a.insert(2, "string", 3); // just add a "size_t pos" upon append method
    a.erase(10,9); // only apply to string (not to vector!!!)
    a.erase(a.begin()+7);
    a.erase(a.begin()+7, a.end());
    a.replace(10, 8, b, 7, 6); // just add a "size_t pos" and "size_t len" upon append method

    a.swap(b);

    a.push_back('c');
    a.pop_back();

    auto it = a.begin();
    auto it = a.end();
    auto it = a.rbegin();
    auto it = a.rend();
    if (a.empty()) {}

    int s = a.length();
    int ss = a.size();

    a.find("tr") == string::npos; // find or not
    a.find("tr");
    a.find("tr", 4);
    a.find("tr", 4, 1); // starting pos, len
    a.rfind("tr"); // last appearance
    a.rfind("tr", 4);
    a.rfind("tr", 4, 1);

    a.compare(6, 5, "apple", 4, 5); // <0, ==0, >0

    // number, power and random
    int ma = INT_MAX; // #include <climits> -2147483648, 2147483647
    int mi = INT_MIN;
    int p = pow(3.0, 4.0); // #include <cmath> 81
    int a = abs(-3); // #include <cstdlib> integral
    int a = abs(-3.0); // #include <cmath> double/float
    int r = rand() % 100; // #include <cstdlib> 0-99
    int r = rand() % 100 + 1; // #include <cstdlib> 1-100

    // bool and condition
    bool t = true;
    bool f = false;

    // loop
    for (auto c : a) {}
    for (auto it = a.begin(); it != a.end(); it++) {}
    for (auto it = a.rbegin(); it != a.rend(); it++) {}

    // vector
    vector<int> v{2, 3, 4};
    vector<int> v(3);
    vector<int> v(3, 8);
    vector<vector<int>> v(3, vector<int>(8));
    vector<int> v(v1.begin()+1, v1.end());
    vector<pair<int, int>> vp;

    v = v1; // copy another vector
    v = vector<int>(); // const vector<>&
    v = {2, 3}; // initializer list

    // return {}; // return empty vector
    // return vector<int>();

    v.push_back(4);
    v.pop_back();
    vp.emplace_back(3, 5); // implicit constructor
    auto it = v.insert(v.end(), v2.begin(), v2.end());
    auto it = v.insert(v.begin()+2, 7);
    auto it = v.insert(v.begin(), 2, 7); // insert 2 "7"s
    auto it = v.erase(v.begin()+1);
    auto it = v.erase(v.begin()+1, v.end());
    v.swap(v2);
    v.clear();

    int vf = v.front();
    int vf = v.back();
    auto it = v.begin();
    auto it = v.end();
    auto it = v.rbegin();
    auto it = v.rend();

    if (v.empty()) {}

    // stack (by default underlying container is <deque>)
    // * Queue, Stack, and Priority_queue has no begin()/end(). Can't do loop. Can't modify elements by clear(), reverse().
    stack<int> st;
    stack<int> st1;
    stack<pair<int, int>> st2;

    int s = st.size();
    if (st.empty()) {}

    st.push(3); // st.push_back() of underlying container
    st2.emplace(2, 3);
    st.pop(); // st.pop_back() of underlying container

    st.top(); // st.back() of underlying container

    st.swap(st1); // cannot swap with st2 !!!

    // queue (by default underlying container is <deque>)
    queue<int> q;
    queue<int> q1;
    queue<pair<int, int>> q2;

    int s = q.size();
    if (q.empty()) {}

    q.push(3);
    q2.emplace(2, 3);

    q.front();
    q.back();

    q.pop(); // pop_front() of underlying container

    q.swap(q1); // cannot swap with q2 !!!

    // deque
    deque<int> dq;
    deque<int> dq1;
    deque<pair<int, int>> q2;

    int s = dq.size();
    if (dq.empty()) {}

    dq.push_front(3);
    dq.push_back(3);
    dq1.emplace(dq.begin()+2, 8, 3); // pos (1st arg)
    dq1.emplace_front(8, 3);
    dq1.emplace_back(8, 3);

    dq.front();
    dq.back();

    dq.begin();
    dq.end();
    dq.rbegin();
    dq.rend();

    dq.pop_front();
    dq.pop_back();

    dq.swap(q1); // cannot swap with q2 !!!

    // priority_queue (by default: Max-Heap; <vector> as the underlying container)
    priority_queue<int> pq;
    priority_queue<int, vector<int>, greater<int>> pq1; // Min-Heap
    priority_queue<pair<int, int>> pq2;

    int s = pq.size();
    if (pq.empty()) {}

    pq.push(3);
    pq2.emplace(2, 3);

    int t = pq.top();

    pq.pop();

    pq.swap(pq1); // cannot swap with pq2

    class point{
    public:
        int x;
        int y;
        point(int x, int y):x(x),y(y) {}
    };

    class pointCompLessThan {
    public:
        bool operator()(point& a, point& b) {  // operator()!!!
            return a.x < b.x;
        }
    };

    priority_queue<point, vector<point>, pointCompLessThan> Q;
    Q.emplace(1, 2);

    // bitset (index 0 from the right end !!!!!)
    vector<bitset<26>> dp;
    bitset<26> set_i(0);
    bitset<26> set_j(string("10101001"));
    set_i[1] = 1;
    int n = set_i.size(); // 26
    if (set_i.count() == 1) {}
    if ((set_i & set_j).none()) {}
    if ((set_i | set_j).any()) {}
    if ((set_i ^ set_j).all()) {}
    set_i.set(c - 'a');
    set_i.set(); // apply to all bits
    set_i.reset(c - 'a');
    set_i.reset();
    set_i.flip(c - 'a');
    set_i.flip();

    // unordered set and multiset; map and multimap
    // * Can be looped through using iterators from begin() to end()
    // * Array/vector are not hashable. One way to work around is to convert it to strings.
    unordered_set<int> s;
    unordered_set<int> s1{"red", "blue", "green"}; // {} is list initialization for containers, which is preferred !!!
    // unordered_set<int> s1( {"red", "blue", "green"} ); // also correct but allows parsing ambiguities !!!
    unordered_map<string, int> m;
    unordered_map<string, int> m1{{"red", 1}, {"blue", 2}, {"green", 3}}; // {} is list initialization for containers, which is preferred !!!
    // unordered_map<string, int> m1( {{"red", 1}, {"blue", 2}, {"green", 3}} ); // also correct but allows parsing ambiguities !!!

    int ss = s.size();
    if (s.empty()) {}

    auto it = s.begin();
    auto it = s.end();

    if (s.find(5) != s.end() || s.count(5) == 1) {}

    s.insert("orange");
    s.insert(v.begin(), v.end());
    s.insert( {"orange", "purple"} ); // initializer list
    s.erase("blue");
    s.erase(s.begin()+2);
    s.erase(s.begin()+2, s.end());

    pair<string, int> mypair ("baking powder", 3);
    m.insert (mypair);                        // copy insertion
    m.insert (make_pair<string, int>("eggs", 6)); // move insertion
    m.insert (m1.begin(), m2.end());  // range insertion
    m.insert ( {{"sugar", 8}, {"salt", 1}} );    // initializer list insertion

    s.swap(s2);

    unordered_multiset<int> ms( { 2, 3, 2, 4, 2} );
    int count = ms.count(2); // 3
    auto myrange = ms.equal_range(2);
    while (myrange.first != myrange.second) {
        cout << *myrange.first++ << endl;
    }

    // (ordered) set and multiset; (map and multimap)
    set<int> s;
    set<int> s1{"red", "blue", "green"}; // {} is list initialization for containers, which is preferred !!!
    // set<int> s1( {"red", "blue", "green"} ); // also correct but allows parsing ambiguities !!!

    int ss = s.size();
    if (s.empty()) {}

    auto it = s.begin();
    auto it = s.end();
    auto it = s.rbegin();
    auto it = s.rend();

    if (s.find(5) != s.end() || s.count(5) == 1) {}

    s.insert("orange");
    s.insert(v.begin(), v.end());
    s.insert( {"orange", "purple"} ); // initializer list
    s.erase("blue");
    s.erase(s.begin()+2);
    s.erase(s.begin()+2, s.end());

    s.swap(s2);

    unordered_multiset<int> ms( { 2, 3, 2, 4, 2} );
    int count = ms.count(2);
    auto it = ms.lower_bound(3);
    auto it = ms.upper_bound(4);
    auto myrange = ms.equal_range(2);
    while (myrange.first != myrange.second) {
        cout << *myrange.first++ << endl;
    }

    // pair (<utility>) and tuple (<tuple>)
    pair <string, double> p1 ("tomatoes", 2.30);
    p1.first;
    p1.second;

    tuple<int,char> foo (10, 'x');
    auto bar = make_tuple ("test", 3.1, 14, 'y');

    get<2>(bar) = 100;                          // access element

    int myint; char mychar;
    tie (myint, mychar) = foo;                  // unpack elements
    tie (ignore, ignore, myint, mychar) = bar;  // unpack (with ignore)

    //// data structure
    // tree
    struct TreeNode {
        int val;
        TreeNode* left;
        TreeNode* right;
        TreeNode (int v) : val(v), left(nullptr), right(nullptr) {}
    };

    // trie
    class TrieNode {
    public:
        // vector<TrieNode*> next;
        bool isEnd;
        vector<shared_ptr<TrieNode>> next;

        TrieNode() : isEnd(false), next(26, nullptr) {}
    };
    class Trie {
        // TrieNode* root;
        shared_ptr<TrieNode> root;
    public:
        /** Initialize your data structure here. */
        Trie() {
            // root = new TrieNode();
            root = make_shared<TrieNode>();
        }

        /** Inserts a word into the trie. */
        void insert(string word) {
            // TrieNode* cur = root;
            shared_ptr<TrieNode> cur = root;
            for (char c: word) {
                if (cur->next[c - 'a'] == nullptr) {
                    // cur->next[c - 'a'] = new TrieNode();
                    cur->next[c - 'a'] = make_shared<TrieNode>();
                }
                cur = cur->next[c - 'a'];
            }
            cur->isEnd = true;
            return;
        }

        /** Returns if the word is in the trie. */
        bool search(string word) {
            // TrieNode* cur = root;
            shared_ptr<TrieNode> cur = root;
            for (char c : word) {
                if (cur->next[c - 'a'] == nullptr) {
                    return false;
                } else {
                    cur = cur->next[c - 'a'];
                }
            }
            return cur->isEnd;
        }

        /** Returns if there is any word in the trie that starts with the given prefix. */
        bool startsWith(string prefix) {
            // TrieNode* cur = root;
            shared_ptr<TrieNode> cur = root;
            for (char c : prefix) {
                if (cur->next[c - 'a'] == nullptr) {
                    return false;
                } else {
                    cur = cur->next[c - 'a'];
                }
            }
            return true;
        }
    };

    // single-linked list
    struct ListNode {
        int val;
        ListNode* next;
        ListNode (int v) : val(v), next(nullptr) {};
    };

    // double-linked list
    struct ListNode {
        int val;
        ListNode* prev;
        ListNode* next;
        ListNode (int v) : val(v), prev(nullptr), next(nullptr) {};
    };

    // graph node and graph (adjacency list)
    struct GraphNode {

        int val;
        // string str;
        vector<GraphNode*> neighbors;
        // vector<int> neighbors;
        // vector<string> neighbors;
        // vector<pair<string, int>> neighbors; // weighted edges
        // unordered_set<int> neighbors;
        // unordered_set<string> neighbors;
        // unordered_set<pair<string, int>> neighbors; // weighted edges

        GraphNode() {
            val = 0;
            neighbors = vector<GraphNode*>();
        }

        GraphNode(int _v) {
            val = _v;
            neighbors = vector<GraphNode*>();
        }

        GraphNode(int _v, vector<GraphNode*> _ns) {
            val = _v;
            neighbors = _ns;
        }
    };

    // ref: https://www.redblobgames.com/pathfinding/a-star/implementation.cpp
    struct Graph {
        unordered_map<char, vector<char>> edges;
        // unordered_map<int, vector<int>> edges;
        // ... other types
        vector<char> neighbors (char val) {
            return edges[val];
        }
    };
    Graph g {
        {
            { 'A', {'B'} },
            { 'B', {'C', 'D'} },
            { 'C', {'A'} },
            { 'D', {'B'} }
        }
    }; // {} gives direct initialization with explicit constructor; whereas ={} gives copy initialization !!!!!

    //// algorithm

    // STL
    transform(a.begin(), a.end(), a.begin(), toupper); // #include<algorithm>
    auto it = find(a.begin(), a.end(), "str"); // #include<algorithm>
    reverse(v.begin(), v.end()); // #include<algorithm>
    sort(v.begin(), v.end()); // #include<algorithm>
    sort(v.begin(), v.end(), boolFuncLessThan); // #include<algorithm> asc
    sort(v.begin(), v.end(), [](point& a, point& b) {return a.y < b.y;});
    sort(v.begin(), v.end(), boolFuncGreaterThan); // #include<algorithm> desc
    auto it = remove(v.begin(), v.end(), 13); // #include<algorithm>
    int count = count(v.begin(), v.end(), 13); // #include<algorithm>
    int count_if = count_if(v.begin(), v.end(), boolFunc); // #include<algorithm>

    // split string
    // ref: https://doc.bccnsoft.com/docs/cppreference_en/cppsstream/all.html

    // #include <boost/algorithm/string.hpp>
    // string s = "hello world";
    // vector<string> res;
    // boost::split(res, s, [](char c){return c == ' ';});

    istringstream iss(s); // or stringstream
    // or ss << s;
    // or ss.str(s);
    vector<string> res;
    string token;
    char delimiter = ' ';
    while(getline(ss, token, delimiter)) {
        res.push_back(token);
    }

    // join strings
    ostringstream oss;
    if (begin != end) ss << *begin++;
    while (begin != end) {
        ss << delimiter;
        ss << *begin++;
    }
    ss << concluder;
    string joined_str = ss.str();

    // stringstream peek and EOF (empty stringstream is a special case!!!)
    stringstream ss("");
    ss.peek(); // make empty ss reach EOF !!!
    if (ss.eof()) {} // true; false without peek() call above

    // binary search
    int target = 4;
    vector<int> arr{1, 2, 3, 4, 5};
    int l = 0;
    int r = arr.size()-1;
    while (l < r) { // or l <= r
        int mid = l + (r-l) / 2;
        if (target == arr[mid]) {
            return mid;
        } else if (target > arr[mid]) {
            l = mid + 1;
        } else {
            r = mid; // or r = mid - 1
        }
    }

    // Depth first seach (DFS)
    void dfs_iterative (vector<vector<int>> graph, int start) {
        stack<int> st{start};
        unordered_set<int> visited{start};
        while (!st.empty()) {
            int cur = st.top();
            st.pop();
            for (auto& n : graph[cur]) {
                if (visited.find(n) == visited.end()) {
                    visited.insert(n);
                    st.push(n);
                }
            }
        }
    }
    void dfs_recursive (vector<vector<int>> graph, int start, unordered_set<int> visited) {
        visited.insert(start);
        for (auto& n : graph[cur]) {
            if (visited.find(n) == visited.end()) {
                dfs_recursive(graph, n, visited);
            }
        }
    }

    // Breadth first seach (BFS)
    void bfs (vector<vector<int>> graph, int start, int target) {
        queue<pair<int, int>> q; // depth (cost), node_val
        q.emplace(0, start);
        unordered_set<int> visited{start};
        while (!q.empty()) {
            int cur_depth = q.front().first;
            int cur = q.front().second;
            q.pop();
            if (cur == target) return cur_depth; // shortest path to target
            for (auto& n : graph[cur]) {
                if (visited.find(n) == visited.end()) {
                    visited.insert(n);
                    q.emplace(cur_depth + 1, n);
                }
            }
        }
    }

    // Dijkstra's algorithm
    void dijksta (vector<vector<pair<int, int>>> graph, int start, int target) {
        // Min-Heap
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq; // cost, node
        pq.emplace(0, start);
        while (!pq.empty()) {
            int cur_cost = pq.top().first;
            int cur = pq.top().second;
            pq.pop();
            if (cur == target) return cur_cost; // shortest path to target
            for (auto& n : graph[cur]) {
                int n_weight = n.first;
                int n_node = n.second;
                pq.emplace(cur_cost + n_weight, n_node);
            }
        }
    }

    // A* algorithm
    inline int heuristic (int& a, int& b) {
        return abs(a - b);
    }
    void a_star (vector<vector<pair<int, int>>> graph, int start, int target) {
        // Min-Heap
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq; // cost, node
        pq.emplace(0, start);

        unordered_map<int, int> prev; // node, node (prev)
        unordered_map<int, int> cost; // node, cost

        while (!pq.empty()) {
            int cur_cost = pq.top().first;
            int cur = pq.top().second;
            pq.pop();
            if (cur == target) return cur_cost; // shortest path to target
            for (auto& n : graph[cur]) {
                int n_weight = n.first;
                int n_node = n.second;
                int new_cost = cur_cost + n_weight;
                if (cost.find(n_node) == cost.end() || new_cost < cost[n_node]) {
                    cost[n_node] = new_cost;
                    pq.emplace(new_cost + heuristics(n_node, target), n_node);
                    prev[n_node] = cur;
                }
            }
        }
    }

    // Topological sort (In-degree; Adjacency matrix - Adjacency list)
    int n = 10;
    vector<pair<int, int>> prerequisites;
    vector<vector<int>> graph(n); // adjacency list
    vector<int> in_degree(n, 0);
    vector<int> res;

    // construct graph from edges (adjacency matrix) and compute in-degrees
    for (auto &p: prerequisites) {
        graph[p.second].push_back(p.first);
        in_degree[p.first]++;
    }

    queue<int> q;
    for (int i = 0; i < n; i++) {
        if (in_degree[i] == 0) {
            q.push(i);
            res.push_back(i);
        }
    }

    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        for (auto& n: graph[cur]) {
            in_degree[n]--;
            if (in_degree[n] == 0) {
                q.push(n);
                res.push_back(n);
            }
        }
    }

    // union-find
    // - canonical implementation of Weighted Union-Find with path compression
    //     - weighted: size of each component
    //     - path compression: update nodes' direct parent as root
    // - Complexity
    //     - UnionFind(): O(N)
    //     - find_root(): nearly 1 (amortized)
    //     - union_weighted(): nearly 1 (amortized)
    //     - connected(): nearly 1 (amortized)
    //     - count(): O(1)
    // - ref
    //     - https://algs4.cs.princeton.edu/15uf/WeightedQuickUnionPathCompressionUF.java.html
    //     - https://www.hackerearth.com/practice/notes/disjoint-set-union-union-find/
    class UnionFind {
        int count;
        vector<int> parent;
        vector<int> size;
    public:
        UnionFind(int N) : count(N), parent(N), size(N, 1) {
            for (int i=0; i < N; i++) {
                parent[i] = i;
            }
        }
        void union(int p, int q) {
            int rp = find_root(p);
            int rq = find_root(q);
            if (rp == rq) return;

            if (size[p] < size[q]) {
                parent[p] = rq;
                size[q] += size[p];
            } else {
                parent[q] = rp;
                size[p] += size[q];
            }
            count--;
        }

        int find_root(int p) {
            int root = p;
            while(parent[p] != p) {
                p = parent[parent[p]]; // log(N)
            }
            // update all parents with root value
            while(p != root) {
                int tmp = parent[p];
                parent[p] = root;
                p = tmp;
            }
            return root;
        }

        bool connected(int p, int q) {
            return find_root(p) == find_root(q);
        }

        int count() {
            return count;
        }
    };

    // copy and move (lvalue, rvalue)
    // ref: https://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html
    // lvalue: provides a (semi)permanent piece of memory
    // rvalue: a temporary object
    // copy (without std::move()) is more time-expensive
    std::move(lvalue); // return rvalue

    // smart pointers
    shared_ptr<int> sp; // increment counter when sp1 = sp
    unique_ptr<int> up; // always do std::move() when up1 = up

    // inline function
    // compiler will insert the body of the function in that location as opposed to making a function call
    // only for small functions that are used frequently, not for large functions
    inline void swap(int & a, int & b) // inline is a compiler directive
    {
        // code needs to be recompiled when the function is changed
        int temp = a;
        a = b;
        b = temp;
    }

    // lambda function
    // ref: https://lospi.net/c++/developing/software/visual%20studio/2015/03/11/lambdas-and-cpp11.html
    std::transform(
        words.begin(), words.end(),
        result.begin(),
        [&](int x){ return x + a - b; } // all passed by reference
        //[=](int x){ return x + a - b; } // all passed by value
        //[a, &b](int x){ return x + a - b; } // a passed by value, b passed by reference
    );

    // exception handling
    // * Common exceptions: http://stdcxx.apache.org/doc/stdlibref/2-3.html
    try {
        bitset<5> bs(string("01234")); // "234" are invalid
        // throw invalid_argument( "Invalid argument!" );
    } catch( const invalid_argument& e ) {
        cerr << "Invalid Argument: " << e.what() << endl;
    }
}


/** lexical_cast_nonfinite_facets.cpp
*
* Copyright (c) 2011 Paul A. Bristow
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt
* or copy at http://www.boost.org/LICENSE_1_0.txt)
*
* This very simple program illustrates how to use the
* `boost/math/nonfinite_num_facets.hpp' with lexical cast
* to obtain C99 representation of infinity and NaN.
* This example is from the original Floating Point  Utilities contribution by Johan Rade.
* Floating Point Utility library has been accepted into Boost,
* but the utilities are incorporated into Boost.Math library.
*
\file

\brief A very simple example of using lexical cast with
non_finite_num facet for C99 standard output of infinity and NaN.

\detail This example shows how to create a C99 non-finite locale,
and imbue input and output streams with the non_finite_num put and get facets.
This allows lexical_cast output and input of infinity and NaN in a Standard portable way,
This permits 'loop-back' of output back into input (and portably across different system too).

*/

#include <boost/math/special_functions/nonfinite_num_facets.hpp>
using boost::math::nonfinite_num_get;
using boost::math::nonfinite_num_put;

#include <boost/lexical_cast.hpp>
using boost::lexical_cast;

#include <iostream>
using std::cout;
using std::endl;
using std::cerr;

#include <iomanip>
using std::setw;
using std::left;
using std::right;
using std::internal;

#include <string>
using std::string;

#include <sstream>
using std::istringstream;

#include <limits>
using std::numeric_limits;

#include <locale>
using std::locale;

#include <boost/assert.hpp>

int main ()
{
  std::cout << "lexical_cast example (NOT using finite_num_facet)." << std::endl;

  if((std::numeric_limits<double>::has_infinity == false) || (std::numeric_limits<double>::infinity() == 0))
  {
    std::cout << "Infinity not supported on this platform." << std::endl;
    return 0;
  }

  if((std::numeric_limits<double>::has_quiet_NaN == false) || (std::numeric_limits<double>::quiet_NaN() == 0))
  {
    std::cout << "NaN not supported on this platform." << std::endl;
    return 0;
  }

  // Some tests that are expected to fail on some platforms.
  // (But these tests are expected to pass using non_finite num_put and num_get facets).

  // Use the current 'native' default locale.
  std::locale default_locale (std::locale::classic ()); // Note the currrent (default C) locale.

  // Create plus and minus infinity.
  double plus_infinity = +std::numeric_limits<double>::infinity();
  double minus_infinity = -std::numeric_limits<double>::infinity();

  // and create a NaN (NotANumber).
  double NaN = +std::numeric_limits<double>::quiet_NaN ();

  // Output the nonfinite values using the current (default C) locale.
  // The default representations differ from system to system,
  // for example, using Microsoft compilers, 1.#INF, -1.#INF, and 1.#QNAN.
  cout << "Using default locale" << endl;
  cout << "+std::numeric_limits<double>::infinity() = " << plus_infinity << endl;
  cout << "-std::numeric_limits<double>::infinity() = " << minus_infinity << endl;
  cout << "+std::numeric_limits<double>::quiet_NaN () = " << NaN << endl;

  // Checks below are expected to fail on some platforms!

  // Now try some 'round-tripping', 'reading' "inf"
  double x = boost::lexical_cast<double>("inf");
  // and check we get a floating-point infinity.
  BOOST_ASSERT(x == std::numeric_limits<double>::infinity());

  // Check we can convert the other way from floating-point infinity,
  string s = boost::lexical_cast<string>(numeric_limits<double>::infinity());
  // to a C99 string representation as "inf".
  BOOST_ASSERT(s == "inf");

  // Finally try full 'round-tripping' (in both directions):
  BOOST_ASSERT(lexical_cast<double>(lexical_cast<string>(numeric_limits<double>::infinity()))
    == numeric_limits<double>::infinity());
  BOOST_ASSERT(lexical_cast<string>(lexical_cast<double>("inf")) == "inf");

  return 0;
} // int main()

/*

Output:

from MSVC 10, fails (as expected)

  lexical_cast_native.vcxproj -> J:\Cpp\fp_facet\fp_facet\Debug\lexical_cast_native.exe
  lexical_cast example (NOT using finite_num_facet).
  Using default locale
  +std::numeric_limits<double>::infinity() = 1.#INF
  -std::numeric_limits<double>::infinity() = -1.#INF
  +std::numeric_limits<double>::quiet_NaN () = 1.#QNAN
C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\Microsoft.CppCommon.targets(183,5): error MSB3073: The command ""J:\Cpp\fp_facet\fp_facet\Debug\lexical_cast_native.exe"
C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\Microsoft.CppCommon.targets(183,5): error MSB3073: :VCEnd" exited with code 3.

*/


/**
 * @file
 * @author University of Warwick
 * @version 1.0
 *
 * @section LICENSE
 *
 * @section DESCRIPTION
 *
 * Tests for the MeshHDF5Source Class
 */

#define BOOST_TEST_MODULE MeshHDF5Source
#include <boost/test/unit_test.hpp>
#include <boost/test/output_test_stream.hpp>

#include <stdexcept>
#include <string>

#include "MeshHDF5Source.h"
#include "EuclideanPoint.h"
#include "EuclideanVector.h"

using namespace cupcfd::geometry::mesh;

namespace utf = boost::unit_test;

// Setup
BOOST_AUTO_TEST_CASE(setup)
{
    int argc = boost::unit_test::framework::master_test_suite().argc;
    char ** argv = boost::unit_test::framework::master_test_suite().argv;

    MPI_Init(&argc, &argv);
}

// === Constructors ===
// Test 1:
BOOST_AUTO_TEST_CASE(constructor_test1)
{

}

// === getAttribute int ===

// === getAttribute float ===

// === getAttribute double ===

// === getCellLabels ===
BOOST_AUTO_TEST_CASE(getCellLabels)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	int indices[4] = {0, 1, 2, 3};

	int cellLabels[4];
	int cellLabelsCmp[4] = {1, 2, 3, 4};

	status = file.getCellLabels(cellLabels, 4, indices, 4);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	BOOST_CHECK_EQUAL_COLLECTIONS(cellLabels, cellLabels + 4, cellLabelsCmp, cellLabelsCmp + 4);
}

// === getFaceLabels ===

// === getVertexLabels ===

// === getBoundaryLabels ===

// === getRegionLabels ===

// === getFaceArea ===

// === getFaceNVertices ===
BOOST_AUTO_TEST_CASE(getFaceNVertices_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	int faceLabels[15] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

	int nFaceVertices[15];
	int nFaceVerticesCmp[15] = {4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 3, 4, 4, 4};

	status = file.getFaceNVertices(nFaceVertices, 15, faceLabels, 15);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	BOOST_CHECK_EQUAL_COLLECTIONS(nFaceVertices, nFaceVertices + 15, nFaceVerticesCmp, nFaceVerticesCmp + 15);
}

// === getFaceVerticesLabelsCSR ===
// Test 1: Partial Read 1
BOOST_AUTO_TEST_CASE(getFaceVerticesLabelsCSR_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	int faceLabels[15] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

	int csrIndices[16];
	int csrData[58];

	int csrIndicesCmp[16] = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 39, 43, 46, 50, 54, 58};

	int csrDataCmp[58] = {1, 5, 14, 11,
					  1, 2, 6, 5,
					  1, 2, 12, 11,
					  5, 6, 15, 14,
					  11, 12, 15, 14,
					  2, 3, 7, 6,
					  2, 3, 13, 12,
					  12, 13, 16, 15,
					  3, 4, 8, 7,
					  3, 4, 13,
					  4, 8, 16, 13,
					  7, 8, 16,
					  7, 10, 18, 16,
					  9, 10, 18, 17,
					  6, 9, 17, 15
	};

	status = file.getFaceVerticesLabelsCSR(csrIndices, 16, csrData, 58, faceLabels, 15);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	BOOST_CHECK_EQUAL_COLLECTIONS(csrIndices, csrIndices + 16, csrIndicesCmp, csrIndicesCmp + 16);
	BOOST_CHECK_EQUAL_COLLECTIONS(csrData, csrData + 58, csrDataCmp, csrDataCmp + 58);
}

// Test 2: Partial Read 2
BOOST_AUTO_TEST_CASE(getFaceVerticesLabelsCSR_test2)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	int faceLabels[12] = {15, 1, 2, 4, 5, 6, 8, 10, 11, 13, 14, 7};

	int csrIndices[13];
	int csrData[47];

	int csrIndicesCmp[16] = {0, 4, 8, 12, 16, 20, 24, 28, 31, 35, 39, 43, 47};

	int csrDataCmp[47] = { 6, 9, 17, 15,
					  1, 5, 14, 11,
					  1, 2, 6, 5,
					  5, 6, 15, 14,
					  11, 12, 15, 14,
					  2, 3, 7, 6,
					  12, 13, 16, 15,
					  3, 4, 13,
					  4, 8, 16, 13,
					  7, 10, 18, 16,
					  9, 10, 18, 17,
					  2, 3, 13, 12
	};

	status = file.getFaceVerticesLabelsCSR(csrIndices, 13, csrData, 48, faceLabels, 12);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	BOOST_CHECK_EQUAL_COLLECTIONS(csrIndices, csrIndices + 13, csrIndicesCmp, csrIndicesCmp + 13);
	BOOST_CHECK_EQUAL_COLLECTIONS(csrData, csrData + 47, csrDataCmp, csrDataCmp + 47);
}

// === getBoundaryVerticesLabelsCSR ===
// Partial Read 1
BOOST_AUTO_TEST_CASE(getBoundaryVerticesLabelsCSR_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	int boundaryLabels[15] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

	int csrIndices[16];
	int csrData[58];

	int csrIndicesCmp[16] = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 39, 43, 47, 50, 54, 58};

	int csrDataCmp[58] = {1, 5, 14, 11,
					  1, 2, 6, 5,
					  1, 2, 12, 11,
					  5, 6, 15, 14,
					  11, 12, 15, 14,
					  2, 3, 7, 6,
					  2, 3, 13, 12,
					  12, 13, 16, 15,
					  3, 4, 8, 7,
					  3, 4, 13,
					  4, 8, 16, 13,
					  7, 10, 18, 16,
					  7, 8, 16,
					  9, 10, 18, 17,
					  15, 16, 18, 17
	};

	status = file.getBoundaryVerticesLabelsCSR(csrIndices, 16, csrData, 58, boundaryLabels, 15);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	BOOST_CHECK_EQUAL_COLLECTIONS(csrIndices, csrIndices + 16, csrIndicesCmp, csrIndicesCmp + 16);
	BOOST_CHECK_EQUAL_COLLECTIONS(csrData, csrData + 58, csrDataCmp, csrDataCmp + 58);
}

// === getCellCount ===
// Test 1:
BOOST_AUTO_TEST_CASE(getCellCount_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	int cells;
	cupcfd::error::eCodes status;

	status = file.getCellCount(&cells);
	BOOST_CHECK_EQUAL(cells, 4);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
}

// === getFaceCount ===
// Test 1:
BOOST_AUTO_TEST_CASE(getFaceCount_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	int faces;
	cupcfd::error::eCodes status;

	status = file.getFaceCount(&faces);
	BOOST_CHECK_EQUAL(faces, 20);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
}

// === getBoundaryCount ===
// Test 1:
BOOST_AUTO_TEST_CASE(getBoundaryCount_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	int boundaries;
	cupcfd::error::eCodes status;

	status = file.getBoundaryCount(&boundaries);
	BOOST_CHECK_EQUAL(boundaries, 17);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
}

// === getRegionCount ===
// Test 1:
BOOST_AUTO_TEST_CASE(getRegionCount_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	int regions;
	cupcfd::error::eCodes status;

	status = file.getRegionCount(&regions);
	BOOST_CHECK_EQUAL(regions, 1);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
}

// === getVertexCount ===
// Test 1:
BOOST_AUTO_TEST_CASE(getVertexCount_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	int vertices;
	cupcfd::error::eCodes status;

	status = file.getVertexCount(&vertices);
	BOOST_CHECK_EQUAL(vertices, 18);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
}

// === getMaxFaceCount ===
// Test 1:
BOOST_AUTO_TEST_CASE(getMaxFaceCount_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	int maxFaceCount;
	cupcfd::error::eCodes status;

	status = file.getMaxFaceCount(&maxFaceCount);
	BOOST_CHECK_EQUAL(maxFaceCount, 6);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
}

// === getMaxVertexCount ===
BOOST_AUTO_TEST_CASE(getMaxVertexCount_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	int maxVertexCount;
	cupcfd::error::eCodes status;

	status = file.getMaxVertexCount(&maxVertexCount);
	BOOST_CHECK_EQUAL(maxVertexCount, 4);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
}

// === getCellNFaces ===
BOOST_AUTO_TEST_CASE(getCellNFaces_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	// Cell Labels - Indexed from base of 1 for this format
	int nLabels[3] = {1,3,4};

	int nFaces[3];
	int nFacesCmp[3] = {6, 5, 6};

	status = file.getCellNFaces(nFaces, 3, nLabels, 3);
	BOOST_CHECK_EQUAL_COLLECTIONS(nFaces, nFaces + 3, nFacesCmp, nFacesCmp + 3);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
}

// === getCellVolume ===
BOOST_AUTO_TEST_CASE(getCellVolume_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	int nLabels[3] = {1,3,4};

	double vol[3];
	int nVol = 3;
	double volCmp[4] = {1.0, 3.0, 4.0};

	status = file.getCellVolume(vol, nVol, nLabels, 3);
	BOOST_CHECK_EQUAL_COLLECTIONS(vol, vol + 4, volCmp, volCmp + 4);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
}

// === getCellCenter ===
BOOST_AUTO_TEST_CASE(getCellCenter_test1)
{

}

// === getCellFaceLabels ===
BOOST_AUTO_TEST_CASE(getCellFaceLabels_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	int cellLabels[3] = {1, 3, 4};

	int faceLabelsInd[4];
	int faceLabelsIndCmp[4] = {0, 6, 11, 17};

	int faceLabelsData[17];
	int faceLabelsDataCmp[17] = {1, 2, 3, 4, 5, 18, 9, 10, 11, 12, 19, 13, 14, 15, 16, 17, 20};

	status = file.getCellFaceLabels(faceLabelsInd, 4, faceLabelsData, 17, cellLabels, 3);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	BOOST_CHECK_EQUAL_COLLECTIONS(faceLabelsInd, faceLabelsInd + 4, faceLabelsIndCmp, faceLabelsIndCmp + 4);
	BOOST_CHECK_EQUAL_COLLECTIONS(faceLabelsData, faceLabelsData + 17, faceLabelsDataCmp, faceLabelsDataCmp + 17);
}

// === getFaceIsBoundary ===
BOOST_AUTO_TEST_CASE(getFaceIsBoundary_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	int faceLabels[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
	bool isBoundary[20];
	bool isBoundaryCmp[20] = {true, true, true, true, true, true, true, true, true, true,
							 true, true, true, true, true, true, true, false, false, false};

	status = file.getFaceIsBoundary(isBoundary, 20, faceLabels, 20);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	BOOST_CHECK_EQUAL_COLLECTIONS(isBoundary, isBoundary + 20, isBoundaryCmp, isBoundaryCmp + 20);
}

// === getFaceBoundaryLabels ===
// Test 1: Get Correct Boundary Labels for all valid faces
BOOST_AUTO_TEST_CASE(getFaceBoundaryLabels_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	int faceLabels[17] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};

	int boundaryLabels[17];
	int boundaryLabelsCmp[17] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 12, 14, 17, 16, 15};

	status = file.getFaceBoundaryLabels(boundaryLabels, 17, faceLabels, 17);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	BOOST_CHECK_EQUAL_COLLECTIONS(boundaryLabels, boundaryLabels + 17, boundaryLabelsCmp, boundaryLabelsCmp + 17);
}

// Test 2: Error: One or more faces are not boundary faces
BOOST_AUTO_TEST_CASE(getFaceBoundaryLabels_test2)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	int faceLabels[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
	int boundaryLabels[20];

	status = file.getFaceBoundaryLabels(boundaryLabels, 20, faceLabels, 20);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_ERROR);
}

// === getFaceCell1Labels ===
BOOST_AUTO_TEST_CASE(getFaceCell1Labels_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	int faceLabels[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
	int cellLabels[20];
	int cellLabelsCmp[20] = {1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 1, 2, 2};

	status = file.getFaceCell1Labels(cellLabels, 20, faceLabels, 20);
	BOOST_CHECK_EQUAL_COLLECTIONS(cellLabels, cellLabels + 20, cellLabelsCmp, cellLabelsCmp + 20);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
}

// === getFaceCell2Labels ===
BOOST_AUTO_TEST_CASE(getFaceCell2Labels_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	int faceLabels[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
	int cellLabels[20];
	int cellLabelsCmp[20] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 3, 4};

	status = file.getFaceCell2Labels(cellLabels, 20, faceLabels, 20);
	BOOST_CHECK_EQUAL_COLLECTIONS(cellLabels, cellLabels + 20, cellLabelsCmp, cellLabelsCmp + 20);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
}

// === getFaceLambda ===
BOOST_AUTO_TEST_CASE(getFaceLambda2_test1)
{
	/*
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	double faceLambda[20];
	double faceLambdaCmp[20] = {12.0, 3.2, 0.53, 634.0, -431.3, 23.351, 54.6352, 4246.2,
							 421.4, 2.53, 351.4, 23.0, 41.0, 0.0, 426.2, 42111.5, 2462.2,
							 14.3, 15.0, -252.2};

	status = file.getFaceLambda(faceLambda, 20);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	for(int i = 0; i < 20; i++)
	{
		// Allow some leeway for floating point tolerance
		BOOST_TEST(faceLambda[i] == faceLambdaCmp[i]);
	}
	*/
}

// === getFaceNormal ===
BOOST_AUTO_TEST_CASE(getFaceNormal_test1)
{
	/*
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	cupcfd::geometry::euclidean::EuclideanVector<double,3> normal[20];
	cupcfd::geometry::euclidean::EuclideanVector<double,3> normalCmp[20];
	double normalCmpX[20] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0};
	double normalCmpY[20] = {100.0, 101.0, 102.0, 103.0, 104.0, 105.5, 106.0, 107.0, 108.0, 109.0, 110.0, 111.0, 112.3, 113.0, 114.0, 115.0, 116.0, 117.0, 118.0, 119.0};
	double normalCmpZ[20] = {200.8, 201.0, 202.0, 203.0, 204.5, 205.0, 206.0, 207.0, 208.0, 209.0, 210.0, 211.1, 212.0, 213.0, 214.0, 215.0, 216.0, 217.0, 218.9, 219.0};

	for(int i = 0; i < 20; i++)
	{
		normalCmp[i] = cupcfd::geometry::euclidean::EuclideanVector<double,3>(normalCmpX[i], normalCmpY[i], normalCmpZ[i]);
	}

	status = file.getFaceNormal(normal, 20);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	for(int i = 0; i < 20; i++)
	{
		// Boost does not handle comparing EuclieanPoints in it's macro
		BOOST_TEST(normal[i].cmp[0] == normalCmp[i].cmp[0]);
		BOOST_TEST(normal[i].cmp[1] == normalCmp[i].cmp[1]);
		BOOST_TEST(normal[i].cmp[2] == normalCmp[i].cmp[2]);
	}
	*/
}

// === getFaceCenter ===
BOOST_AUTO_TEST_CASE(getFaceCenter_test1)
{

}

// === getVertexCoords ===
BOOST_AUTO_TEST_CASE(getVertexCoords_test1)
{

}

// === getBoundaryFaceLabels ===
BOOST_AUTO_TEST_CASE(getBoundaryFaceLabels_test1)
{

}

// === getBoundaryNVertices ===
BOOST_AUTO_TEST_CASE(getBoundaryNVertices_test1)
{
	cupcfd::comm::Communicator comm(MPI_COMM_WORLD);

	std::string filePath = "../tests/geometry/mesh/data/MeshHDF5.hdf5";
	MeshHDF5Source<int, double> file(filePath);
	cupcfd::error::eCodes status;

	// Note: Face 13 - > Boundary 12 and Face 12 -> Boundary 13.
	// This is to test that it is boundary labels being used to do lookup rather
	// than face labels since this will give values in different order
	int boundaryLabels[17] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};

	int nVertices[17];
	int nVerticesCmp[17] = {4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 3, 4, 4, 4, 4};

	status = file.getBoundaryNVertices(nVertices, 15, boundaryLabels, 15);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	BOOST_CHECK_EQUAL_COLLECTIONS(nVertices, nVertices + 15, nVerticesCmp, nVerticesCmp + 15);
}

// === getBoundaryRegionLabels ===
// Test 1:
BOOST_AUTO_TEST_CASE(getBoundaryRegionLabels_test1)
{

}

// === getBoundaryDistance ===
// Test 1:
BOOST_AUTO_TEST_CASE(getBoundaryDistance_test1)
{

}

// === getRegionName ===
// Test 1:
BOOST_AUTO_TEST_CASE(getRegionName_test1)
{

}

// === buildDistributedAdjacencyList1 ===
// Test: Build a arbitrary graph based on manually assigned cells
BOOST_AUTO_TEST_CASE(buildDistributedAdjacencyList1_test1)
{

}

// === buildDistributedAdjacencyList2 ===
// Test: Build a graph that has a naive cell allocation
BOOST_AUTO_TEST_CASE(buildDistributedAdjacencyList2_test1)
{

}

BOOST_AUTO_TEST_CASE(cleanup)
{
    MPI_Finalize();
}


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#define NT2_UNIT_MODULE "nt2 complex.arithmetic toolbox - arg/simd Mode"

//////////////////////////////////////////////////////////////////////////////
// unit test behavior of boost.simd.arithmetic components in simd mode
//////////////////////////////////////////////////////////////////////////////
/// created by jt the 28/11/2010
///

#include <nt2/include/functions/unary_minus.hpp>
#include <nt2/include/functions/splat.hpp>
#include <boost/simd/include/native.hpp>
#include <nt2/sdk/functor/meta/call.hpp>
#include <nt2/sdk/meta/scalar_of.hpp>
#include <nt2/include/constants/inf.hpp>
#include <nt2/include/constants/minf.hpp>
#include <nt2/include/constants/nan.hpp>
#include <nt2/include/constants/one.hpp>
#include <nt2/include/constants/mone.hpp>
#include <nt2/include/constants/zero.hpp>
#include <nt2/sdk/complex/complex.hpp>
#include <nt2/sdk/complex/dry.hpp>
#include <nt2/sdk/complex/imaginary.hpp>
#include <nt2/sdk/complex/meta/as_imaginary.hpp>
#include <nt2/sdk/complex/meta/as_dry.hpp>

#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/relation.hpp>

NT2_TEST_CASE_TPL ( abs_cplx__1_0,  BOOST_SIMD_SIMD_REAL_TYPES)
{
  using boost::simd::native;
  typedef NT2_SIMD_DEFAULT_EXTENSION  ext_t;
  typedef std::complex<T>                              cT;
  typedef native<T ,ext_t>                             vT;
  typedef native<cT ,ext_t>                           vcT;
  typedef typename nt2::meta::as_imaginary<T>::type   ciT;
  typedef native<ciT ,ext_t>                         vciT;
  typedef typename nt2::meta::as_dry<T>::type          dT;
  typedef native<dT ,ext_t>                           vdT;

  // specific values tests
  {
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::splat<vcT>(cT(T(1.1),T(1.6))))[0], cT(T(-1.1),T(-1.6)));
  }
  {
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Inf<vcT>())[0], nt2::Minf<vcT>()[0]);
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Minf<vcT>())[0], nt2::Inf<vcT>()[0]);
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Mone<vcT>())[0], nt2::One<vcT>()[0]);
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Nan<vcT>())[0], nt2::Nan<vcT>()[0]);
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::One<vcT>())[0], nt2::Mone<vcT>()[0]);
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Zero<vcT>())[0], nt2::Zero<vcT>()[0]);
  }
  {
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::splat<vciT>(ciT(T(-1.1))))[0], ciT(T( 1.1)) );
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::splat<vciT>(ciT(T(1.1))))[0],ciT(T(-1.1)));
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Inf<vciT>())[0], nt2::Minf<ciT>());
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Minf<vciT>())[0],nt2::Inf<ciT>());
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Mone<vciT>())[0],nt2::One<ciT>());
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Nan<vciT>())[0], nt2::Nan<ciT>());
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::One<vciT>())[0], nt2::Mone<ciT>());
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Zero<vciT>())[0], nt2::Zero<ciT>());
  }
  {
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::splat<vdT>(dT(T(-1.1))))[0], dT(T(1.1)));
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::splat<vdT>(dT(T(1.1))))[0],  dT(T(-1.1)));
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Inf<vdT>())[0], nt2::Minf<dT>());
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Minf<vdT>())[0], nt2::Inf<dT>());
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Mone<vdT>())[0], nt2::One<dT>());
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Nan<vdT>())[0], nt2::Nan<dT>());
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::One<vdT>())[0], nt2::Mone<dT>());
    NT2_TEST_EQUAL(nt2::unary_minus(nt2::Zero<vdT>())[0], nt2::Zero<dT>());
  }
} // end of test for floating_


/*!
	@brief Tests the Array class
 */

#include "StdAfx.h"
#include "Array.h"

#include <boost/test/unit_test.hpp>

#ifdef UNIT_TEST

BOOST_AUTO_TEST_SUITE( ArrayTests )

	/** @test begin() method of class Array.
	 */
	BOOST_AUTO_TEST_CASE( begin )
	{
		typedef Array< short > array_type ;
		array_type a ;
		BOOST_CHECK_EQUAL ( a.begin() , a.end() ) ;
		a.resize( 10 ) ;
		std::fill(a.begin(), a.end(), 0) ;
		a(0) = 5 ;
		a(1) = 25 ;
		BOOST_CHECK ( a.begin() != a.end() ) ;

		array_type::iterator pos = a.begin() ;
		BOOST_CHECK_EQUAL( 5 , *pos ) ;

		array_type::const_iterator cpos = a.begin() ;

		std::advance( cpos, 1 ) ;
		BOOST_CHECK_EQUAL( cpos , ++pos ) ;
		BOOST_CHECK_EQUAL( *cpos , *pos ) ;
		BOOST_CHECK_EQUAL( 25, *cpos) ;

		cpos = a.begin() ;
		std::advance( cpos, a.size() ) ;
		BOOST_CHECK_EQUAL( cpos , a.end() ) ;

	}

	/** @test end() method of class Array.
	*/
	BOOST_AUTO_TEST_CASE( end )
	{
		typedef Array< double > array_type ;

		array_type a ;
		BOOST_CHECK_EQUAL ( a.begin() , a.end() ) ;
		a.resize( 10 ) ;
		a.init(0.0) ;
		BOOST_CHECK_EQUAL ( a.begin() + 10 , a.end() ) ;

		a(8) = 5.f ;
		a(9) = 4.f ;

		array_type::iterator pos = a.end() ;
		--pos ;
		BOOST_CHECK( FLOAT_EQ( *pos, a[9] ) ) ;
		--pos ;
		BOOST_CHECK( FLOAT_EQ( *pos, a[8] )) ;
		*pos = 25.f ;
		BOOST_CHECK( FLOAT_EQ( 25.f, a[8] ) ) ;

		array_type::const_iterator cpos = a.end() ;

		--cpos ;
		BOOST_CHECK( FLOAT_EQ( *cpos, a[9] ) ) ;
		--cpos ;
		BOOST_CHECK( FLOAT_EQ( *cpos, a[8] )) ;
		*pos = 25.f ;
		cpos = pos ;
		BOOST_CHECK( FLOAT_EQ( 25.f, *cpos ) ) ;
	 }

	/** @test size() method of class Array.
	*/
	BOOST_AUTO_TEST_CASE( size )
	{
		Array< double > a ;
		BOOST_CHECK_EQUAL ( a.size() , 0u ) ;
		a.resize( 5 ) ;
		BOOST_CHECK_EQUAL ( a.size() , 5u ) ;
	}

	/** @test max_size() method of class Array.
	*/
	BOOST_AUTO_TEST_CASE( max_size )
	{
		Array< double > a ;
		BOOST_CHECK_EQUAL ( a.max_size() , 0u ) ;
		a.resize( 10 ) ;
		BOOST_CHECK_EQUAL ( a.max_size() , 10u ) ;
		a.resize( 5 ) ;
		BOOST_CHECK_EQUAL ( a.max_size() , 10u ) ;
	}

	/** @test clear() method of class Array.
	*/
	BOOST_AUTO_TEST_CASE( clear )
	{
		Array< double > a ;

		a.resize( 10 ) ;
		BOOST_CHECK_EQUAL ( a.size() , 10u ) ;
		BOOST_CHECK_EQUAL ( a.max_size() , 10u ) ;

		a.clear( ) ;
		BOOST_CHECK_EQUAL ( a.size() , 0u ) ;
		BOOST_CHECK_EQUAL ( a.max_size() , 0u ) ;
		BOOST_CHECK_EQUAL ( a.empty() , true ) ;
		BOOST_CHECK_EQUAL ( a.begin() , a.end() ) ;
	}

	/** @test resize() method of class Array.
	*/
	BOOST_AUTO_TEST_CASE( resize )
	{
		Array< long > a(10) ;

		std::fill(a.begin(), a.end(), 5) ;
		a.resize( 5 ) ;
		BOOST_CHECK_EQUAL( 5, (int)a.size()) ;
		BOOST_CHECK_EQUAL( 10, (int)a.max_size()) ;
		BOOST_CHECK_EQUAL( a[3] , 5 ) ;

		a.resize( 20 ) ;
		BOOST_CHECK_EQUAL( 20, (int)a.size()) ;
		BOOST_CHECK_EQUAL( 20, (int)a.max_size()) ;
		BOOST_CHECK_EQUAL( a[0] , 5 ) ;

	}

	/** @test ParensOperator of class Array.
	*/
	BOOST_AUTO_TEST_CASE( ParensOperator )
	{
		typedef Array< short > array_type ;
		array_type a ;

		BOOST_CHECK_EQUAL( 0u, a.max_size()) ;
		a.resize( 10 ) ;

		a(0) = 3 ;
		BOOST_CHECK_EQUAL( a(0) , 3 ) ;

		a(1) = 10 ;
		BOOST_CHECK_EQUAL( a(1) , 10 ) ;
	}
	BOOST_AUTO_TEST_CASE( ParensOperator_throws_on_oob )
	{
		typedef Array< short > array_type ;
		array_type a ;

		BOOST_CHECK_EQUAL( 0u, a.max_size()) ;

		try
		{
			a(2) = 4 ;
			BOOST_FAIL( "Should have thrown when referencing out of bounds index" ) ;
		}
		catch( std::out_of_range& )
		{
			BOOST_CHECK_EQUAL( 0u, a.max_size()) ;
		}
	}
	BOOST_AUTO_TEST_CASE( BracketOperator_throws_on_oob )
	{
		typedef Array< short > array_type ;
		array_type a ;

		BOOST_CHECK_EQUAL( 0u, a.max_size()) ;
		a.resize( 2 ) ;

		try
		{
			const short i = a[2] ;
			i ;
			BOOST_FAIL( "Should have thrown when referencing out of bounds index" ) ;
		}
		catch( std::out_of_range& )
		{
			BOOST_CHECK_EQUAL( 2u, a.max_size()) ;
		}
	}

	/** @test init() method of class Array.
	*/
	BOOST_AUTO_TEST_CASE( init )
	{
		typedef Array< short > array_type ;
		array_type a ;

		a.resize( 15 ) ;
		a.init(35) ;

		FOREACH(short i, a)
		{
			BOOST_CHECK_EQUAL(35, i) ;
		}
	}

	/** @test OperatorEqual method of class Array.
	*/
	BOOST_AUTO_TEST_CASE( OperatorEqual )
	{
		typedef Array< short > array_type ;
		array_type array1, array2 ;

		array1.resize( 15 ) ;
		array1.init(35) ;

		array2 = array1 ;

		BOOST_CHECK_EQUAL( array1.size() , array2.size() ) ;
		BOOST_CHECK_EQUAL( array1.max_size() , array2.max_size() ) ;

		for ( size_t i = 0u ; i < array1.size() ; ++i )
		{
			BOOST_CHECK_EQUAL( array2[i] , array1[i] ) ;
		}
		BOOST_CHECK( array1.val_identity(array2) ) ;
	}

	/** @test implementation of black-box test for clone. */
	BOOST_AUTO_TEST_CASE( clone )
	{
		typedef Array< short > array_type ;
		array_type a, b ;

		a.resize( 15 ) ;
		a.init(35) ;

		a.clone( b ) ;

		BOOST_CHECK(a.val_identity(b)) ;
	}

	/** @test implementation of black-box test for val_identity. */
	BOOST_AUTO_TEST_CASE( val_identity )
	{
		typedef Array< short > array_type ;
		array_type array1, array2 ;

		array1.resize( 15 ) ;
		array1.init(35) ;

		array2 = array1 ;

		BOOST_CHECK_EQUAL( (int)array1.size() , (int)array2.size() ) ;
		BOOST_CHECK_EQUAL( (int)array1.max_size() , (int)array2.max_size() ) ;

		BOOST_CHECK( array1.val_identity(array2) ) ;
		BOOST_CHECK( array2.val_identity(array1) ) ;
	}
	BOOST_AUTO_TEST_CASE( val_identity_self )
	{
		typedef Array< short > array_type ;
		array_type a(5) ;

		a.init(3) ;

		BOOST_CHECK(a.val_identity(a)) ;
	}
	BOOST_AUTO_TEST_CASE( val_identity_false_val )
	{
		typedef Array< short > array_type ;
		array_type a(5), b(5) ;

		a.init(3) ;
		b.init(4) ;

		BOOST_CHECK(! a.val_identity(b)) ;
		BOOST_CHECK(! b.val_identity(a)) ;
	}
	BOOST_AUTO_TEST_CASE( val_identity_false_size )
	{
		typedef Array< short > array_type ;
		array_type a(5), b(7) ;

		a.init(0) ;
		b.init(0) ;

		BOOST_CHECK(! a.val_identity(b)) ;
		BOOST_CHECK(! b.val_identity(a)) ;
	}

	/** @test swap() method of class Array.
	*/
	BOOST_AUTO_TEST_CASE( swap )
	{
		typedef Array< short > array_type ;
		array_type a, b ;

		a.resize(5) ;
		a.init(10) ;

		b.swap(a) ;

		BOOST_CHECK_EQUAL(0u, a.size()) ;
		BOOST_CHECK_EQUAL(5, (int)b.size()) ;

		BOOST_CHECK_EQUAL(0u, a.max_size()) ;
		BOOST_CHECK_EQUAL(5, (int)b.max_size()) ;

		FOREACH(short i, b)
		{
			BOOST_CHECK_EQUAL(10, i) ;
		}
	}

	/** @test implementation of black-box test for Array.
	no requirement for number of elements to be greater than 0.
	*/
	BOOST_AUTO_TEST_CASE( Constructor )
	{
		typedef Array< short > array_type ;
		array_type array1(10) ;
		BOOST_CHECK_EQUAL( array1.size() , 10u ) ;
		BOOST_CHECK_EQUAL( array1.max_size() , 10u ) ;

		array1.init(10) ;

		array_type array2( array1 ) ;
		BOOST_CHECK( array1.val_identity(array2) ) ;
		BOOST_CHECK( array2.val_identity(array1) ) ;
		BOOST_CHECK_EQUAL( array1.size() , array2.size() ) ;
		BOOST_CHECK_EQUAL( array1.max_size() , array2.max_size() ) ;

		array1.resize(5) ;

		array_type array3( array1 ) ;
		BOOST_CHECK( array1.val_identity(array3) ) ;
		BOOST_CHECK( array3.val_identity(array1) ) ;
		BOOST_CHECK_EQUAL( array1.size() , array3.size() ) ;
		BOOST_CHECK_EQUAL( array1.max_size() , array3.max_size() ) ;
	}
	BOOST_AUTO_TEST_CASE( assignment_to_self )
	{
		typedef Array< int > array_type ;
		array_type a(5) ;
		a.init(10) ;

		a = a ;
		FOREACH(int i, a)
		{
			BOOST_CHECK_EQUAL(10, i) ;
		}
	}
	BOOST_AUTO_TEST_CASE( assignment_to_other)
	{
		typedef Array< int > array_type ;
		array_type a ;
		array_type b(10) ;
		b.init(10) ;

		a = b ;
		BOOST_CHECK(b.val_identity(a)) ;
	}
BOOST_AUTO_TEST_SUITE_END()

#endif


//          Copyright Maksym V. Bilinets 2015 - 2019.
// Distributed under the Boost Software License, Version 1.0.
//      (See accompanying file LICENSE.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt )

#include <dst/binary_tree/algorithm.h>
#include <dst/binary_tree/tree.h>

#include <boost/test/unit_test.hpp>

namespace
{
// $      |    $
// $      0    $
// $    /   \  $
// $   1     2 $
// $  / \   /  $
// $ 3   4 5   $
const dst::binary_tree::initializer_tree<int>
  init_tree({{3, 1, 4}, 0, {5, 2, {}}});

const dst::binary_tree::tree<int> tree(init_tree);
}

BOOST_AUTO_TEST_SUITE(test_binary_tree_algorithm)

BOOST_AUTO_TEST_CASE(test_leaf)
{
  BOOST_TEST(!leaf(init_tree.root()));
  BOOST_TEST(!leaf(right(init_tree.root())));
  BOOST_TEST(leaf(left(right(init_tree.root()))));
}

BOOST_AUTO_TEST_CASE(test_maximum)
{
  BOOST_TEST(*maximum(init_tree.root()) == 2);
}

BOOST_AUTO_TEST_CASE(test_minimum)
{
  BOOST_TEST(*minimum(init_tree.root()) == 3);
}

BOOST_AUTO_TEST_CASE(test_parent)
{
  BOOST_TEST(*parent(left(tree.root())) == 0);
  BOOST_TEST(*parent(right(tree.root())) == 0);
  BOOST_TEST(!parent(tree.root()));
}

BOOST_AUTO_TEST_CASE(test_roll_down_left)
{
  BOOST_TEST(*roll_down_left(init_tree.root()) == 3);
}

BOOST_AUTO_TEST_CASE(test_roll_down_right)
{
  BOOST_TEST(*roll_down_right(init_tree.root()) == 5);
}

BOOST_AUTO_TEST_CASE(test_root)
{
  for (auto it = tree.begin(); it != tree.end(); ++it)
  {
    BOOST_TEST((root(it.base()) == tree.root()));
  }
}

BOOST_AUTO_TEST_CASE(test_sibling)
{
  BOOST_TEST(*sibling(left(tree.root())) == 2);
  BOOST_TEST(*sibling(right(tree.root())) == 1);
  BOOST_TEST(!sibling(left(right(tree.root()))));
}

BOOST_AUTO_TEST_CASE(test_topologically_equal)
{
  // $      |    $
  // $      0    $
  // $    /   \  $
  // $   1     2 $
  // $  / \   /  $
  // $ 3   4 5   $
  dst::binary_tree::initializer_tree<int> a({{3, 1, 4}, 0, {5, 2, {}}});

  BOOST_TEST(topologically_equal(init_tree.root(), a.root()));

  // $    |      $
  // $    4      $
  // $  /   \    $
  // $ 3     5   $
  // $  \   / \  $
  // $   1 0   2 $
  dst::binary_tree::initializer_tree<int> b({{{}, 3, 1}, 4, {0, 5, 2}});

  BOOST_TEST(!topologically_equal(init_tree.root(), b.root()));

  // $   | $
  // $   4 $
  // $  /  $
  // $ 3   $
  // $  \  $
  // $   1 $
  dst::binary_tree::initializer_tree<int> c({{{}, 3, 1}, 4, {}});

  BOOST_TEST(!topologically_equal(init_tree.root(), c.root()));

  // $ | $
  dst::binary_tree::initializer_tree<int> d;

  BOOST_TEST(!topologically_equal(init_tree.root(), d.root()));

  // $ | $
  dst::binary_tree::initializer_tree<int> e;

  BOOST_TEST(topologically_equal(d.root(), e.root()));
}

BOOST_AUTO_TEST_CASE(test_successor)
{
  auto it = left(left(tree.root()));

  BOOST_TEST(*it == 3);

  it = successor(it);

  BOOST_TEST(*it == 1);

  it = successor(it);

  BOOST_TEST(*it == 4);

  it = successor(it);

  BOOST_TEST(*it == 0);

  it = successor(it);

  BOOST_TEST(*it == 5);

  it = successor(it);

  BOOST_TEST(*it == 2);

  BOOST_TEST(!successor(it));
}

BOOST_AUTO_TEST_CASE(test_predecessor)
{
  auto it = right(tree.root());

  BOOST_TEST(*it == 2);

  it = predecessor(it);

  BOOST_TEST(*it == 5);

  it = predecessor(it);

  BOOST_TEST(*it == 0);

  it = predecessor(it);

  BOOST_TEST(*it == 4);

  it = predecessor(it);

  BOOST_TEST(*it == 1);

  it = predecessor(it);

  BOOST_TEST(*it == 3);

  BOOST_TEST(!predecessor(it));
}

BOOST_AUTO_TEST_CASE(test_order)
{
  const auto it_0 = tree.root();
  const auto it_1 = left(it_0);
  const auto it_2 = right(it_0);
  const auto it_3 = left(it_1);
  const auto it_4 = right(it_1);
  const auto it_5 = left(it_2);

  BOOST_TEST(order(it_4, it_5));
  BOOST_TEST(order(it_5, it_4) == false);

  BOOST_TEST(order(it_3, it_4));
  BOOST_TEST(order(it_4, it_3) == false);

  BOOST_TEST(order(it_3, it_0));
  BOOST_TEST(order(it_0, it_3) == false);

  BOOST_TEST(order(it_5, it_5) == false);
}

BOOST_AUTO_TEST_CASE(test_inorder_depth_first_search)
{
  std::vector<int> sequence(begin_inorder_depth_first_search(tree.root()),
                            end_inorder_depth_first_search(tree.nil()));

  BOOST_TEST(sequence == std::vector<int>({3, 1, 4, 0, 5, 2}),
             boost::test_tools::per_element());
}

BOOST_AUTO_TEST_CASE(test_postorder_depth_first_search)
{
  std::vector<int> sequence(begin_postorder_depth_first_search(tree.root()),
                            end_postorder_depth_first_search(tree.nil()));

  BOOST_TEST(sequence == std::vector<int>({3, 4, 1, 5, 2, 0}),
             boost::test_tools::per_element());
}

BOOST_AUTO_TEST_SUITE_END()


#include "quadrature/calculators/spherical_harmonic_zeroth_moment.h"

#include <memory>

#include <deal.II/base/mpi.h>
#include <deal.II/lac/petsc_vector.h>

#include "system/system_types.h"
#include "system/moments/spherical_harmonic_types.h"
#include "quadrature/utility/quadrature_utilities.h"
#include "quadrature/tests/quadrature_set_mock.hpp"
#include "quadrature/tests/quadrature_point_mock.hpp"
#include "system/solution/tests/mpi_group_angular_solution_mock.h"
#include "test_helpers/gmock_wrapper.h"

namespace {

using namespace bart;

using ::testing::Ref, ::testing::Return, ::testing::ReturnRef;

void SetVector(system::MPIVector& to_set, double value) {
  auto [first_row, last_row] = to_set.local_range();
  for (unsigned int i = first_row; i < last_row; ++i)
    to_set[i] = value;
  to_set.compress(dealii::VectorOperation::insert);
}

/* Tests for the SphericalHarmonicMomentsZerothMoment class. Mock quadrature set
 * is required.
 *
 * Test initial conditions: test object is constructed with mock quadrature set,
 * observation pointer is provided to set call expectations. Three mpi vectors
 * are provided with values 1, 10, and 100.
 */
template <typename DimensionWrapper>
class QuadCalcSphericalHarmonicMomentsOnlyScalar : public ::testing::Test {
 protected:
  static constexpr int dim = DimensionWrapper::value;
  // Aliases
  using QuadratureSetType = quadrature::QuadratureSetMock<dim>;
  using MomentCalculatorType = quadrature::calculators::SphericalHarmonicZerothMoment<dim>;

  // Pointer to tested object
  std::unique_ptr<MomentCalculatorType> test_calculator;

  // Supporting objects
  system::solution::MPIGroupAngularSolutionMock mock_solution_;
  std::array<system::MPIVector, 3> mpi_vectors_;

  // Test object dependency
  std::shared_ptr<QuadratureSetType> mock_quadrature_set_ptr_;

  // Observing pointers
  QuadratureSetType* quadrature_set_obs_ptr_;

  // Test parameters
  const int n_processes = dealii::Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD);
  const int n_entries_per_proc = 10;

  void SetUp() override;
};

template <typename DimensionWrapper>
void QuadCalcSphericalHarmonicMomentsOnlyScalar<DimensionWrapper>::SetUp() {

  // Instantiate mock objects
  mock_quadrature_set_ptr_ = std::make_shared<QuadratureSetType>();

  // Instantiate object to be tested
  test_calculator = std::make_unique<MomentCalculatorType>(
      mock_quadrature_set_ptr_);

  // Set up observation pointers
  quadrature_set_obs_ptr_ = dynamic_cast<QuadratureSetType*>(
      test_calculator->quadrature_set_ptr());

  for (auto& mpi_vector : mpi_vectors_) {
    mpi_vector.reinit(MPI_COMM_WORLD,
                      n_processes * n_entries_per_proc,
                      n_entries_per_proc);
  }

  SetVector(mpi_vectors_[0], 1);
  SetVector(mpi_vectors_[1], 10);
  SetVector(mpi_vectors_[2], 100);
}

TYPED_TEST_CASE(QuadCalcSphericalHarmonicMomentsOnlyScalar,
                bart::testing::AllDimensions);

// Constructor should have set quadrature_set_ptr properly.
TYPED_TEST(QuadCalcSphericalHarmonicMomentsOnlyScalar, Constructor) {

  auto quadrature_set_ptr = this->test_calculator->quadrature_set_ptr();
  ASSERT_NE(nullptr, quadrature_set_ptr);
}

/* An error should be thrown if there is a mismatch between the total angles
 * reported by the solution and the size of the quadrature set */
TYPED_TEST(QuadCalcSphericalHarmonicMomentsOnlyScalar, CalculateBadAngleNumber) {
  auto& quadrature_set_mock = *this->quadrature_set_obs_ptr_;
  auto& test_calculator = this->test_calculator;
  auto mock_solution_ptr = &this->mock_solution_;

  EXPECT_CALL(quadrature_set_mock, size())
      .WillOnce(Return(4));
  EXPECT_CALL(*mock_solution_ptr, total_angles())
      .WillOnce(Return(3));
  EXPECT_ANY_THROW(test_calculator->CalculateMoment(mock_solution_ptr, 0, 0, 0));
}

/* Moments should be calculated properly.
 *
 * To accomplish this test we will fill a set with mock points with weights
 * given by 2.2 + i*1.1 where i is an index value. These index values will
 * identify one of the mpi_vectors provided by the test, which are equal to 1,
 * 10, and 100, sequentiall. Therefore we expect the final moment to be equal to
 * 2.2 + 3.3*10 + 4.4*100
 */
TYPED_TEST(QuadCalcSphericalHarmonicMomentsOnlyScalar, CalculateMomentsMPI) {
  auto& quadrature_set_mock = *this->quadrature_set_obs_ptr_;
  auto& test_calculator = this->test_calculator;
  auto mock_solution_ptr = &this->mock_solution_;
  constexpr int dim = this->dim;

  const int n_angles = 3;
  const int group = 0;

  EXPECT_CALL(quadrature_set_mock, size())
      .WillOnce(Return(n_angles));
  EXPECT_CALL(*mock_solution_ptr, total_angles())
      .WillOnce(Return(n_angles));

  std::set<std::shared_ptr<quadrature::QuadraturePointI<dim>>,
           quadrature::utility::quadrature_point_compare<dim>>
      mock_quadrature_point_set;

  for (int angle = 0; angle < n_angles; ++angle) {
    // Solutions are identified by angle index, so we expect a request for
    // the solution for each angle to be called.
    EXPECT_CALL(*mock_solution_ptr, GetSolution(angle))
        .WillOnce(ReturnRef(this->mpi_vectors_[angle]));

    // We make our mock quadrature point and set the weight and position
    auto mock_quadrature_point =
        std::make_shared<::testing::NiceMock<quadrature::QuadraturePointMock<dim>>>();
    EXPECT_CALL(*mock_quadrature_point, weight())
        .WillOnce(Return(2.2 + angle*1.1));
    // Position is only added for ordering in the set, value doesn't matter as
    // long as each processor orders them equally (by position)
    std::array<double, dim> position;
    position.fill(angle*1.1);
    ON_CALL(*mock_quadrature_point, cartesian_position())
        .WillByDefault(Return(position));

    // Insert into our mock set, get an insert pair that includes an interator
    // to the newly inserted object
    auto insert_pair = mock_quadrature_point_set.insert(mock_quadrature_point);

    // We expect the function to retrieve the index of the point. This is what
    // links THIS point to the correct solution.
    EXPECT_CALL(quadrature_set_mock,
        GetQuadraturePointIndex(*insert_pair.first))
        .WillOnce(Return(angle));
  }

  EXPECT_CALL(quadrature_set_mock, begin())
      .WillOnce(Return(mock_quadrature_point_set.begin()));
  EXPECT_CALL(quadrature_set_mock, end())
      .WillOnce(Return(mock_quadrature_point_set.end()));

  system::moments::MomentVector expected_result(
      this->n_entries_per_proc*this->n_processes);

  expected_result = 4.4*100 + 3.3*10 + 2.2;

  auto result = test_calculator->CalculateMoment(mock_solution_ptr, group, 0, 0);
  ASSERT_NE(result.size(), 0); // Make sure it isn't empty
  EXPECT_EQ(result, expected_result);
}

} // namespace