<commit_before>/********************************************************
 * Ville Hyvönen & Teemu Pitkänen                       *
 * HIIT / University of Helsinki                        *
 * ville.o.hyvonen<at>helsinki.fi                       *
 * teemu.pitkanen<at>cs.helsinki.fi                     *
 * 2016                                                 *
 ********************************************************/

#include "armadillo"
#include <cstdlib>
#include <queue>
#include "Mrpt.h"

using namespace arma;

/**
 * The constructor of the index. The inputs are the data for which the index
 * will be built and additional parameters that affect the accuracy of the NN
 * approximation. Concisely, larger n_trees_ or n_0_ values improve accuracy but
 * slow down the queries. A general rule for the right balance is not known. The
 * constructor does not actually build the trees, but that is done by a separate
 * function 'grow' that has to be called before queries can be made.
 * @param X_ - The data to be indexed. Samples as columns, features as rows.
 * @param n_trees_ - The number of trees to be used in the index.
 * @param n_0_ - The maximum leaf size to be used in the index.
 * @param id_ - A name used for filenames when saving.
 */
Mrpt::Mrpt(const fmat& X_, int n_trees_, int n_0_, std::string id_) : X(X_), n_trees(n_trees_), n_0(n_0_), id(id_){
    n_samples = X.n_cols;
    dim = X.n_rows;
    depth = ceil(log2(n_samples / n_0));
    n_pool = n_trees * depth;
    n_array = pow(2, depth + 1);
    split_points = fmat();
    random_matrix = fmat();
}

/**
 * The function whose call starts the actual index construction. Initializes
 * arrays to store the tree structures and computes all the projections needed
 * later. Then repeatedly calls method grow_subtree that builds a single
 * RP-tree.
 */
void Mrpt::grow() {
    split_points = zeros<fmat>(n_array, n_trees);
    uvec indices = linspace<uvec>(0, n_samples - 1, n_samples);

    // generate the random matrix and project the data set onto it
    random_matrix = conv_to<fmat>::from(randn(n_pool, dim));
    projected_data = random_matrix * X;

    // Grow the trees
    for (int n_tree = 0; n_tree < n_trees; n_tree++) {
        first_idx = n_tree * depth;
        std::vector<uvec> t = grow_subtree(indices, 0, 0, n_tree);
        tree_leaves.push_back(t);
    }
}

/**
 * Builds a single random projection tree. The tree is constructed by recursively
 * projecting the data on a random vector and splitting into two by the median.
 * @param indices - The indices left in this branch
 * @param tree_level - The level in tree where the recursion is at
 * @param i - The index within the tree where we are at
 * @param n_tree - The index of the tree within the index
 * @return The leaves as a vector of arma::uvecs
 */
std::vector<uvec> Mrpt::grow_subtree(const uvec &indices, int tree_level, int i, uword n_tree) {
    int n = indices.size();
    int idx_left = 2 * i + 1;
    int idx_right = idx_left + 1;

    if (tree_level == depth) {
        std::vector<uvec> v;
        v.push_back(indices);
        return v;
    }

    uvec level = {first_idx + tree_level};
    frowvec projection = projected_data(level, indices);
    uvec ordered = sort_index(projection);

    int split_point = n % 2 ? n / 2 : n / 2 - 1; // median split
    int idx_split_point = ordered(split_point);
    int idx_split_point2 = ordered(split_point + 1);

    split_points(i, n_tree) = n % 2 ? projection(idx_split_point) : (projection(idx_split_point) + projection(idx_split_point2)) / 2;
    uvec left_indices = ordered.subvec(0, split_point);
    uvec right_indices = ordered.subvec(split_point + 1, n - 1);

    std::vector<uvec> v = grow_subtree(indices.elem(left_indices), tree_level + 1, idx_left, n_tree);
    std::vector<uvec> w = grow_subtree(indices.elem(right_indices), tree_level + 1, idx_right, n_tree);
    v.insert( v.end(), w.begin(), w.end() );
    return v;
}

/**
 * This function finds the k approximate nearest neighbors of the query object
 * q. The accuracy of the query depends on both the parameters used for index
 * construction and additional parameters given to this function. This
 * function implements two tricks to improve performance. The voting trick
 * interprets each index object in leaves returned by tree traversals as votes,
 * and only performs the final linear search with the 'elect' most voted
 * objects. The priority queue trick keeps track of nodes where the split value
 * was close to the projection so that we can split the tree traversal to both
 * subtrees if we want.
 * @param q - The query object whose neighbors the function finds.
 * @param k - The number of neighbors the user wants the function to return
 * @param elect - The number of most voted objects elected to linear search
 * @param branches - The number of extra branches explored in priority queue trick
 * @return The indices of the k approximate nearest neighbors in the original
 * data set for which the index was built.
 */
uvec Mrpt::query(const fvec& q, int k, int elect, int branches) {

    fvec projected_query = random_matrix * q;
    uvec votes = zeros<uvec>(n_samples);
    std::priority_queue<Gap, std::vector<Gap>, std::greater<Gap>> pq;

    /*
     * The following loops over all trees, and routes the query to exactly one
     * leaf in each.
     */
    int j = 0; // Used to find the correct projection value, increases through all trees
    for (int n_tree = 0; n_tree < n_trees; n_tree++) {
        const fvec& tree = split_points.unsafe_col(n_tree);

        double split_point = tree[0];
        int idx_left, idx_right;
        int idx_tree = 0;

        while (split_point) {
            idx_left = 2 * idx_tree + 1;
            idx_right = idx_left + 1;
            if (projected_query(j) <= split_point) {
                idx_tree = idx_left;
                pq.push(Gap(n_tree, idx_right, j+1, split_point-projected_query(j)));
            } else {
                idx_tree = idx_right;
                pq.push(Gap(n_tree, idx_left, j+1, projected_query(j)-split_point));
            }
            j++;
            split_point = tree[idx_tree];
        }
        const uvec& idx_one_tree = tree_leaves[n_tree][idx_tree - pow(2, depth) + 1];
        for (int i = 0; i < idx_one_tree.size(); i++){
            votes[idx_one_tree[i]]++;
        }
    }

    /*
     * The following loop routes the query to extra leaves in the same trees
     * handled already once above. The extra branches are popped from the
     * priority queue and routed down the tree just as new root-to-leaf queries.
     */
    for (int b = 0; b < branches; b++){
        if (pq.empty()) break;
        Gap gap = pq.top();
        pq.pop();

        const fvec& tree = split_points.unsafe_col(gap.tree);
        int idx_tree = gap.node;
        int idx_left, idx_right;
        j = gap.level;
        double split_point = tree[idx_tree];

        while (split_point) {
            idx_left = 2 * idx_tree + 1;
            idx_right = idx_left + 1;
            if (projected_query(j) <= split_point) {
                idx_tree = idx_left;
                pq.push(Gap(gap.tree, idx_right, j+1, split_point-projected_query(j)));
            } else {
                idx_tree = idx_right;
                pq.push(Gap(gap.tree, idx_left, j+1, projected_query(j)-split_point));
            }
            j++;
            split_point = tree[idx_tree];
        }

        const uvec& idx_one_tree = tree_leaves[gap.tree][idx_tree - pow(2, depth) + 1];
        for (int idx : idx_one_tree){
            votes[idx]++;
        }
    }

    // Compute the actual NNs within the 'elect' objects with most votes
    uvec elected = sort_index(votes, "descend");
    elected.resize(elect);
    return exact_knn(X, q, k, elected);
}

/**
 * This function implements the barebones MRPT algorithm and finds the k
 * approximate nearest neighbors of the query object q using multiple random
 * projection trees. The accuracy of the query depends on the parameters used
 * for index construction. Separated from the function above for optimal performance.
 * @param q - The query object whose neighbors the function finds.
 * @param k - The number of neighbors the user wants the function to return
 * @return The indices of the k approximate nearest neighbors in the original
 * data set for which the index was built.
 */
uvec Mrpt::query(const fvec& q, int k) {
    fvec projected_query = random_matrix * q;
    std::vector<int> idx_canditates(n_trees * n_0);
    int j = 0;

    for (int n_tree = 0; n_tree < n_trees; n_tree++) {
        const fvec& tree = split_points.unsafe_col(n_tree);

        double split_point = tree[0];
        int idx_left, idx_right;
        int idx_tree = 0;

        while (split_point) {
            idx_left = 2 * idx_tree + 1;
            idx_right = idx_left + 1;
            idx_tree = projected_query(j++) <= split_point ? idx_left : idx_right;
            split_point = tree[idx_tree];
        }

        const uvec& idx_one_tree = tree_leaves[n_tree][idx_tree - pow(2, depth) + 1];
        idx_canditates.insert(idx_canditates.begin(), idx_one_tree.begin(), idx_one_tree.end());
    }

    std::sort(idx_canditates.begin(), idx_canditates.end());
    auto last = std::unique(idx_canditates.begin(), idx_canditates.end());
    idx_canditates.erase(last, idx_canditates.end());

    return exact_knn(X, q, k, conv_to<uvec>::from(idx_canditates));
}

/**
 * find k nearest neighbors from data for the query point
 * @param X - data matrix, row = data point, col = dimension
 * @param q - query point as a row matrix
 * @param k - number of neighbors searched for
 * @param indices - indices of the points in the original matrix where search is made
 * @return - indices of nearest neighbors in data matrix X as a column vector
 */
uvec exact_knn(const fmat& D, const fvec& q, uword k, uvec indices) {
    int n_cols = indices.size();
    fvec distances = fvec(n_cols);
    for (int i = 0; i < n_cols; i++)
        distances[i] = sum(pow((D.col(indices(i)) - q), 2));

    if(k == 1) {
        uvec ret(1);
        distances.min(ret[0]);
        return ret;
    }

    uvec sorted_indices = indices(sort_index(distances));
    return sorted_indices.size() > k ? sorted_indices.head(k): sorted_indices;
}<commit_msg>Fixed leaf-size 1 bug<commit_after>/********************************************************
 * Ville Hyvönen & Teemu Pitkänen                       *
 * HIIT / University of Helsinki                        *
 * ville.o.hyvonen<at>helsinki.fi                       *
 * teemu.pitkanen<at>cs.helsinki.fi                     *
 * 2016                                                 *
 ********************************************************/

#include "armadillo"
#include <cstdlib>
#include <queue>
#include "Mrpt.h"

using namespace arma;

/**
 * The constructor of the index. The inputs are the data for which the index
 * will be built and additional parameters that affect the accuracy of the NN
 * approximation. Concisely, larger n_trees_ or n_0_ values improve accuracy but
 * slow down the queries. A general rule for the right balance is not known. The
 * constructor does not actually build the trees, but that is done by a separate
 * function 'grow' that has to be called before queries can be made.
 * @param X_ - The data to be indexed. Samples as columns, features as rows.
 * @param n_trees_ - The number of trees to be used in the index.
 * @param n_0_ - The maximum leaf size to be used in the index.
 * @param id_ - A name used for filenames when saving.
 */
Mrpt::Mrpt(const fmat& X_, int n_trees_, int n_0_, std::string id_) : X(X_), n_trees(n_trees_), n_0(n_0_), id(id_){
    n_samples = X.n_cols;
    dim = X.n_rows;
    if (n_0 == 1) // n_0==1 => leaves have sizes 1 or 2 (b/c 0.5 is impossible)
        depth = floor(log2(n_samples));
    else
        depth = ceil(log2(n_samples / n_0));
    n_pool = n_trees * depth;
    n_array = pow(2, depth + 1);
    split_points = fmat();
    random_matrix = fmat();
}

/**
 * The function whose call starts the actual index construction. Initializes
 * arrays to store the tree structures and computes all the projections needed
 * later. Then repeatedly calls method grow_subtree that builds a single
 * RP-tree.
 */
void Mrpt::grow() {
    split_points = zeros<fmat>(n_array, n_trees);
    uvec indices = linspace<uvec>(0, n_samples - 1, n_samples);

    // generate the random matrix and project the data set onto it
    random_matrix = conv_to<fmat>::from(randn(n_pool, dim));
    projected_data = random_matrix * X;

    // Grow the trees
    for (int n_tree = 0; n_tree < n_trees; n_tree++) {
        first_idx = n_tree * depth;
        std::vector<uvec> t = grow_subtree(indices, 0, 0, n_tree);
        tree_leaves.push_back(t);
    }
}

/**
 * Builds a single random projection tree. The tree is constructed by recursively
 * projecting the data on a random vector and splitting into two by the median.
 * @param indices - The indices left in this branch
 * @param tree_level - The level in tree where the recursion is at
 * @param i - The index within the tree where we are at
 * @param n_tree - The index of the tree within the index
 * @return The leaves as a vector of arma::uvecs
 */
std::vector<uvec> Mrpt::grow_subtree(const uvec &indices, int tree_level, int i, uword n_tree) {
    int n = indices.size();
    int idx_left = 2 * i + 1;
    int idx_right = idx_left + 1;

    if (tree_level == depth) {
        std::vector<uvec> v;
        v.push_back(indices);
        return v;
    }

    uvec level = {first_idx + tree_level};
    frowvec projection = projected_data(level, indices);
    uvec ordered = sort_index(projection);

    int split_point = n % 2 ? n / 2 : n / 2 - 1; // median split
    int idx_split_point = ordered(split_point);
    int idx_split_point2 = ordered(split_point + 1);

    split_points(i, n_tree) = n % 2 ? projection(idx_split_point) : (projection(idx_split_point) + projection(idx_split_point2)) / 2;
    uvec left_indices = ordered.subvec(0, split_point);
    uvec right_indices = ordered.subvec(split_point + 1, n - 1);

    std::vector<uvec> v = grow_subtree(indices.elem(left_indices), tree_level + 1, idx_left, n_tree);
    std::vector<uvec> w = grow_subtree(indices.elem(right_indices), tree_level + 1, idx_right, n_tree);
    v.insert( v.end(), w.begin(), w.end() );
    return v;
}

/**
 * This function finds the k approximate nearest neighbors of the query object
 * q. The accuracy of the query depends on both the parameters used for index
 * construction and additional parameters given to this function. This
 * function implements two tricks to improve performance. The voting trick
 * interprets each index object in leaves returned by tree traversals as votes,
 * and only performs the final linear search with the 'elect' most voted
 * objects. The priority queue trick keeps track of nodes where the split value
 * was close to the projection so that we can split the tree traversal to both
 * subtrees if we want.
 * @param q - The query object whose neighbors the function finds.
 * @param k - The number of neighbors the user wants the function to return
 * @param elect - The number of most voted objects elected to linear search
 * @param branches - The number of extra branches explored in priority queue trick
 * @return The indices of the k approximate nearest neighbors in the original
 * data set for which the index was built.
 */
uvec Mrpt::query(const fvec& q, int k, int elect, int branches) {

    fvec projected_query = random_matrix * q;
    uvec votes = zeros<uvec>(n_samples);
    std::priority_queue<Gap, std::vector<Gap>, std::greater<Gap>> pq;

    /*
     * The following loops over all trees, and routes the query to exactly one
     * leaf in each.
     */
    int j = 0; // Used to find the correct projection value, increases through all trees
    for (int n_tree = 0; n_tree < n_trees; n_tree++) {
        const fvec& tree = split_points.unsafe_col(n_tree);

        double split_point = tree[0];
        int idx_left, idx_right;
        int idx_tree = 0;

        while (split_point) {
            idx_left = 2 * idx_tree + 1;
            idx_right = idx_left + 1;
            if (projected_query(j) <= split_point) {
                idx_tree = idx_left;
                pq.push(Gap(n_tree, idx_right, j+1, split_point-projected_query(j)));
            } else {
                idx_tree = idx_right;
                pq.push(Gap(n_tree, idx_left, j+1, projected_query(j)-split_point));
            }
            j++;
            split_point = tree[idx_tree];
        }
        const uvec& idx_one_tree = tree_leaves[n_tree][idx_tree - pow(2, depth) + 1];
        for (int i = 0; i < idx_one_tree.size(); i++){
            votes[idx_one_tree[i]]++;
        }
    }

    /*
     * The following loop routes the query to extra leaves in the same trees
     * handled already once above. The extra branches are popped from the
     * priority queue and routed down the tree just as new root-to-leaf queries.
     */
    for (int b = 0; b < branches; b++){
        if (pq.empty()) break;
        Gap gap = pq.top();
        pq.pop();

        const fvec& tree = split_points.unsafe_col(gap.tree);
        int idx_tree = gap.node;
        int idx_left, idx_right;
        j = gap.level;
        double split_point = tree[idx_tree];

        while (split_point) {
            idx_left = 2 * idx_tree + 1;
            idx_right = idx_left + 1;
            if (projected_query(j) <= split_point) {
                idx_tree = idx_left;
                pq.push(Gap(gap.tree, idx_right, j+1, split_point-projected_query(j)));
            } else {
                idx_tree = idx_right;
                pq.push(Gap(gap.tree, idx_left, j+1, projected_query(j)-split_point));
            }
            j++;
            split_point = tree[idx_tree];
        }

        const uvec& idx_one_tree = tree_leaves[gap.tree][idx_tree - pow(2, depth) + 1];
        for (int idx : idx_one_tree)
            votes[idx]++;
    }

    // Compute the actual NNs within the 'elect' objects with most votes
    uvec elected = sort_index(votes, "descend");
    elected.resize(elect);
    return exact_knn(X, q, k, elected);
}

/**
 * This function implements the barebones MRPT algorithm and finds the k
 * approximate nearest neighbors of the query object q using multiple random
 * projection trees. The accuracy of the query depends on the parameters used
 * for index construction. Separated from the function above for optimal performance.
 * @param q - The query object whose neighbors the function finds.
 * @param k - The number of neighbors the user wants the function to return
 * @return The indices of the k approximate nearest neighbors in the original
 * data set for which the index was built.
 */
uvec Mrpt::query(const fvec& q, int k) {
    fvec projected_query = random_matrix * q;
    std::vector<int> idx_canditates(n_trees * n_0);
    int j = 0;

    for (int n_tree = 0; n_tree < n_trees; n_tree++) {
        const fvec& tree = split_points.unsafe_col(n_tree);

        double split_point = tree[0];
        int idx_left, idx_right;
        int idx_tree = 0;

        while (split_point) {
            idx_left = 2 * idx_tree + 1;
            idx_right = idx_left + 1;
            idx_tree = projected_query(j++) <= split_point ? idx_left : idx_right;
            split_point = tree[idx_tree];
        }

        const uvec& idx_one_tree = tree_leaves[n_tree][idx_tree - pow(2, depth) + 1];
        idx_canditates.insert(idx_canditates.begin(), idx_one_tree.begin(), idx_one_tree.end());
    }

    std::sort(idx_canditates.begin(), idx_canditates.end());
    auto last = std::unique(idx_canditates.begin(), idx_canditates.end());
    idx_canditates.erase(last, idx_canditates.end());

    return exact_knn(X, q, k, conv_to<uvec>::from(idx_canditates));
}

/**
 * find k nearest neighbors from data for the query point
 * @param X - data matrix, row = data point, col = dimension
 * @param q - query point as a row matrix
 * @param k - number of neighbors searched for
 * @param indices - indices of the points in the original matrix where search is made
 * @return - indices of nearest neighbors in data matrix X as a column vector
 */
uvec exact_knn(const fmat& D, const fvec& q, uword k, uvec indices) {
    int n_cols = indices.size();
    fvec distances = fvec(n_cols);
    for (int i = 0; i < n_cols; i++)
        distances[i] = sum(pow((D.col(indices(i)) - q), 2));

    if(k == 1) {
        uvec ret(1);
        distances.min(ret[0]);
        return ret;
    }

    uvec sorted_indices = indices(sort_index(distances));
    return sorted_indices.size() > k ? sorted_indices.head(k): sorted_indices;
}<|endoftext|>


<commit_before>#include "bochs.h"
#ifdef WIN32
// windows.h included in bochs.h
#else
//#  error "extdb.cc only supported in win32 environment"
#endif

TRegs regs;

char debug_loaded = 0;

void (*call_debugger)(TRegs *,Bit8u *, Bit32u);

void bx_external_debugger(BX_CPU_C *cpu)
{
     //printf("Calling debugger state=%d\n",regs.debug_state);
     switch (regs.debug_state) {
     case debug_run:
       return;
     case debug_count:
       if (--regs.debug_counter) return;
       regs.debug_state = debug_step;
       break;
     case debug_skip:
       if (cpu->dword.eip != regs.debug_eip ||
           cpu->sregs[1].selector.value != regs.debug_cs) return;
       regs.debug_state = debug_step;
       break;
     }

#if BX_SUPPORT_X86_64
     regs.rax = cpu->gen_reg[0].rrx;
     regs.rcx = cpu->gen_reg[1].rrx;
     regs.rdx = cpu->gen_reg[2].rrx;
     regs.rbx = cpu->gen_reg[3].rrx;
     regs.rsp = cpu->gen_reg[4].rrx;
     regs.rbp = cpu->gen_reg[5].rrx;
     regs.rsi = cpu->gen_reg[6].rrx;
     regs.rdi = cpu->gen_reg[7].rrx;
     regs.r8  = cpu->gen_reg[8].rrx;
     regs.r9  = cpu->gen_reg[9].rrx;
     regs.r10 = cpu->gen_reg[10].rrx;
     regs.r11 = cpu->gen_reg[11].rrx;
     regs.r12 = cpu->gen_reg[12].rrx;
     regs.r13 = cpu->gen_reg[13].rrx;
     regs.r14 = cpu->gen_reg[14].rrx;
     regs.r15 = cpu->gen_reg[15].rrx;
     regs.rip = cpu->rip;
#else
     regs.rax = cpu->gen_reg[0].dword.erx;
     regs.rcx = cpu->gen_reg[1].dword.erx;
     regs.rdx = cpu->gen_reg[2].dword.erx;
     regs.rbx = cpu->gen_reg[3].dword.erx;
     regs.rsp = cpu->gen_reg[4].dword.erx;
     regs.rbp = cpu->gen_reg[5].dword.erx;
     regs.rsi = cpu->gen_reg[6].dword.erx;
     regs.rdi = cpu->gen_reg[7].dword.erx;
     regs.r8  = 0;
     regs.r9  = 0;
     regs.r10 = 0;
     regs.r11 = 0;
     regs.r12 = 0;
     regs.r13 = 0;
     regs.r14 = 0;
     regs.r15 = 0;
     regs.rip = cpu->dword.eip;
#endif
     regs.rflags = cpu->read_eflags();
     regs.es = cpu->sregs[0].selector.value;
     regs.cs = cpu->sregs[1].selector.value;
     regs.ss = cpu->sregs[2].selector.value;
     regs.ds = cpu->sregs[3].selector.value;
     regs.fs = cpu->sregs[4].selector.value;
     regs.gs = cpu->sregs[5].selector.value;
     regs.gdt.base = cpu->gdtr.base;
     regs.gdt.limit = cpu->gdtr.limit;
     regs.idt.base = cpu->idtr.base;
     regs.idt.limit = cpu->idtr.limit;
     regs.ldt = cpu->ldtr.selector.value;
     regs.cr0 = cpu->cr0.val32;
     regs.cr1 = cpu->cr1;
     regs.cr2 = cpu->cr2;
     regs.cr3 = cpu->cr3;
     regs.cr4 = cpu->cr4.getRegister();
     //regs.cr5 = cpu->cr5;
     //regs.cr6 = cpu->cr6;
     //regs.cr7 = cpu->cr7;
     regs.fsbase = cpu->sregs[BX_SEG_REG_FS].cache.u.segment.base;
     regs.gsbase = cpu->sregs[BX_SEG_REG_GS].cache.u.segment.base;
#if BX_SUPPORT_X86_64
    regs.efer = (BX_CPU_THIS_PTR msr.sce << 0)
               | (BX_CPU_THIS_PTR msr.lme << 8)
               | (BX_CPU_THIS_PTR msr.lma << 10);
#else
    regs.efer = 0;
#endif

     if (debug_loaded == 0) {
       HINSTANCE hdbg;

       debug_loaded = 1;
       hdbg = LoadLibrary("debug.dll");
       call_debugger = (void (*)(TRegs *,Bit8u *, Bit32u)) GetProcAddress(hdbg,"call_debugger");

       if (call_debugger != NULL) debug_loaded = 2;
     }
     if (debug_loaded == 2) {
       DEV_vga_refresh();
       call_debugger(&regs,cpu->mem->vector,cpu->mem->len);
     }
}
<commit_msg>fix for BX_CPU_LEVEL < 4<commit_after>#include "bochs.h"
#ifdef WIN32
// windows.h included in bochs.h
#else
//#  error "extdb.cc only supported in win32 environment"
#endif

TRegs regs;

char debug_loaded = 0;

void (*call_debugger)(TRegs *,Bit8u *, Bit32u);

void bx_external_debugger(BX_CPU_C *cpu)
{
     //printf("Calling debugger state=%d\n",regs.debug_state);
     switch (regs.debug_state) {
     case debug_run:
       return;
     case debug_count:
       if (--regs.debug_counter) return;
       regs.debug_state = debug_step;
       break;
     case debug_skip:
       if (cpu->dword.eip != regs.debug_eip ||
           cpu->sregs[1].selector.value != regs.debug_cs) return;
       regs.debug_state = debug_step;
       break;
     }

#if BX_SUPPORT_X86_64
     regs.rax = cpu->gen_reg[0].rrx;
     regs.rcx = cpu->gen_reg[1].rrx;
     regs.rdx = cpu->gen_reg[2].rrx;
     regs.rbx = cpu->gen_reg[3].rrx;
     regs.rsp = cpu->gen_reg[4].rrx;
     regs.rbp = cpu->gen_reg[5].rrx;
     regs.rsi = cpu->gen_reg[6].rrx;
     regs.rdi = cpu->gen_reg[7].rrx;
     regs.r8  = cpu->gen_reg[8].rrx;
     regs.r9  = cpu->gen_reg[9].rrx;
     regs.r10 = cpu->gen_reg[10].rrx;
     regs.r11 = cpu->gen_reg[11].rrx;
     regs.r12 = cpu->gen_reg[12].rrx;
     regs.r13 = cpu->gen_reg[13].rrx;
     regs.r14 = cpu->gen_reg[14].rrx;
     regs.r15 = cpu->gen_reg[15].rrx;
     regs.rip = cpu->rip;
#else
     regs.rax = cpu->gen_reg[0].dword.erx;
     regs.rcx = cpu->gen_reg[1].dword.erx;
     regs.rdx = cpu->gen_reg[2].dword.erx;
     regs.rbx = cpu->gen_reg[3].dword.erx;
     regs.rsp = cpu->gen_reg[4].dword.erx;
     regs.rbp = cpu->gen_reg[5].dword.erx;
     regs.rsi = cpu->gen_reg[6].dword.erx;
     regs.rdi = cpu->gen_reg[7].dword.erx;
     regs.r8  = 0;
     regs.r9  = 0;
     regs.r10 = 0;
     regs.r11 = 0;
     regs.r12 = 0;
     regs.r13 = 0;
     regs.r14 = 0;
     regs.r15 = 0;
     regs.rip = cpu->dword.eip;
#endif
     regs.rflags = cpu->read_eflags();
     regs.es = cpu->sregs[0].selector.value;
     regs.cs = cpu->sregs[1].selector.value;
     regs.ss = cpu->sregs[2].selector.value;
     regs.ds = cpu->sregs[3].selector.value;
     regs.fs = cpu->sregs[4].selector.value;
     regs.gs = cpu->sregs[5].selector.value;
     regs.gdt.base = cpu->gdtr.base;
     regs.gdt.limit = cpu->gdtr.limit;
     regs.idt.base = cpu->idtr.base;
     regs.idt.limit = cpu->idtr.limit;
     regs.ldt = cpu->ldtr.selector.value;
     regs.cr0 = cpu->cr0.val32;
     regs.cr1 = cpu->cr1;
     regs.cr2 = cpu->cr2;
     regs.cr3 = cpu->cr3;
#if BX_CPU_LEVEL >= 4
     regs.cr4 = cpu->cr4.getRegister();
#endif
     //regs.cr5 = cpu->cr5;
     //regs.cr6 = cpu->cr6;
     //regs.cr7 = cpu->cr7;
     regs.fsbase = cpu->sregs[BX_SEG_REG_FS].cache.u.segment.base;
     regs.gsbase = cpu->sregs[BX_SEG_REG_GS].cache.u.segment.base;
#if BX_SUPPORT_X86_64
    regs.efer = (BX_CPU_THIS_PTR msr.sce << 0)
               | (BX_CPU_THIS_PTR msr.lme << 8)
               | (BX_CPU_THIS_PTR msr.lma << 10);
#else
    regs.efer = 0;
#endif

     if (debug_loaded == 0) {
       HINSTANCE hdbg;

       debug_loaded = 1;
       hdbg = LoadLibrary("debug.dll");
       call_debugger = (void (*)(TRegs *,Bit8u *, Bit32u)) GetProcAddress(hdbg,"call_debugger");

       if (call_debugger != NULL) debug_loaded = 2;
     }
     if (debug_loaded == 2) {
       DEV_vga_refresh();
       call_debugger(&regs,cpu->mem->vector,cpu->mem->len);
     }
}
<|endoftext|>


<commit_before>/**
 * Non-metric Space Library
 *
 * Authors: Bilegsaikhan Naidan (https://github.com/bileg), Leonid Boytsov (http://boytsov.info).
 * With contributions from Lawrence Cayton (http://lcayton.com/) and others.
 *
 * For the complete list of contributors and further details see:
 * https://github.com/searchivarius/NonMetricSpaceLib
 *
 * Copyright (c) 2014
 *
 * This code is released under the
 * Apache License Version 2.0 http://www.apache.org/licenses/.
 *
 */
#include <algorithm>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cmath>

#include "space.h"
#include "rangequery.h"
#include "knnquery.h"
#include "searchoracle.h"
#include "method/vptree.h"
#include "method/vptree_utils.h"
#include "methodfactory.h"

#define MIN_PIVOT_SELECT_DATA_QTY 10
#define MAX_PIVOT_SELECT_ATTEMPTS 5

namespace similarity {

using std::string;
using std::stringstream;
using std::endl;
using std::cout;
using std::cerr;

template <typename dist_t, typename SearchOracle>
VPTree<dist_t, SearchOracle>::VPTree(
                       bool  PrintProgress,
                       Space<dist_t>& space,
                       const ObjectVector& data,
                       bool use_random_center) :
                              space_(space),
                              data_(data),
                              PrintProgress_(PrintProgress),
                              use_random_center_(use_random_center),
                              max_pivot_select_attempts_(MAX_PIVOT_SELECT_ATTEMPTS),
                              oracle_(space, data, PrintProgress),
                              QueryTimeParams_(oracle_.GetQueryTimeParamNames()) {
                                QueryTimeParams_.push_back("maxLeavesToVisit");
                              }

template <typename dist_t, typename SearchOracle>
void VPTree<dist_t, SearchOracle>::CreateIndex(const AnyParams& IndexParams) {
  AnyParamManager pmgr(IndexParams);

  pmgr.GetParamOptional("bucketSize", BucketSize_, 50);
  pmgr.GetParamOptional("chunkBucket", ChunkBucket_, true);
  pmgr.GetParamOptional("selectPivotAttempts", max_pivot_select_attempts_, MAX_PIVOT_SELECT_ATTEMPTS);

  CHECK_MSG(max_pivot_select_attempts_ >= 1, "selectPivotAttempts should be >=1");

  LOG(LIB_INFO) << "bucketSize          = " << BucketSize_;
  LOG(LIB_INFO) << "chunkBucket         = " << ChunkBucket_;
  LOG(LIB_INFO) << "selectPivotAttempts = " << max_pivot_select_attempts_;

  // Call this function *ONLY AFTER* the bucket size is obtained!
  oracle_.SetIndexTimeParams(pmgr);
  oracle_.LogParams();

  pmgr.CheckUnused();

  this->ResetQueryTimeParams(); // reset query-time parameters

  unique_ptr<ProgressDisplay>   progress_bar(PrintProgress_ ?
                                              new ProgressDisplay(data_.size(), cerr):
                                              NULL);

  root_.reset(new VPNode(0,
                     progress_bar.get(),
                     oracle_,
                     space_, data_,
                     max_pivot_select_attempts_,
                     BucketSize_, ChunkBucket_,
                     use_random_center_ /* use random center */));

  if (progress_bar) { // make it 100%
    (*progress_bar) += (progress_bar->expected_count() - progress_bar->count());
  }
}

template <typename dist_t,typename SearchOracle>
VPTree<dist_t, SearchOracle>::~VPTree() {
}

template <typename dist_t,typename SearchOracle>
const std::string VPTree<dist_t, SearchOracle>::StrDesc() const {
  return "vptree: " + SearchOracle::GetName();
}

template <typename dist_t, typename SearchOracle>
void VPTree<dist_t, SearchOracle>::Search(RangeQuery<dist_t>* query, IdType) const {
  int mx = MaxLeavesToVisit_;
  root_->GenericSearch(query, mx);
}

template <typename dist_t, typename SearchOracle>
void VPTree<dist_t, SearchOracle>::Search(KNNQuery<dist_t>* query, IdType) const {
  int mx = MaxLeavesToVisit_;
  root_->GenericSearch(query, mx);
}

template <typename dist_t, typename SearchOracle>
void VPTree<dist_t, SearchOracle>::VPNode::CreateBucket(bool ChunkBucket,
                                                        const ObjectVector& data,
                                                        ProgressDisplay* progress_bar) {
    if (ChunkBucket) {
      CreateCacheOptimizedBucket(data, CacheOptimizedBucket_, bucket_);
    } else {
      bucket_ = new ObjectVector(data);
    }
    if (progress_bar) (*progress_bar) += data.size();
}

template <typename dist_t, typename SearchOracle>
VPTree<dist_t, SearchOracle>::VPNode::VPNode(
                               unsigned level,
                               ProgressDisplay* progress_bar,
                               const SearchOracle& oracle,
                               const Space<dist_t>& space, const ObjectVector& data,
                               size_t max_pivot_select_attempts,
                               size_t BucketSize, bool ChunkBucket,
                               bool use_random_center)
    : oracle_(oracle),
      pivot_(NULL), mediandist_(0),
      left_child_(NULL), right_child_(NULL),
      bucket_(NULL), CacheOptimizedBucket_(NULL)
{
  CHECK(!data.empty());

  if (!data.empty() && data.size() <= BucketSize) {
    CreateBucket(ChunkBucket, data, progress_bar);
    return;
  }

  if (data.size() >= 2) {
    unsigned bestDP = 0;
    float    largestSIGMA = 0;
    DistObjectPairVector<dist_t> dpARR[max_pivot_select_attempts];

    // To compute StdDev we need at least 2 points not counting the pivot
    size_t maxAtt = data.size() >= max(3, MIN_PIVOT_SELECT_DATA_QTY) ? max_pivot_select_attempts : 1;

    vector<double> dists(data.size());
    for (size_t att = 0; att < maxAtt; ++att) {
      dpARR[att].reserve(data.size());
      const size_t  currPivotIndex = SelectVantagePoint(data, use_random_center);
      const Object* pCurrPivot = data[currPivotIndex];
      for (size_t i = 0; i < data.size(); ++i) {
        if (i == currPivotIndex) {
          continue;
        }
        // Distance can be asymmetric, the pivot is always on the left side!
        dist_t d = space.IndexTimeDistance(pCurrPivot, data[i]);
        dists[i] = d;
        dpARR[att].emplace_back(d, data[i]);
      }

      double sigma = StdDev(&dists[0], dists.size());
      if (att == 0 || sigma > largestSIGMA) {
        //LOG(LIB_INFO) << " ### " << largestSIGMA << " -> "  << sigma << " att=" << att << " data.size()=" << data.size();
        largestSIGMA = sigma;
        bestDP = att;
        pivot_ = pCurrPivot;
        std::sort(dpARR[att].begin(), dpARR[att].end(), DistObjectPairAscComparator<dist_t>());
      }
    }

    DistObjectPairVector<dist_t>& dp = dpARR[bestDP];
    DistObjectPair<dist_t>  medianDistObj = GetMedian(dp);
    mediandist_ = medianDistObj.first;

    ObjectVector left;
    ObjectVector right;

    for (auto it = dp.begin(); it != dp.end(); ++it) {
      const Object* v = it->second;

      /*
       * Note that here we compare a pair (distance, pointer)
       * If distances are equal, pointers are compared.
       * Thus, we would get a balanced split, even if the median
       * occurs many times in the array dp[].
       */
      if (*it < medianDistObj) {
        left.push_back(v);
      } else {
        right.push_back(v);
      }
    }

    /*
     * Sometimes, e.g.., for integer-valued distances,
     * mediandist_ will be non-discriminative. In this case
     * it is more efficient to put everything into a single bucket.
     */
    size_t LeastSize = dp.size() / BalanceConst;

    if (left.size() < LeastSize || right.size() < LeastSize) {
        CreateBucket(ChunkBucket, data, progress_bar);
        return;
    }

    if (!left.empty()) {
      left_child_ = new VPNode(level + 1, progress_bar, oracle_, space, left, max_pivot_select_attempts, BucketSize, ChunkBucket, use_random_center);
    }

    if (!right.empty()) {
      right_child_ = new VPNode(level + 1, progress_bar, oracle_, space, right, max_pivot_select_attempts, BucketSize, ChunkBucket, use_random_center);
    }
  } else {
    CHECK_MSG(data.size() == 1, "Bug: expect the subset to contain exactly one element!");
    pivot_ = data[0];
  }
}

template <typename dist_t, typename SearchOracle>
VPTree<dist_t, SearchOracle>::VPNode::~VPNode() {
  delete left_child_;
  delete right_child_;
  ClearBucket(CacheOptimizedBucket_, bucket_);
}

template <typename dist_t, typename SearchOracle>
template <typename QueryType>
void VPTree<dist_t, SearchOracle>::VPNode::GenericSearch(QueryType* query,
                                                         int& MaxLeavesToVisit) const {
  if (MaxLeavesToVisit <= 0) return; // early termination
  if (bucket_) {
    --MaxLeavesToVisit;

    for (unsigned i = 0; i < bucket_->size(); ++i) {
      const Object* Obj = (*bucket_)[i];
      dist_t distQC = query->DistanceObjLeft(Obj);
      query->CheckAndAddToResult(distQC, Obj);
    }
    return;
  }

  // Distance can be asymmetric, the pivot is always the left argument (see the function that creates the node)!
  dist_t distQC = query->DistanceObjLeft(pivot_);
  query->CheckAndAddToResult(distQC, pivot_);

  if (distQC < mediandist_) {      // the query is inside
    // then first check inside
    if (left_child_ != NULL && oracle_.Classify(distQC, query->Radius(), mediandist_) != kVisitRight)
       left_child_->GenericSearch(query, MaxLeavesToVisit);

    /*
     * After potentially visiting the left child, we need to reclassify the node,
     * because the query radius might have decreased.
     */

    // after that outside
    if (right_child_ != NULL && oracle_.Classify(distQC, query->Radius(), mediandist_) != kVisitLeft)
       right_child_->GenericSearch(query, MaxLeavesToVisit);
  } else {                         // the query is outside
    // then first check outside
    if (right_child_ != NULL && oracle_.Classify(distQC, query->Radius(), mediandist_) != kVisitLeft)
       right_child_->GenericSearch(query, MaxLeavesToVisit);

    /*
     * After potentially visiting the left child, we need to reclassify the node,
     * because the query radius might have decreased.
     */

    // after that inside
    if (left_child_ != NULL && oracle_.Classify(distQC, query->Radius(), mediandist_) != kVisitRight)
      left_child_->GenericSearch(query, MaxLeavesToVisit);
  }
}

template class VPTree<float, PolynomialPruner<float> >;
template class VPTree<double, PolynomialPruner<double> >;
template class VPTree<int, PolynomialPruner<int> >;

}   // namespace similarity

<commit_msg>Fix windows build<commit_after>/**
 * Non-metric Space Library
 *
 * Authors: Bilegsaikhan Naidan (https://github.com/bileg), Leonid Boytsov (http://boytsov.info).
 * With contributions from Lawrence Cayton (http://lcayton.com/) and others.
 *
 * For the complete list of contributors and further details see:
 * https://github.com/searchivarius/NonMetricSpaceLib
 *
 * Copyright (c) 2014
 *
 * This code is released under the
 * Apache License Version 2.0 http://www.apache.org/licenses/.
 *
 */
#include <algorithm>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cmath>

#include "space.h"
#include "rangequery.h"
#include "knnquery.h"
#include "searchoracle.h"
#include "method/vptree.h"
#include "method/vptree_utils.h"
#include "methodfactory.h"

#define MIN_PIVOT_SELECT_DATA_QTY 10
#define MAX_PIVOT_SELECT_ATTEMPTS 5

namespace similarity {

using std::string;
using std::stringstream;
using std::endl;
using std::cout;
using std::cerr;

template <typename dist_t, typename SearchOracle>
VPTree<dist_t, SearchOracle>::VPTree(
                       bool  PrintProgress,
                       Space<dist_t>& space,
                       const ObjectVector& data,
                       bool use_random_center) :
                              space_(space),
                              data_(data),
                              PrintProgress_(PrintProgress),
                              use_random_center_(use_random_center),
                              max_pivot_select_attempts_(MAX_PIVOT_SELECT_ATTEMPTS),
                              oracle_(space, data, PrintProgress),
                              QueryTimeParams_(oracle_.GetQueryTimeParamNames()) {
                                QueryTimeParams_.push_back("maxLeavesToVisit");
                              }

template <typename dist_t, typename SearchOracle>
void VPTree<dist_t, SearchOracle>::CreateIndex(const AnyParams& IndexParams) {
  AnyParamManager pmgr(IndexParams);

  pmgr.GetParamOptional("bucketSize", BucketSize_, 50);
  pmgr.GetParamOptional("chunkBucket", ChunkBucket_, true);
  pmgr.GetParamOptional("selectPivotAttempts", max_pivot_select_attempts_, MAX_PIVOT_SELECT_ATTEMPTS);

  CHECK_MSG(max_pivot_select_attempts_ >= 1, "selectPivotAttempts should be >=1");

  LOG(LIB_INFO) << "bucketSize          = " << BucketSize_;
  LOG(LIB_INFO) << "chunkBucket         = " << ChunkBucket_;
  LOG(LIB_INFO) << "selectPivotAttempts = " << max_pivot_select_attempts_;

  // Call this function *ONLY AFTER* the bucket size is obtained!
  oracle_.SetIndexTimeParams(pmgr);
  oracle_.LogParams();

  pmgr.CheckUnused();

  this->ResetQueryTimeParams(); // reset query-time parameters

  unique_ptr<ProgressDisplay>   progress_bar(PrintProgress_ ?
                                              new ProgressDisplay(data_.size(), cerr):
                                              NULL);

  root_.reset(new VPNode(0,
                     progress_bar.get(),
                     oracle_,
                     space_, data_,
                     max_pivot_select_attempts_,
                     BucketSize_, ChunkBucket_,
                     use_random_center_ /* use random center */));

  if (progress_bar) { // make it 100%
    (*progress_bar) += (progress_bar->expected_count() - progress_bar->count());
  }
}

template <typename dist_t,typename SearchOracle>
VPTree<dist_t, SearchOracle>::~VPTree() {
}

template <typename dist_t,typename SearchOracle>
const std::string VPTree<dist_t, SearchOracle>::StrDesc() const {
  return "vptree: " + SearchOracle::GetName();
}

template <typename dist_t, typename SearchOracle>
void VPTree<dist_t, SearchOracle>::Search(RangeQuery<dist_t>* query, IdType) const {
  int mx = MaxLeavesToVisit_;
  root_->GenericSearch(query, mx);
}

template <typename dist_t, typename SearchOracle>
void VPTree<dist_t, SearchOracle>::Search(KNNQuery<dist_t>* query, IdType) const {
  int mx = MaxLeavesToVisit_;
  root_->GenericSearch(query, mx);
}

template <typename dist_t, typename SearchOracle>
void VPTree<dist_t, SearchOracle>::VPNode::CreateBucket(bool ChunkBucket,
                                                        const ObjectVector& data,
                                                        ProgressDisplay* progress_bar) {
    if (ChunkBucket) {
      CreateCacheOptimizedBucket(data, CacheOptimizedBucket_, bucket_);
    } else {
      bucket_ = new ObjectVector(data);
    }
    if (progress_bar) (*progress_bar) += data.size();
}

template <typename dist_t, typename SearchOracle>
VPTree<dist_t, SearchOracle>::VPNode::VPNode(
                               unsigned level,
                               ProgressDisplay* progress_bar,
                               const SearchOracle& oracle,
                               const Space<dist_t>& space, const ObjectVector& data,
                               size_t max_pivot_select_attempts,
                               size_t BucketSize, bool ChunkBucket,
                               bool use_random_center)
    : oracle_(oracle),
      pivot_(NULL), mediandist_(0),
      left_child_(NULL), right_child_(NULL),
      bucket_(NULL), CacheOptimizedBucket_(NULL)
{
  CHECK(!data.empty());

  if (!data.empty() && data.size() <= BucketSize) {
    CreateBucket(ChunkBucket, data, progress_bar);
    return;
  }

  if (data.size() >= 2) {
    unsigned bestDP = 0;
    float    largestSIGMA = 0;
    vector<DistObjectPairVector<dist_t>> dpARR(max_pivot_select_attempts);

    // To compute StdDev we need at least 2 points not counting the pivot
    size_t maxAtt = data.size() >= max(3, MIN_PIVOT_SELECT_DATA_QTY) ? max_pivot_select_attempts : 1;

    vector<double> dists(data.size());
    for (size_t att = 0; att < maxAtt; ++att) {
      dpARR[att].reserve(data.size());
      const size_t  currPivotIndex = SelectVantagePoint(data, use_random_center);
      const Object* pCurrPivot = data[currPivotIndex];
      for (size_t i = 0; i < data.size(); ++i) {
        if (i == currPivotIndex) {
          continue;
        }
        // Distance can be asymmetric, the pivot is always on the left side!
        dist_t d = space.IndexTimeDistance(pCurrPivot, data[i]);
        dists[i] = d;
        dpARR[att].emplace_back(d, data[i]);
      }

      double sigma = StdDev(&dists[0], dists.size());
      if (att == 0 || sigma > largestSIGMA) {
        //LOG(LIB_INFO) << " ### " << largestSIGMA << " -> "  << sigma << " att=" << att << " data.size()=" << data.size();
        largestSIGMA = sigma;
        bestDP = att;
        pivot_ = pCurrPivot;
        std::sort(dpARR[att].begin(), dpARR[att].end(), DistObjectPairAscComparator<dist_t>());
      }
    }

    DistObjectPairVector<dist_t>& dp = dpARR[bestDP];
    DistObjectPair<dist_t>  medianDistObj = GetMedian(dp);
    mediandist_ = medianDistObj.first;

    ObjectVector left;
    ObjectVector right;

    for (auto it = dp.begin(); it != dp.end(); ++it) {
      const Object* v = it->second;

      /*
       * Note that here we compare a pair (distance, pointer)
       * If distances are equal, pointers are compared.
       * Thus, we would get a balanced split, even if the median
       * occurs many times in the array dp[].
       */
      if (*it < medianDistObj) {
        left.push_back(v);
      } else {
        right.push_back(v);
      }
    }

    /*
     * Sometimes, e.g.., for integer-valued distances,
     * mediandist_ will be non-discriminative. In this case
     * it is more efficient to put everything into a single bucket.
     */
    size_t LeastSize = dp.size() / BalanceConst;

    if (left.size() < LeastSize || right.size() < LeastSize) {
        CreateBucket(ChunkBucket, data, progress_bar);
        return;
    }

    if (!left.empty()) {
      left_child_ = new VPNode(level + 1, progress_bar, oracle_, space, left, max_pivot_select_attempts, BucketSize, ChunkBucket, use_random_center);
    }

    if (!right.empty()) {
      right_child_ = new VPNode(level + 1, progress_bar, oracle_, space, right, max_pivot_select_attempts, BucketSize, ChunkBucket, use_random_center);
    }
  } else {
    CHECK_MSG(data.size() == 1, "Bug: expect the subset to contain exactly one element!");
    pivot_ = data[0];
  }
}

template <typename dist_t, typename SearchOracle>
VPTree<dist_t, SearchOracle>::VPNode::~VPNode() {
  delete left_child_;
  delete right_child_;
  ClearBucket(CacheOptimizedBucket_, bucket_);
}

template <typename dist_t, typename SearchOracle>
template <typename QueryType>
void VPTree<dist_t, SearchOracle>::VPNode::GenericSearch(QueryType* query,
                                                         int& MaxLeavesToVisit) const {
  if (MaxLeavesToVisit <= 0) return; // early termination
  if (bucket_) {
    --MaxLeavesToVisit;

    for (unsigned i = 0; i < bucket_->size(); ++i) {
      const Object* Obj = (*bucket_)[i];
      dist_t distQC = query->DistanceObjLeft(Obj);
      query->CheckAndAddToResult(distQC, Obj);
    }
    return;
  }

  // Distance can be asymmetric, the pivot is always the left argument (see the function that creates the node)!
  dist_t distQC = query->DistanceObjLeft(pivot_);
  query->CheckAndAddToResult(distQC, pivot_);

  if (distQC < mediandist_) {      // the query is inside
    // then first check inside
    if (left_child_ != NULL && oracle_.Classify(distQC, query->Radius(), mediandist_) != kVisitRight)
       left_child_->GenericSearch(query, MaxLeavesToVisit);

    /*
     * After potentially visiting the left child, we need to reclassify the node,
     * because the query radius might have decreased.
     */

    // after that outside
    if (right_child_ != NULL && oracle_.Classify(distQC, query->Radius(), mediandist_) != kVisitLeft)
       right_child_->GenericSearch(query, MaxLeavesToVisit);
  } else {                         // the query is outside
    // then first check outside
    if (right_child_ != NULL && oracle_.Classify(distQC, query->Radius(), mediandist_) != kVisitLeft)
       right_child_->GenericSearch(query, MaxLeavesToVisit);

    /*
     * After potentially visiting the left child, we need to reclassify the node,
     * because the query radius might have decreased.
     */

    // after that inside
    if (left_child_ != NULL && oracle_.Classify(distQC, query->Radius(), mediandist_) != kVisitRight)
      left_child_->GenericSearch(query, MaxLeavesToVisit);
  }
}

template class VPTree<float, PolynomialPruner<float> >;
template class VPTree<double, PolynomialPruner<double> >;
template class VPTree<int, PolynomialPruner<int> >;

}   // namespace similarity

<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "skia/ext/bitmap_platform_device_mac.h"

#include <time.h>

#include "SkMatrix.h"
#include "SkRegion.h"
#include "SkTypes.h"
#include "SkUtils.h"

#include "skia/ext/skia_utils_mac.h"

namespace skia {

namespace {

// Constrains position and size to fit within available_size. If |size| is -1,
// all the |available_size| is used. Returns false if the position is out of
// |available_size|.
bool Constrain(int available_size, int* position, int *size) {
  if (*size < -2)
    return false;

  if (*position < 0) {
    if (*size != -1)
      *size += *position;
    *position = 0;
  }
  if (*size == 0 || *position >= available_size)
    return false;

  if (*size > 0) {
    int overflow = (*position + *size) - available_size;
    if (overflow > 0) {
      *size -= overflow;
    }
  } else {
    // Fill up available size.
    *size = available_size - *position;
  }
  return true;
}

}  // namespace

class BitmapPlatformDeviceMac::BitmapPlatformDeviceMacData : public SkRefCnt {
 public:
  explicit BitmapPlatformDeviceMacData(CGContextRef bitmap);

  // Create/destroy CoreGraphics context for our bitmap data.
  CGContextRef GetBitmapContext() {
    LoadConfig();
    return bitmap_context_;
  }

  void ReleaseBitmapContext() {
    SkASSERT(bitmap_context_);
    CGContextRelease(bitmap_context_);
    bitmap_context_ = NULL;
  }

  // Sets the transform and clip operations. This will not update the CGContext,
  // but will mark the config as dirty. The next call of LoadConfig will
  // pick up these changes.
  void SetMatrixClip(const SkMatrix& transform, const SkRegion& region);

  // Loads the current transform and clip into the DC. Can be called even when
  // |bitmap_context_| is NULL (will be a NOP).
  void LoadConfig();

  // Lazily-created graphics context used to draw into the bitmap.
  CGContextRef bitmap_context_;

  // True when there is a transform or clip that has not been set to the
  // CGContext.  The CGContext is retrieved for every text operation, and the
  // transform and clip do not change as much. We can save time by not loading
  // the clip and transform for every one.
  bool config_dirty_;

  // Translation assigned to the CGContext: we need to keep track of this
  // separately so it can be updated even if the CGContext isn't created yet.
  SkMatrix transform_;

  // The current clipping
  SkRegion clip_region_;

 private:
  friend class base::RefCounted<BitmapPlatformDeviceMacData>;
  ~BitmapPlatformDeviceMacData() {
    if (bitmap_context_)
      CGContextRelease(bitmap_context_);
  }

  // Disallow copy & assign.
  BitmapPlatformDeviceMacData(const BitmapPlatformDeviceMacData&);
  BitmapPlatformDeviceMacData& operator=(const BitmapPlatformDeviceMacData&);
};

BitmapPlatformDeviceMac::\
    BitmapPlatformDeviceMacData::BitmapPlatformDeviceMacData(
    CGContextRef bitmap)
    : bitmap_context_(bitmap),
      config_dirty_(true) {  // Want to load the config next time.
  SkASSERT(bitmap_context_);
  // Initialize the clip region to the entire bitmap.

  SkIRect rect;
  rect.set(0, 0,
           CGBitmapContextGetWidth(bitmap_context_),
           CGBitmapContextGetHeight(bitmap_context_));
  clip_region_ = SkRegion(rect);
  transform_.reset();
  CGContextRetain(bitmap_context_);
}

void BitmapPlatformDeviceMac::BitmapPlatformDeviceMacData::SetMatrixClip(
    const SkMatrix& transform,
    const SkRegion& region) {
  transform_ = transform;
  clip_region_ = region;
  config_dirty_ = true;
}

void BitmapPlatformDeviceMac::BitmapPlatformDeviceMacData::LoadConfig() {
  if (!config_dirty_ || !bitmap_context_)
    return;  // Nothing to do.
  config_dirty_ = false;

  // Transform.
  SkMatrix t(transform_);
  LoadTransformToCGContext(bitmap_context_, t);
  t.setTranslateX(-t.getTranslateX());
  t.setTranslateY(-t.getTranslateY());
  LoadClippingRegionToCGContext(bitmap_context_, clip_region_, t);
}

// We use this static factory function instead of the regular constructor so
// that we can create the pixel data before calling the constructor. This is
// required so that we can call the base class' constructor with the pixel
// data.
BitmapPlatformDeviceMac* BitmapPlatformDeviceMac::Create(CGContextRef context,
                                                         int width,
                                                         int height,
                                                         bool is_opaque) {
  void* data = malloc(height * width * 4);
  if (!data) return NULL;

  SkBitmap bitmap;
  bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
  bitmap.setPixels(data);

  // Note: The Windows implementation clears the Bitmap later on.
  // This bears mentioning since removal of this line makes the
  // unit tests only fail periodically (or when MallocPreScribble is set).
  bitmap.eraseARGB(0, 0, 0, 0);

  bitmap.setIsOpaque(is_opaque);

  if (is_opaque) {
#ifndef NDEBUG
    // To aid in finding bugs, we set the background color to something
    // obviously wrong so it will be noticable when it is not cleared
    bitmap.eraseARGB(255, 0, 255, 128);  // bright bluish green
#endif
  }

  if (!context) {
    CGColorSpaceRef color_space =
      CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    // allocate a bitmap context with 4 components per pixel (BGRA). Apple
    // recommends these flags for improved CG performance.
    context =
      CGBitmapContextCreate(data, width, height, 8, width*4,
                            color_space,
                            kCGImageAlphaPremultipliedFirst |
                                kCGBitmapByteOrder32Host);

    // Change the coordinate system to match WebCore's
    CGContextTranslateCTM(context, 0, height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGColorSpaceRelease(color_space);
  }

  // The device object will take ownership of the graphics context.
  return new BitmapPlatformDeviceMac(
      new BitmapPlatformDeviceMacData(context), bitmap);
}

// The device will own the bitmap, which corresponds to also owning the pixel
// data. Therefore, we do not transfer ownership to the SkDevice's bitmap.
BitmapPlatformDeviceMac::BitmapPlatformDeviceMac(
    BitmapPlatformDeviceMacData* data, const SkBitmap& bitmap)
    : PlatformDeviceMac(bitmap),
      data_(data) {
}

// The copy constructor just adds another reference to the underlying data.
// We use a const cast since the default Skia definitions don't define the
// proper constedness that we expect (accessBitmap should really be const).
BitmapPlatformDeviceMac::BitmapPlatformDeviceMac(
    const BitmapPlatformDeviceMac& other)
    : PlatformDeviceMac(
          const_cast<BitmapPlatformDeviceMac&>(other).accessBitmap(true)),
      data_(other.data_) {
  data_->ref();
}

BitmapPlatformDeviceMac::~BitmapPlatformDeviceMac() {
  data_->unref();
}

BitmapPlatformDeviceMac& BitmapPlatformDeviceMac::operator=(
    const BitmapPlatformDeviceMac& other) {
  data_ = other.data_;
  data_->ref();
  return *this;
}

CGContextRef BitmapPlatformDeviceMac::GetBitmapContext() {
  return data_->GetBitmapContext();
}

void BitmapPlatformDeviceMac::setMatrixClip(const SkMatrix& transform,
                                            const SkRegion& region) {
  data_->SetMatrixClip(transform, region);
}

void BitmapPlatformDeviceMac::DrawToContext(CGContextRef context, int x, int y,
                                            const CGRect* src_rect) {
  bool created_dc = false;
  if (!data_->bitmap_context_) {
    created_dc = true;
    GetBitmapContext();
  }

  // this should not make a copy of the bits, since we're not doing
  // anything to trigger copy on write
  CGImageRef image = CGBitmapContextCreateImage(data_->bitmap_context_);
  CGRect bounds;
  if (src_rect) {
    bounds = *src_rect;
    bounds.origin.x = x;
    bounds.origin.y = y;
    CGImageRef sub_image = CGImageCreateWithImageInRect(image, *src_rect);
    CGContextDrawImage(context, bounds, sub_image);
    CGImageRelease(sub_image);
  } else {
    bounds.origin.x = 0;
    bounds.origin.y = 0;
    bounds.size.width = width();
    bounds.size.height = height();
    CGContextDrawImage(context, bounds, image);
  }
  CGImageRelease(image);

  if (created_dc)
    data_->ReleaseBitmapContext();
}

// Returns the color value at the specified location.
SkColor BitmapPlatformDeviceMac::getColorAt(int x, int y) {
  const SkBitmap& bitmap = accessBitmap(true);
  SkAutoLockPixels lock(bitmap);
  uint32_t* data = bitmap.getAddr32(0, 0);
  return static_cast<SkColor>(data[x + y * width()]);
}

void BitmapPlatformDeviceMac::onAccessBitmap(SkBitmap*) {
  // Not needed in CoreGraphics
}

void BitmapPlatformDeviceMac::processPixels(int x, int y,
                                            int width, int height,
                                            adjustAlpha adjustor) {
  const SkBitmap& bitmap = accessBitmap(true);
  SkMatrix& matrix = data_->transform_;
  int bitmap_start_x = SkScalarRound(matrix.getTranslateX()) + x;
  int bitmap_start_y = SkScalarRound(matrix.getTranslateY()) + y;

  SkAutoLockPixels lock(bitmap);
  if (Constrain(bitmap.width(), &bitmap_start_x, &width) &&
      Constrain(bitmap.height(), &bitmap_start_y, &height)) {
    uint32_t* data = bitmap.getAddr32(0, 0);
    size_t row_words = bitmap.rowBytes() / 4;
    for (int i = 0; i < height; i++) {
      size_t offset = (i + bitmap_start_y) * row_words + bitmap_start_x;
      for (int j = 0; j < width; j++) {
        adjustor(data + offset + j);
      }
    }
  }
}

}  // namespace skia

<commit_msg>Fix memory leak of "screen size bitmap" (e.g. 1.5M if 750x548)  that happened on *every page view* on OSX.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "skia/ext/bitmap_platform_device_mac.h"

#include <time.h>

#include "SkMatrix.h"
#include "SkRegion.h"
#include "SkTypes.h"
#include "SkUtils.h"

#include "skia/ext/skia_utils_mac.h"

namespace skia {

namespace {

// Constrains position and size to fit within available_size. If |size| is -1,
// all the |available_size| is used. Returns false if the position is out of
// |available_size|.
bool Constrain(int available_size, int* position, int *size) {
  if (*size < -2)
    return false;

  if (*position < 0) {
    if (*size != -1)
      *size += *position;
    *position = 0;
  }
  if (*size == 0 || *position >= available_size)
    return false;

  if (*size > 0) {
    int overflow = (*position + *size) - available_size;
    if (overflow > 0) {
      *size -= overflow;
    }
  } else {
    // Fill up available size.
    *size = available_size - *position;
  }
  return true;
}

}  // namespace

class BitmapPlatformDeviceMac::BitmapPlatformDeviceMacData : public SkRefCnt {
 public:
  explicit BitmapPlatformDeviceMacData(CGContextRef bitmap);

  // Create/destroy CoreGraphics context for our bitmap data.
  CGContextRef GetBitmapContext() {
    LoadConfig();
    return bitmap_context_;
  }

  void ReleaseBitmapContext() {
    SkASSERT(bitmap_context_);
    CGContextRelease(bitmap_context_);
    bitmap_context_ = NULL;
  }

  // Sets the transform and clip operations. This will not update the CGContext,
  // but will mark the config as dirty. The next call of LoadConfig will
  // pick up these changes.
  void SetMatrixClip(const SkMatrix& transform, const SkRegion& region);

  // Loads the current transform and clip into the DC. Can be called even when
  // |bitmap_context_| is NULL (will be a NOP).
  void LoadConfig();

  // Lazily-created graphics context used to draw into the bitmap.
  CGContextRef bitmap_context_;

  // True when there is a transform or clip that has not been set to the
  // CGContext.  The CGContext is retrieved for every text operation, and the
  // transform and clip do not change as much. We can save time by not loading
  // the clip and transform for every one.
  bool config_dirty_;

  // Translation assigned to the CGContext: we need to keep track of this
  // separately so it can be updated even if the CGContext isn't created yet.
  SkMatrix transform_;

  // The current clipping
  SkRegion clip_region_;

 private:
  friend class base::RefCounted<BitmapPlatformDeviceMacData>;
  ~BitmapPlatformDeviceMacData() {
    if (bitmap_context_)
      CGContextRelease(bitmap_context_);
  }

  // Disallow copy & assign.
  BitmapPlatformDeviceMacData(const BitmapPlatformDeviceMacData&);
  BitmapPlatformDeviceMacData& operator=(const BitmapPlatformDeviceMacData&);
};

BitmapPlatformDeviceMac::\
    BitmapPlatformDeviceMacData::BitmapPlatformDeviceMacData(
    CGContextRef bitmap)
    : bitmap_context_(bitmap),
      config_dirty_(true) {  // Want to load the config next time.
  SkASSERT(bitmap_context_);
  // Initialize the clip region to the entire bitmap.

  SkIRect rect;
  rect.set(0, 0,
           CGBitmapContextGetWidth(bitmap_context_),
           CGBitmapContextGetHeight(bitmap_context_));
  clip_region_ = SkRegion(rect);
  transform_.reset();
  CGContextRetain(bitmap_context_);
}

void BitmapPlatformDeviceMac::BitmapPlatformDeviceMacData::SetMatrixClip(
    const SkMatrix& transform,
    const SkRegion& region) {
  transform_ = transform;
  clip_region_ = region;
  config_dirty_ = true;
}

void BitmapPlatformDeviceMac::BitmapPlatformDeviceMacData::LoadConfig() {
  if (!config_dirty_ || !bitmap_context_)
    return;  // Nothing to do.
  config_dirty_ = false;

  // Transform.
  SkMatrix t(transform_);
  LoadTransformToCGContext(bitmap_context_, t);
  t.setTranslateX(-t.getTranslateX());
  t.setTranslateY(-t.getTranslateY());
  LoadClippingRegionToCGContext(bitmap_context_, clip_region_, t);
}

// We use this static factory function instead of the regular constructor so
// that we can create the pixel data before calling the constructor. This is
// required so that we can call the base class' constructor with the pixel
// data.
BitmapPlatformDeviceMac* BitmapPlatformDeviceMac::Create(CGContextRef context,
                                                         int width,
                                                         int height,
                                                         bool is_opaque) {
  SkBitmap bitmap;
  bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
  if (bitmap.allocPixels() != true)
    return NULL;
  void* data = bitmap.getPixels();

  // Note: The Windows implementation clears the Bitmap later on.
  // This bears mentioning since removal of this line makes the
  // unit tests only fail periodically (or when MallocPreScribble is set).
  bitmap.eraseARGB(0, 0, 0, 0);

  bitmap.setIsOpaque(is_opaque);

  if (is_opaque) {
#ifndef NDEBUG
    // To aid in finding bugs, we set the background color to something
    // obviously wrong so it will be noticable when it is not cleared
    bitmap.eraseARGB(255, 0, 255, 128);  // bright bluish green
#endif
  }

  if (!context) {
    CGColorSpaceRef color_space =
      CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    // allocate a bitmap context with 4 components per pixel (BGRA). Apple
    // recommends these flags for improved CG performance.
    context =
      CGBitmapContextCreate(data, width, height, 8, width*4,
                            color_space,
                            kCGImageAlphaPremultipliedFirst |
                                kCGBitmapByteOrder32Host);

    // Change the coordinate system to match WebCore's
    CGContextTranslateCTM(context, 0, height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGColorSpaceRelease(color_space);
  }

  // The device object will take ownership of the graphics context.
  return new BitmapPlatformDeviceMac(
      new BitmapPlatformDeviceMacData(context), bitmap);
}

// The device will own the bitmap, which corresponds to also owning the pixel
// data. Therefore, we do not transfer ownership to the SkDevice's bitmap.
BitmapPlatformDeviceMac::BitmapPlatformDeviceMac(
    BitmapPlatformDeviceMacData* data, const SkBitmap& bitmap)
    : PlatformDeviceMac(bitmap),
      data_(data) {
}

// The copy constructor just adds another reference to the underlying data.
// We use a const cast since the default Skia definitions don't define the
// proper constedness that we expect (accessBitmap should really be const).
BitmapPlatformDeviceMac::BitmapPlatformDeviceMac(
    const BitmapPlatformDeviceMac& other)
    : PlatformDeviceMac(
          const_cast<BitmapPlatformDeviceMac&>(other).accessBitmap(true)),
      data_(other.data_) {
  data_->ref();
}

BitmapPlatformDeviceMac::~BitmapPlatformDeviceMac() {
  data_->unref();
}

BitmapPlatformDeviceMac& BitmapPlatformDeviceMac::operator=(
    const BitmapPlatformDeviceMac& other) {
  data_ = other.data_;
  data_->ref();
  return *this;
}

CGContextRef BitmapPlatformDeviceMac::GetBitmapContext() {
  return data_->GetBitmapContext();
}

void BitmapPlatformDeviceMac::setMatrixClip(const SkMatrix& transform,
                                            const SkRegion& region) {
  data_->SetMatrixClip(transform, region);
}

void BitmapPlatformDeviceMac::DrawToContext(CGContextRef context, int x, int y,
                                            const CGRect* src_rect) {
  bool created_dc = false;
  if (!data_->bitmap_context_) {
    created_dc = true;
    GetBitmapContext();
  }

  // this should not make a copy of the bits, since we're not doing
  // anything to trigger copy on write
  CGImageRef image = CGBitmapContextCreateImage(data_->bitmap_context_);
  CGRect bounds;
  if (src_rect) {
    bounds = *src_rect;
    bounds.origin.x = x;
    bounds.origin.y = y;
    CGImageRef sub_image = CGImageCreateWithImageInRect(image, *src_rect);
    CGContextDrawImage(context, bounds, sub_image);
    CGImageRelease(sub_image);
  } else {
    bounds.origin.x = 0;
    bounds.origin.y = 0;
    bounds.size.width = width();
    bounds.size.height = height();
    CGContextDrawImage(context, bounds, image);
  }
  CGImageRelease(image);

  if (created_dc)
    data_->ReleaseBitmapContext();
}

// Returns the color value at the specified location.
SkColor BitmapPlatformDeviceMac::getColorAt(int x, int y) {
  const SkBitmap& bitmap = accessBitmap(true);
  SkAutoLockPixels lock(bitmap);
  uint32_t* data = bitmap.getAddr32(0, 0);
  return static_cast<SkColor>(data[x + y * width()]);
}

void BitmapPlatformDeviceMac::onAccessBitmap(SkBitmap*) {
  // Not needed in CoreGraphics
}

void BitmapPlatformDeviceMac::processPixels(int x, int y,
                                            int width, int height,
                                            adjustAlpha adjustor) {
  const SkBitmap& bitmap = accessBitmap(true);
  SkMatrix& matrix = data_->transform_;
  int bitmap_start_x = SkScalarRound(matrix.getTranslateX()) + x;
  int bitmap_start_y = SkScalarRound(matrix.getTranslateY()) + y;

  SkAutoLockPixels lock(bitmap);
  if (Constrain(bitmap.width(), &bitmap_start_x, &width) &&
      Constrain(bitmap.height(), &bitmap_start_y, &height)) {
    uint32_t* data = bitmap.getAddr32(0, 0);
    size_t row_words = bitmap.rowBytes() / 4;
    for (int i = 0; i < height; i++) {
      size_t offset = (i + bitmap_start_y) * row_words + bitmap_start_x;
      for (int j = 0; j < width; j++) {
        adjustor(data + offset + j);
      }
    }
  }
}

}  // namespace skia

<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (c) 2018 Torsten Sadowski <tsadowski[at]gmx.net>            *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include <FCConfig.h>
#include "SpaceballEvent.h"

#include <QMainWindow>

#include "GuiNativeEventLinuxX11.h"

#include "GuiApplicationNativeEventAware.h"
#include <Base/Console.h>

#include <QX11Info>
#include <spnav.h>

#if QT_VERSION >= 0x050000
  #include "GuiRawInputEventFilter.h"
  #undef Bool
  #undef CursorShape
  #undef Expose
  #undef KeyPress
  #undef KeyRelease
  #undef FocusIn
  #undef FocusOut
  #undef FontChange
  #undef None
  #undef Status
  #undef Unsorted
  #undef False
  #undef True
  #undef Complex
#endif // #if QT_VERSION >= 0x050000

Gui::GuiNativeEvent::GuiNativeEvent(Gui::GUIApplicationNativeEventAware *app)
: GuiAbstractNativeEvent(app)
{
}

Gui::GuiNativeEvent::~GuiNativeEvent()
{
    if (spnav_close())
        Base::Console().Log("Couldn't disconnect from spacenav daemon\n");
    else
        Base::Console().Log("Disconnected from spacenav daemon\n");
}

void Gui::GuiNativeEvent::initSpaceball(QMainWindow *window)
{
    if (spnav_x11_open(QX11Info::display(), window->winId()) == -1) {
        Base::Console().Log("Couldn't connect to spacenav daemon\n");
    } else {
        Base::Console().Log("Connected to spacenav daemon\n");
		mainApp->setSpaceballPresent(true);

    #if QT_VERSION >= 0x050000
        mainApp->installNativeEventFilter(new Gui::RawInputEventFilter(&xcbEventFilter));
    #endif // #if QT_VERSION >= 0x050000
    }
}

#if QT_VERSION >= 0x050000

bool Gui::GuiNativeEvent::xcbEventFilter(void *xcb_void, long* result)
{
    Q_UNUSED(result);
    auto inst(dynamic_cast<Gui::GUIApplicationNativeEventAware *>(QApplication::instance()));
    if (!inst)
      return false;

    spnav_event navEvent;

	const xcb_client_message_event_t* xcb_ev = static_cast<const xcb_client_message_event_t*>(xcb_void);
    // Qt4 used XEvents in native event filters, but Qt5 changed to XCB.  The
    // SpaceNavigator API only works with XEvent, so we need to construct a
    // temporary XEvent with just enough information for spnav_x11_event()
    if ((xcb_ev->response_type & 0x7F) == XCB_CLIENT_MESSAGE) {
        XClientMessageEvent xev;

        xev.type = ClientMessage;
        xev.message_type = xcb_ev->type;
        memcpy(xev.data.b, xcb_ev->data.data8, sizeof(xev.data.b));
        xev.serial = 0; // These are just to squash warnings...
        xev.send_event = 0;
        xev.display = 0;
        xev.window = 0;
        xev.format = 0;

        if (!spnav_x11_event(reinterpret_cast<XEvent *>(&xev), &navEvent)) {
            return false;
        }
    } else {
        return false;
    }
    // navEvent is now initialised

    switch (navEvent.type) {
        case SPNAV_EVENT_MOTION:
        {
            motionDataArray[0] = -navEvent.motion.x;
            motionDataArray[1] = -navEvent.motion.z;
            motionDataArray[2] = -navEvent.motion.y;
            motionDataArray[3] = -navEvent.motion.rx;
            motionDataArray[4] = -navEvent.motion.rz;
            motionDataArray[5] = -navEvent.motion.ry;

            inst->postMotionEvent(motionDataArray);
            return true;
        }

        case SPNAV_EVENT_BUTTON:
        {
            auto buttonEvent(new Spaceball::ButtonEvent());
            buttonEvent->setButtonNumber(navEvent.button.bnum);
            if (navEvent.button.press) {
                buttonEvent->setButtonStatus(Spaceball::BUTTON_PRESSED);
            } else {
                buttonEvent->setButtonStatus(Spaceball::BUTTON_RELEASED);
            }
            inst->postButtonEvent(navEvent.button.bnum, navEvent.button.press);
            return true;
        }
        default:
            Base::Console().Log("Unknown spaceball event\n");
            return true;
    } // end switch (navEvent.type) {
}

#else  // if QT_VERSION >= 0x050000

bool Gui::GuiNativeEvent::x11EventFilter(XEvent *event)
{
    /*
    First we check if we have a motion flush event:
    - If there are unprocessed motion events we are in a flooding situation.
      In that case we wait with generating a Spaceball event.
    - A motion event counter of 0 indicates that FreeCAD is ready to process
      the event. A Spaceball event, using the saved motion data, is posted.
    */
    static Display* display = QX11Info::display();
    static Atom motion_flush_event = XInternAtom(display, "FCMotionFlushEvent", false);
    static int nMotionEvents = 0;

    if (event->type == ClientMessage)
    {
        Atom message_type = event->xclient.message_type;

        if (message_type == motion_flush_event)
        {
            nMotionEvents--;
            if (nMotionEvents == 0)
            {
            mainApp->postMotionEvent(motionDataArray);
            }

            return true;
        } // XEvent: motion_flush_event
    } // XEvent: ClientMessage

    /*
    From here on we deal with spacenav events only:
    - motion: The event data is saved and a self addressed flush event
              is sent through the window system (XEvent).
              In the case of an event flooding, the motion data is added up.
    - button: A Spaceball event is posted (QInputEvent).
    */
    spnav_event navEvent;
    if (!spnav_x11_event(event, &navEvent))
        return false;

    if (navEvent.type == SPNAV_EVENT_MOTION)
    {
        /*
        If the motion data of the preceding event has not been processed
        through posting an Spaceball event (flooding situation),
        the motion data provided by the incoming event is added to the saved data.
        */
    	int dx, dy, dz, drx, dry, drz;

        if (nMotionEvents == 0)
        {
            dx = 0;
            dy = 0;
            dz = 0;
            drx = 0;
            dry = 0;
            drz = 0;
        }
        else
        {
            dx = motionDataArray[0];
            dy = motionDataArray[1];
            dz = motionDataArray[2];
            drx = motionDataArray[3];
            dry = motionDataArray[4];
            drz = motionDataArray[5];
        }

        motionDataArray[0] = -navEvent.motion.x;
        motionDataArray[1] = -navEvent.motion.z;
        motionDataArray[2] = -navEvent.motion.y;
        motionDataArray[3] = -navEvent.motion.rx;
        motionDataArray[4] = -navEvent.motion.rz;
        motionDataArray[5] = -navEvent.motion.ry;

        motionDataArray[0] += dx;
        motionDataArray[1] += dy;
        motionDataArray[2] += dz;
        motionDataArray[3] += drx;
        motionDataArray[4] += dry;
        motionDataArray[5] += drz;

        /*
        Send a self addressed flush event through the window system. This will
        trigger a Spaceball event if FreeCAD is ready to do so.
        */
        nMotionEvents++;
        XClientMessageEvent flushEvent;

        flushEvent.display = display;
        flushEvent.window = event->xclient.window;
        flushEvent.type = ClientMessage;
        flushEvent.format = 8;
        flushEvent.message_type = motion_flush_event;

        XSendEvent (display, flushEvent.window, False, 0, (XEvent*)&flushEvent); // siehe spnavd, False, 0

        return true;
    }

    if (navEvent.type == SPNAV_EVENT_BUTTON)
    {
        mainApp->postButtonEvent(navEvent.button.bnum, navEvent.button.press);
        return true;
    }

    Base::Console().Log("Unknown spaceball event\n");
    return true;
}
#endif  // if/else QT_VERSION >= 0x050000

#include "3Dconnexion/moc_GuiNativeEventLinuxX11.cpp"
<commit_msg>fixes 0003913: libspnav crash on linux wayland during startup<commit_after>/***************************************************************************
 *   Copyright (c) 2018 Torsten Sadowski <tsadowski[at]gmx.net>            *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include <FCConfig.h>
#include "SpaceballEvent.h"

#include <QMainWindow>

#include "GuiNativeEventLinuxX11.h"

#include "GuiApplicationNativeEventAware.h"
#include <Base/Console.h>

#include <QX11Info>
#include <spnav.h>

#if QT_VERSION >= 0x050000
  #include "GuiRawInputEventFilter.h"
  #undef Bool
  #undef CursorShape
  #undef Expose
  #undef KeyPress
  #undef KeyRelease
  #undef FocusIn
  #undef FocusOut
  #undef FontChange
  #undef None
  #undef Status
  #undef Unsorted
  #undef False
  #undef True
  #undef Complex
#endif // #if QT_VERSION >= 0x050000

Gui::GuiNativeEvent::GuiNativeEvent(Gui::GUIApplicationNativeEventAware *app)
: GuiAbstractNativeEvent(app)
{
}

Gui::GuiNativeEvent::~GuiNativeEvent()
{
    if (spnav_close())
        Base::Console().Log("Couldn't disconnect from spacenav daemon\n");
    else
        Base::Console().Log("Disconnected from spacenav daemon\n");
}

void Gui::GuiNativeEvent::initSpaceball(QMainWindow *window)
{
#if QT_VERSION >= 0x050200
    if (!QX11Info::isPlatformX11()) {
        Base::Console().Log("Application is not running on X11\n");
        return;
    }
#endif
    if (spnav_x11_open(QX11Info::display(), window->winId()) == -1) {
        Base::Console().Log("Couldn't connect to spacenav daemon\n");
    } else {
        Base::Console().Log("Connected to spacenav daemon\n");
        mainApp->setSpaceballPresent(true);

#if QT_VERSION >= 0x050000
        mainApp->installNativeEventFilter(new Gui::RawInputEventFilter(&xcbEventFilter));
#endif // #if QT_VERSION >= 0x050000
    }
}

#if QT_VERSION >= 0x050000

bool Gui::GuiNativeEvent::xcbEventFilter(void *xcb_void, long* result)
{
    Q_UNUSED(result);
    auto inst(dynamic_cast<Gui::GUIApplicationNativeEventAware *>(QApplication::instance()));
    if (!inst)
      return false;

    spnav_event navEvent;

	const xcb_client_message_event_t* xcb_ev = static_cast<const xcb_client_message_event_t*>(xcb_void);
    // Qt4 used XEvents in native event filters, but Qt5 changed to XCB.  The
    // SpaceNavigator API only works with XEvent, so we need to construct a
    // temporary XEvent with just enough information for spnav_x11_event()
    if ((xcb_ev->response_type & 0x7F) == XCB_CLIENT_MESSAGE) {
        XClientMessageEvent xev;

        xev.type = ClientMessage;
        xev.message_type = xcb_ev->type;
        memcpy(xev.data.b, xcb_ev->data.data8, sizeof(xev.data.b));
        xev.serial = 0; // These are just to squash warnings...
        xev.send_event = 0;
        xev.display = 0;
        xev.window = 0;
        xev.format = 0;

        if (!spnav_x11_event(reinterpret_cast<XEvent *>(&xev), &navEvent)) {
            return false;
        }
    } else {
        return false;
    }
    // navEvent is now initialised

    switch (navEvent.type) {
        case SPNAV_EVENT_MOTION:
        {
            motionDataArray[0] = -navEvent.motion.x;
            motionDataArray[1] = -navEvent.motion.z;
            motionDataArray[2] = -navEvent.motion.y;
            motionDataArray[3] = -navEvent.motion.rx;
            motionDataArray[4] = -navEvent.motion.rz;
            motionDataArray[5] = -navEvent.motion.ry;

            inst->postMotionEvent(motionDataArray);
            return true;
        }

        case SPNAV_EVENT_BUTTON:
        {
            auto buttonEvent(new Spaceball::ButtonEvent());
            buttonEvent->setButtonNumber(navEvent.button.bnum);
            if (navEvent.button.press) {
                buttonEvent->setButtonStatus(Spaceball::BUTTON_PRESSED);
            } else {
                buttonEvent->setButtonStatus(Spaceball::BUTTON_RELEASED);
            }
            inst->postButtonEvent(navEvent.button.bnum, navEvent.button.press);
            return true;
        }
        default:
            Base::Console().Log("Unknown spaceball event\n");
            return true;
    } // end switch (navEvent.type) {
}

#else  // if QT_VERSION >= 0x050000

bool Gui::GuiNativeEvent::x11EventFilter(XEvent *event)
{
    /*
    First we check if we have a motion flush event:
    - If there are unprocessed motion events we are in a flooding situation.
      In that case we wait with generating a Spaceball event.
    - A motion event counter of 0 indicates that FreeCAD is ready to process
      the event. A Spaceball event, using the saved motion data, is posted.
    */
    static Display* display = QX11Info::display();
    static Atom motion_flush_event = XInternAtom(display, "FCMotionFlushEvent", false);
    static int nMotionEvents = 0;

    if (event->type == ClientMessage)
    {
        Atom message_type = event->xclient.message_type;

        if (message_type == motion_flush_event)
        {
            nMotionEvents--;
            if (nMotionEvents == 0)
            {
            mainApp->postMotionEvent(motionDataArray);
            }

            return true;
        } // XEvent: motion_flush_event
    } // XEvent: ClientMessage

    /*
    From here on we deal with spacenav events only:
    - motion: The event data is saved and a self addressed flush event
              is sent through the window system (XEvent).
              In the case of an event flooding, the motion data is added up.
    - button: A Spaceball event is posted (QInputEvent).
    */
    spnav_event navEvent;
    if (!spnav_x11_event(event, &navEvent))
        return false;

    if (navEvent.type == SPNAV_EVENT_MOTION)
    {
        /*
        If the motion data of the preceding event has not been processed
        through posting an Spaceball event (flooding situation),
        the motion data provided by the incoming event is added to the saved data.
        */
    	int dx, dy, dz, drx, dry, drz;

        if (nMotionEvents == 0)
        {
            dx = 0;
            dy = 0;
            dz = 0;
            drx = 0;
            dry = 0;
            drz = 0;
        }
        else
        {
            dx = motionDataArray[0];
            dy = motionDataArray[1];
            dz = motionDataArray[2];
            drx = motionDataArray[3];
            dry = motionDataArray[4];
            drz = motionDataArray[5];
        }

        motionDataArray[0] = -navEvent.motion.x;
        motionDataArray[1] = -navEvent.motion.z;
        motionDataArray[2] = -navEvent.motion.y;
        motionDataArray[3] = -navEvent.motion.rx;
        motionDataArray[4] = -navEvent.motion.rz;
        motionDataArray[5] = -navEvent.motion.ry;

        motionDataArray[0] += dx;
        motionDataArray[1] += dy;
        motionDataArray[2] += dz;
        motionDataArray[3] += drx;
        motionDataArray[4] += dry;
        motionDataArray[5] += drz;

        /*
        Send a self addressed flush event through the window system. This will
        trigger a Spaceball event if FreeCAD is ready to do so.
        */
        nMotionEvents++;
        XClientMessageEvent flushEvent;

        flushEvent.display = display;
        flushEvent.window = event->xclient.window;
        flushEvent.type = ClientMessage;
        flushEvent.format = 8;
        flushEvent.message_type = motion_flush_event;

        XSendEvent (display, flushEvent.window, False, 0, (XEvent*)&flushEvent); // siehe spnavd, False, 0

        return true;
    }

    if (navEvent.type == SPNAV_EVENT_BUTTON)
    {
        mainApp->postButtonEvent(navEvent.button.bnum, navEvent.button.press);
        return true;
    }

    Base::Console().Log("Unknown spaceball event\n");
    return true;
}
#endif  // if/else QT_VERSION >= 0x050000

#include "3Dconnexion/moc_GuiNativeEventLinuxX11.cpp"
<|endoftext|>