<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;
    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;
}<commit_msg>Simplified a for-loop<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 idx : idx_one_tree)
            votes[idx]++;
    }

    /*
     * 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 <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>

#include <stdint.h>
#include <inttypes.h>

#include "cgb.hpp"
#include "dlug.h"

int main(int argc, char **argv) {
  int i, j, k;
  FILE *fp=stdin;
  char *input_fn = NULL;
  char ch;
  cgf_t *cgf, *cgf_a, *cgf_b;;
  int debug_print = 0, stats_print=0;
  int json_info_print = 0;

  cgf_t **cgfa;
  int n_cgfa=3;

  int n_match, n_ovf;

  int tilepath= -1, tilestep=-1, n_tilestep=-1;
  int tilevariant_flag = 0;

  int n_loq=0, n_tot=0;
  int lvl=0;

  int single_path_concordance=-1;
  int band_flag = 0;

  while ((ch=getopt(argc, argv, "hvi:DSVp:s:l:C:n:jB")) != -1) switch (ch) {
    case 'h':
      show_help();
      exit(0);
      break;
    case 'j':
      json_info_print = 1;
      break;

    case 'p':
      tilepath = atoi(optarg);
      break;
    case 's':
      tilestep = atoi(optarg);
      break;

    case 'n':
      n_tilestep = atoi(optarg);
      break;

    case 'B':
      band_flag = 1;
      break;

    case 'V':
      tilevariant_flag = 1;
      break;

    case 'l':
      lvl = atoi(optarg);
      break;

    case 'C':
      single_path_concordance = atoi(optarg);
      break;

    case 'D':
      debug_print = 1;
      break;
    case 'S':
      stats_print=1;
      break;
    case 'i':
      input_fn = strdup(optarg);
      break;
    case 'v':
      break;
    default:
      break;
  }

  if (input_fn!=NULL) {
    if (!(fp = fopen(input_fn, "r"))) {
      perror(input_fn);
      show_help();
      exit(1);
    }
  } else if (isatty(fileno(stdin))) {
    show_help();
    exit(1);
  }

  //---
  //
  //cgf = load_cgf(fp);
  cgf = load_cgf_buf(fp);
  if (fp!=stdin) { fclose(fp); }

  if (band_flag) {

    if ((tilepath<0) || (tilepath > cgf->path_count)) {
      printf("tilepath out of range (must be within [0,%i])\n", (int)cgf->path_count);
      show_help();
      exit(1);
    }

    if ((tilestep<0) || (tilestep >= cgf->step_per_path[tilepath])) {
      printf("tilestep out of range (must be within [0,%i])\n", (int)cgf->step_per_path[tilepath]);
      show_help();
      exit(1);
    }

    if ((n_tilestep<0) || ((tilestep+n_tilestep) > cgf->step_per_path[tilepath])) {
      n_tilestep = cgf->step_per_path[tilepath] - tilestep;
    }

    //printf(">>>> tilepath %i, tilestep %i, n_tilestep %i\n", tilepath, tilestep, n_tilestep);

    int fold_w = 32;

    std::vector<int> allele[2];
    cgf_tile_band(cgf, tilepath, tilestep, n_tilestep, allele);

    printf("{\n  \"%04x\":[\n", tilepath);
    for (i=0; i<2; i++) {
      printf("    [ ");
      for (j=0; j<allele[i].size(); j++) {
        if (j>0) { printf(", "); }
        if ((j>0) && ((j%fold_w)==0)) { printf("\n      "); }
        printf("%3i", allele[i][j]);
      }
      printf(" ]");
      if (i<(2-1)) { printf(",\n"); }
      else { printf("\n"); }
    }
    printf("  ]\n");
    printf("}\n");

    exit(0);
  }

  cgf_b = load_cgf_fn("data/hu826751-GS03052-DNA_B01.cgf");

  //cgf_b = load_cgf_fn("data/hu0211D6-GS01175-DNA_E02.cgf");

  /*
  //cgf_tile_concordance_0(&k, cgf, cgf_b, 0, 3, 5000);
  cgf_tile_concordance_0(&k, cgf, cgf_b, 1, 3, 10000);
  //cgf_tile_concordance_0(&k, cgf, cgf_b, 3, 3, 5000);
  printf(">>> %d\n", k);
  */

  // testing cgf_tile_concordance_0
  //
  /*
  j=0;
  for (i=0; i<cgf->path_count; i++) {
    cgf_tile_concordance_0(&k, cgf, cgf_b, i, 0, cgf->path[i].n_tile);
    printf(">>> %d\n", k);
    j+=k;
  }

  printf(">>>>> %i\n", j);
  */

  // testing cgf_tile_concordance_1
  //
  /*
  if (!debug_print) {
    cgf_tile_concordance_0(&k, cgf, cgf_b, 1, 5607, 104);

    cgf_tile_concordance_1(&n_match, &n_ovf,
        cgf, cgf_b,
        1, 5607, 104);
        //1, 5607, 5);
        //1, 5600, 100);
    printf("canon_match: %i, n_match: %i, n_ovf: %i\n", k, n_match, n_ovf);
  }
  */

  if (tilevariant_flag) {
    j = cgf_map_variant_id(cgf, tilepath, tilestep);
    printf(">>> %04x.%04x: %d (%x)\n", tilepath, tilestep, j, j);
    exit(0);
  }

  /*
  if (!debug_print) {
    //cgf_tile_concordance_2(&n_match, &n_ovf,
    cgf_tile_concordance_2(&n_match, &n_loq,
        cgf, cgf_b,
        //1, 0, 9000);
        1, 5607, 104);
        //1, 5607, 5);
        //1, 5600, 100);
    //printf("n_match: %i, n_ovf: %i\n", n_match, n_ovf);
    printf("n_match: %i, n_loq: %i\n", n_match, n_loq);
  }
  */

  if (lvl==0) {

    k=0;
    for (i=0; i<cgf->path_count; i++) {
      cgf_tile_concordance_0(&n_match, cgf, cgf_b, i, 0, cgf->path[i].n_tile);
      k+=n_match;
    }

    printf("level: %i, canonical match: %i\n", lvl, k);
  }

  else if (lvl==1) {
    j=0;
    k=0;
    for (i=0; i<cgf->path_count; i++) {
      cgf_tile_concordance_1(&n_match, &n_loq,
          cgf, cgf_b,
          i, 0, cgf->path[i].n_tile);
      //printf(">>> matched %d (loq %d)\n", n_match, n_loq);
      k+=n_match;
      j+=n_loq;
    }

    printf("level: %i, canonical+cache match: %i, loq: %d\n", lvl, k, j);

  }

  else if (lvl==2) {

    if (single_path_concordance != -1) {

      if (tilestep<0) { tilestep=0; }
      if (n_tilestep<0) { n_tilestep = cgf->path[single_path_concordance].n_tile - tilestep; }

      cgf_tile_concordance_2(&n_match, &n_loq,
          cgf, cgf_b,
          single_path_concordance, tilestep, n_tilestep);
          //single_path_concordance, 0, cgf->path[single_path_concordance].n_tile);
      //match_tot += n_match;
      printf("#[%x] level: %i, matched %d (loq %d)\n", single_path_concordance, lvl, n_match, n_loq);
      printf("%04x %d\n", single_path_concordance, n_match);
    }

    else {

    //int pt=0x9e;
    /*
    k=0;
    j=0;
    n_match=0;
    n_loq=0;

    cgf_tile_concordance_2(&n_match, &n_loq,
        cgf, cgf_b,
        tilepath, 0, cgf->path[tilepath].n_tile);
    printf("[%x] level: %i, matched %d (loq %d)\n", tilepath, lvl, n_match, n_loq);
    k+=n_match;
    j+=n_loq;
    */

      int match_tot = 0;

      for (tilepath=0; tilepath<cgf->path_count; tilepath++) {
        cgf_tile_concordance_2(&n_match, &n_loq,
            cgf, cgf_b,
            tilepath, 0, cgf->path[tilepath].n_tile);
        match_tot += n_match;
        //printf("#[%x] level: %i, matched %d (loq %d)\n", tilepath, lvl, n_match, n_loq);
        //printf("%04x %d\n", tilepath, n_match);
      }

      printf("#match_tot: %i\n", match_tot);
    }

    /*
    j=0;
    k=0;
    for (i=0; i<cgf->path_count; i++) {
      cgf_tile_concordance_2(&n_match, &n_loq,
          cgf, cgf_b,
          i, 0, cgf->path[i].n_tile);
      printf("[%x] level: %i, matched %d (loq %d)\n", i, lvl, n_match, n_loq);
      k+=n_match;
      j+=n_loq;
    }
    */

    //printf("level: %i, match: %i, loq: %d\n", lvl, k, j);

  }

  /*
  j=0;
  k=0;
  for (i=0; i<cgf->path_count; i++) {
  //for (i=0; i<20; i++) {
    cgf_tile_concordance_2(&n_match, &n_loq,
        cgf, cgf_b,
        i, 0, cgf->path[i].n_tile);
    printf(">>> matched %d (loq %d)\n", n_match, n_loq);
    k+=n_match;
    j+=n_loq;
  }

  printf(">>>>> tot: %i (loq: %d)\n", k, j);
  */

  /*
  k=0;
  j=0;
  for (i=0; i<cgf->path_count; i++) {
    cgf_tile_concordance_1(&n_match, &n_ovf,
        cgf, cgf_b,
        i, 0, cgf->path[i].n_tile);
    printf(">>> %d\n", n_match);
    k+=n_match;
    j+=n_ovf;
  }

  printf(">>>>> x %i (ovf %d)\n", k, j);
  */

  /*
  cgfa = (cgf_t **)malloc(sizeof(cgf_t *)*n_cgfa);
  for (i=0; i<n_cgfa; i++) {
    cgfa[i] = load_cgf_fn(input_fn);
    if (cgfa[i]==NULL) { printf("nope\n"); }
  }
  printf("ok\n");
  */

  if (json_info_print) { cgf_json_info_print(cgf); }

  if (debug_print) { debug_print_cgf(cgf); }
  if (stats_print) { stats_print_cgf(cgf); }

}
<commit_msg>adding `start_tilestep` to band output (JSON)<commit_after>#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>

#include <stdint.h>
#include <inttypes.h>

#include "cgb.hpp"
#include "dlug.h"

int main(int argc, char **argv) {
  int i, j, k;
  FILE *fp=stdin;
  char *input_fn = NULL;
  char ch;
  cgf_t *cgf, *cgf_a, *cgf_b;;
  int debug_print = 0, stats_print=0;
  int json_info_print = 0;

  cgf_t **cgfa;
  int n_cgfa=3;

  int n_match, n_ovf;

  int tilepath= -1, tilestep=-1, n_tilestep=-1;
  int tilevariant_flag = 0;

  int n_loq=0, n_tot=0;
  int lvl=0;

  int single_path_concordance=-1;
  int band_flag = 0;

  while ((ch=getopt(argc, argv, "hvi:DSVp:s:l:C:n:jB")) != -1) switch (ch) {
    case 'h':
      show_help();
      exit(0);
      break;
    case 'j':
      json_info_print = 1;
      break;

    case 'p':
      tilepath = atoi(optarg);
      break;
    case 's':
      tilestep = atoi(optarg);
      break;

    case 'n':
      n_tilestep = atoi(optarg);
      break;

    case 'B':
      band_flag = 1;
      break;

    case 'V':
      tilevariant_flag = 1;
      break;

    case 'l':
      lvl = atoi(optarg);
      break;

    case 'C':
      single_path_concordance = atoi(optarg);
      break;

    case 'D':
      debug_print = 1;
      break;
    case 'S':
      stats_print=1;
      break;
    case 'i':
      input_fn = strdup(optarg);
      break;
    case 'v':
      break;
    default:
      break;
  }

  if (input_fn!=NULL) {
    if (!(fp = fopen(input_fn, "r"))) {
      perror(input_fn);
      show_help();
      exit(1);
    }
  } else if (isatty(fileno(stdin))) {
    show_help();
    exit(1);
  }

  //---
  //
  //cgf = load_cgf(fp);
  cgf = load_cgf_buf(fp);
  if (fp!=stdin) { fclose(fp); }

  if (band_flag) {

    if ((tilepath<0) || (tilepath > cgf->path_count)) {
      printf("tilepath out of range (must be within [0,%i])\n", (int)cgf->path_count);
      show_help();
      exit(1);
    }

    if ((tilestep<0) || (tilestep >= cgf->step_per_path[tilepath])) {
      printf("tilestep out of range (must be within [0,%i])\n", (int)cgf->step_per_path[tilepath]);
      show_help();
      exit(1);
    }

    if ((n_tilestep<0) || ((tilestep+n_tilestep) > cgf->step_per_path[tilepath])) {
      n_tilestep = cgf->step_per_path[tilepath] - tilestep;
    }

    //printf(">>>> tilepath %i, tilestep %i, n_tilestep %i\n", tilepath, tilestep, n_tilestep);

    int fold_w = 32;

    std::vector<int> allele[2];
    cgf_tile_band(cgf, tilepath, tilestep, n_tilestep, allele);

    printf("{\n");
    printf("  \"start_tilestep\":%i,\n", tilestep);
    printf("  \"%04x\":[\n", tilepath);
    for (i=0; i<2; i++) {
      printf("    [ ");
      for (j=0; j<allele[i].size(); j++) {
        if (j>0) { printf(", "); }
        if ((j>0) && ((j%fold_w)==0)) { printf("\n      "); }
        printf("%3i", allele[i][j]);
      }
      printf(" ]");
      if (i<(2-1)) { printf(",\n"); }
      else { printf("\n"); }
    }
    printf("  ]\n");
    printf("}\n");

    exit(0);
  }

  cgf_b = load_cgf_fn("data/hu826751-GS03052-DNA_B01.cgf");

  //cgf_b = load_cgf_fn("data/hu0211D6-GS01175-DNA_E02.cgf");

  /*
  //cgf_tile_concordance_0(&k, cgf, cgf_b, 0, 3, 5000);
  cgf_tile_concordance_0(&k, cgf, cgf_b, 1, 3, 10000);
  //cgf_tile_concordance_0(&k, cgf, cgf_b, 3, 3, 5000);
  printf(">>> %d\n", k);
  */

  // testing cgf_tile_concordance_0
  //
  /*
  j=0;
  for (i=0; i<cgf->path_count; i++) {
    cgf_tile_concordance_0(&k, cgf, cgf_b, i, 0, cgf->path[i].n_tile);
    printf(">>> %d\n", k);
    j+=k;
  }

  printf(">>>>> %i\n", j);
  */

  // testing cgf_tile_concordance_1
  //
  /*
  if (!debug_print) {
    cgf_tile_concordance_0(&k, cgf, cgf_b, 1, 5607, 104);

    cgf_tile_concordance_1(&n_match, &n_ovf,
        cgf, cgf_b,
        1, 5607, 104);
        //1, 5607, 5);
        //1, 5600, 100);
    printf("canon_match: %i, n_match: %i, n_ovf: %i\n", k, n_match, n_ovf);
  }
  */

  if (tilevariant_flag) {
    j = cgf_map_variant_id(cgf, tilepath, tilestep);
    printf(">>> %04x.%04x: %d (%x)\n", tilepath, tilestep, j, j);
    exit(0);
  }

  /*
  if (!debug_print) {
    //cgf_tile_concordance_2(&n_match, &n_ovf,
    cgf_tile_concordance_2(&n_match, &n_loq,
        cgf, cgf_b,
        //1, 0, 9000);
        1, 5607, 104);
        //1, 5607, 5);
        //1, 5600, 100);
    //printf("n_match: %i, n_ovf: %i\n", n_match, n_ovf);
    printf("n_match: %i, n_loq: %i\n", n_match, n_loq);
  }
  */

  if (lvl==0) {

    k=0;
    for (i=0; i<cgf->path_count; i++) {
      cgf_tile_concordance_0(&n_match, cgf, cgf_b, i, 0, cgf->path[i].n_tile);
      k+=n_match;
    }

    printf("level: %i, canonical match: %i\n", lvl, k);
  }

  else if (lvl==1) {
    j=0;
    k=0;
    for (i=0; i<cgf->path_count; i++) {
      cgf_tile_concordance_1(&n_match, &n_loq,
          cgf, cgf_b,
          i, 0, cgf->path[i].n_tile);
      //printf(">>> matched %d (loq %d)\n", n_match, n_loq);
      k+=n_match;
      j+=n_loq;
    }

    printf("level: %i, canonical+cache match: %i, loq: %d\n", lvl, k, j);

  }

  else if (lvl==2) {

    if (single_path_concordance != -1) {

      if (tilestep<0) { tilestep=0; }
      if (n_tilestep<0) { n_tilestep = cgf->path[single_path_concordance].n_tile - tilestep; }

      cgf_tile_concordance_2(&n_match, &n_loq,
          cgf, cgf_b,
          single_path_concordance, tilestep, n_tilestep);
          //single_path_concordance, 0, cgf->path[single_path_concordance].n_tile);
      //match_tot += n_match;
      printf("#[%x] level: %i, matched %d (loq %d)\n", single_path_concordance, lvl, n_match, n_loq);
      printf("%04x %d\n", single_path_concordance, n_match);
    }

    else {

    //int pt=0x9e;
    /*
    k=0;
    j=0;
    n_match=0;
    n_loq=0;

    cgf_tile_concordance_2(&n_match, &n_loq,
        cgf, cgf_b,
        tilepath, 0, cgf->path[tilepath].n_tile);
    printf("[%x] level: %i, matched %d (loq %d)\n", tilepath, lvl, n_match, n_loq);
    k+=n_match;
    j+=n_loq;
    */

      int match_tot = 0;

      for (tilepath=0; tilepath<cgf->path_count; tilepath++) {
        cgf_tile_concordance_2(&n_match, &n_loq,
            cgf, cgf_b,
            tilepath, 0, cgf->path[tilepath].n_tile);
        match_tot += n_match;
        //printf("#[%x] level: %i, matched %d (loq %d)\n", tilepath, lvl, n_match, n_loq);
        //printf("%04x %d\n", tilepath, n_match);
      }

      printf("#match_tot: %i\n", match_tot);
    }

    /*
    j=0;
    k=0;
    for (i=0; i<cgf->path_count; i++) {
      cgf_tile_concordance_2(&n_match, &n_loq,
          cgf, cgf_b,
          i, 0, cgf->path[i].n_tile);
      printf("[%x] level: %i, matched %d (loq %d)\n", i, lvl, n_match, n_loq);
      k+=n_match;
      j+=n_loq;
    }
    */

    //printf("level: %i, match: %i, loq: %d\n", lvl, k, j);

  }

  /*
  j=0;
  k=0;
  for (i=0; i<cgf->path_count; i++) {
  //for (i=0; i<20; i++) {
    cgf_tile_concordance_2(&n_match, &n_loq,
        cgf, cgf_b,
        i, 0, cgf->path[i].n_tile);
    printf(">>> matched %d (loq %d)\n", n_match, n_loq);
    k+=n_match;
    j+=n_loq;
  }

  printf(">>>>> tot: %i (loq: %d)\n", k, j);
  */

  /*
  k=0;
  j=0;
  for (i=0; i<cgf->path_count; i++) {
    cgf_tile_concordance_1(&n_match, &n_ovf,
        cgf, cgf_b,
        i, 0, cgf->path[i].n_tile);
    printf(">>> %d\n", n_match);
    k+=n_match;
    j+=n_ovf;
  }

  printf(">>>>> x %i (ovf %d)\n", k, j);
  */

  /*
  cgfa = (cgf_t **)malloc(sizeof(cgf_t *)*n_cgfa);
  for (i=0; i<n_cgfa; i++) {
    cgfa[i] = load_cgf_fn(input_fn);
    if (cgfa[i]==NULL) { printf("nope\n"); }
  }
  printf("ok\n");
  */

  if (json_info_print) { cgf_json_info_print(cgf); }

  if (debug_print) { debug_print_cgf(cgf); }
  if (stats_print) { stats_print_cgf(cgf); }

}
<|endoftext|>


<commit_before>#ifndef CONTAINERS_ARCHIVE_VERSIONED_HPP_
#define CONTAINERS_ARCHIVE_VERSIONED_HPP_

#include "version.hpp"
#include "containers/archive/archive.hpp"

// Silences complaints about spurious comparisons in the macro expansion on gcc-4.4.
#if defined(__GNUC__) && (100 * __GNUC__ + __GNUC_MINOR__ < 406)
#pragma GCC diagnostic ignored "-Wtype-limits"
#endif

// This is only correct as long as cluster_version_t values form a contiguous range.
ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE(cluster_version_t, uint8_t,
                                      cluster_version_t::v1_13,
                                      cluster_version_t::ONLY_VERSION);

#if defined(__GNUC__) && (100 * __GNUC__ + __GNUC_MINOR__ < 406)
#pragma GCC diagnostic error "-Wtype-limits"
#endif

// Serializes a value for a given version.  DOES NOT SERIALIZE THE VERSION NUMBER!
template <class T>
void serialize_for_version(cluster_version_t version, write_message_t *wm,
                           const T &value) {
    // Right now, since there's only one version number, we can just call the normal
    // serialization function.
    rassert(version == cluster_version_t::ONLY_VERSION);
    serialize(wm, value);
}

// Deserializes a value, assuming it's serialized for a given version.  (This doesn't
// deserialize any version numbers.)
template <class T>
archive_result_t deserialize_for_version(cluster_version_t version,
                                         read_stream_t *s,
                                         T *thing) {
    // Right now, since there's only one version number, we can just call the normal
    // serialization function.
    rassert(version == cluster_version_t::ONLY_VERSION);
    return deserialize(s, thing);
}

#endif  // CONTAINERS_ARCHIVE_VERSIONED_HPP_
<commit_msg>Made cluster_version_t serialization macro use int8_t, dropped pragmas.<commit_after>#ifndef CONTAINERS_ARCHIVE_VERSIONED_HPP_
#define CONTAINERS_ARCHIVE_VERSIONED_HPP_

#include "version.hpp"
#include "containers/archive/archive.hpp"

// This is only correct as long as cluster_version_t values form a contiguous range.
ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE(cluster_version_t, int8_t,
                                      cluster_version_t::v1_13,
                                      cluster_version_t::ONLY_VERSION);

// Serializes a value for a given version.  DOES NOT SERIALIZE THE VERSION NUMBER!
template <class T>
void serialize_for_version(cluster_version_t version, write_message_t *wm,
                           const T &value) {
    // Right now, since there's only one version number, we can just call the normal
    // serialization function.
    rassert(version == cluster_version_t::ONLY_VERSION);
    serialize(wm, value);
}

// Deserializes a value, assuming it's serialized for a given version.  (This doesn't
// deserialize any version numbers.)
template <class T>
archive_result_t deserialize_for_version(cluster_version_t version,
                                         read_stream_t *s,
                                         T *thing) {
    // Right now, since there's only one version number, we can just call the normal
    // serialization function.
    rassert(version == cluster_version_t::ONLY_VERSION);
    return deserialize(s, thing);
}

#endif  // CONTAINERS_ARCHIVE_VERSIONED_HPP_
<|endoftext|>


<commit_before>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkColorSpaceXform.h"
#include "SkData.h"
#include "SkMakeUnique.h"
#include "skcms.h"

class SkColorSpaceXform_skcms : public SkColorSpaceXform {
public:
    SkColorSpaceXform_skcms(const skcms_ICCProfile& srcProfile,
                            const skcms_ICCProfile& dstProfile,
                            skcms_AlphaFormat premulFormat)
        : fSrcProfile(srcProfile)
        , fDstProfile(dstProfile)
        , fPremulFormat(premulFormat)
    {}

    bool apply(ColorFormat, void*, ColorFormat, const void*, int, SkAlphaType) const override;

private:
    skcms_ICCProfile  fSrcProfile;
    skcms_ICCProfile  fDstProfile;
    skcms_AlphaFormat fPremulFormat;
};

static skcms_PixelFormat get_skcms_format(SkColorSpaceXform::ColorFormat fmt) {
    switch (fmt) {
        case SkColorSpaceXform::kRGBA_8888_ColorFormat:
            return skcms_PixelFormat_RGBA_8888;
        case SkColorSpaceXform::kBGRA_8888_ColorFormat:
            return skcms_PixelFormat_BGRA_8888;
        case SkColorSpaceXform::kRGB_U16_BE_ColorFormat:
            return skcms_PixelFormat_RGB_161616;
        case SkColorSpaceXform::kRGBA_U16_BE_ColorFormat:
            return skcms_PixelFormat_RGBA_16161616;
        case SkColorSpaceXform::kRGBA_F16_ColorFormat:
            return skcms_PixelFormat_RGBA_hhhh;
        case SkColorSpaceXform::kRGBA_F32_ColorFormat:
            return skcms_PixelFormat_RGBA_ffff;
        case SkColorSpaceXform::kBGR_565_ColorFormat:
            return skcms_PixelFormat_BGR_565;
        default:
            SkDEBUGFAIL("Invalid ColorFormat");
            return skcms_PixelFormat_RGBA_8888;
    }
}

bool SkColorSpaceXform_skcms::apply(ColorFormat dstFormat, void* dst,
                                    ColorFormat srcFormat, const void* src,
                                    int count, SkAlphaType alphaType) const {
    skcms_AlphaFormat srcAlpha = skcms_AlphaFormat_Unpremul;
    skcms_AlphaFormat dstAlpha = kPremul_SkAlphaType == alphaType ? fPremulFormat
                                                                  : skcms_AlphaFormat_Unpremul;

    return skcms_Transform(src, get_skcms_format(srcFormat), srcAlpha, &fSrcProfile,
                           dst, get_skcms_format(dstFormat), dstAlpha, &fDstProfile, count);
}

static bool cs_to_profile(const SkColorSpace* cs, skcms_ICCProfile* profile) {
    if (cs->profileData()) {
        bool result = skcms_Parse(cs->profileData()->data(), cs->profileData()->size(), profile);
        // We shouldn't encounter color spaces that were constructed from invalid profiles!
        SkASSERT(result);
        return result;
    }

    SkMatrix44 toXYZ;
    SkColorSpaceTransferFn tf;
    if (cs->toXYZD50(&toXYZ) && cs->isNumericalTransferFn(&tf)) {
        memset(profile, 0, sizeof(*profile));

        profile->has_trc = true;
        profile->trc[0].parametric.g = tf.fG;
        profile->trc[0].parametric.a = tf.fA;
        profile->trc[0].parametric.b = tf.fB;
        profile->trc[0].parametric.c = tf.fC;
        profile->trc[0].parametric.d = tf.fD;
        profile->trc[0].parametric.e = tf.fE;
        profile->trc[0].parametric.f = tf.fF;
        profile->trc[1].parametric = profile->trc[0].parametric;
        profile->trc[2].parametric = profile->trc[0].parametric;

        profile->has_toXYZD50 = true;
        for (int r = 0; r < 3; ++r) {
            for (int c = 0; c < 3; ++c) {
                profile->toXYZD50.vals[r][c] = toXYZ.get(r, c);
            }
        }

        return true;
    }

    // It should be impossible to make a color space that gets here with our available factories.
    // All ICC-based profiles have profileData. All remaining factories produce XYZ spaces with
    // a single (numerical) transfer function.
    SkDEBUGFAIL("How did we get here?");
    return false;
}

std::unique_ptr<SkColorSpaceXform> MakeSkcmsXform(SkColorSpace* src, SkColorSpace* dst,
                                                  SkTransferFunctionBehavior premulBehavior) {
    // Construct skcms_ICCProfiles from each color space. For now, support A2B and XYZ.
    // Eventually, only need to support XYZ. Map premulBehavior to one of the two premul formats
    // in skcms.
    skcms_ICCProfile srcProfile, dstProfile;

    if (!cs_to_profile(src, &srcProfile) || !cs_to_profile(dst, &dstProfile)) {
        return nullptr;
    }

    skcms_AlphaFormat premulFormat = SkTransferFunctionBehavior::kRespect == premulBehavior
            ? skcms_AlphaFormat_PremulLinear : skcms_AlphaFormat_PremulAsEncoded;
    return skstd::make_unique<SkColorSpaceXform_skcms>(srcProfile, dstProfile, premulFormat);
}

sk_sp<SkColorSpace> SkColorSpace::Make(const skcms_ICCProfile* profile) {
    if (!profile) {
        return nullptr;
    }

    if (!profile->has_toXYZD50 || !profile->has_trc) {
        return nullptr;
    }

    skcms_TransferFunction tf = skcms_BestSingleCurve(profile);
    SkColorSpaceTransferFn skia_tf;
    memcpy(&skia_tf, &tf, sizeof(skia_tf));

    SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
    toXYZD50.set3x3RowMajorf(&profile->toXYZD50.vals[0][0]);

    return SkColorSpace::MakeRGB(skia_tf, toXYZD50);
}
<commit_msg>Support all destination profiles in SkColorSpaceXform_skcms<commit_after>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkColorSpaceXform.h"
#include "SkData.h"
#include "SkMakeUnique.h"
#include "skcms.h"

class SkColorSpaceXform_skcms : public SkColorSpaceXform {
public:
    SkColorSpaceXform_skcms(const skcms_ICCProfile& srcProfile,
                            const skcms_ICCProfile& dstProfile,
                            skcms_AlphaFormat premulFormat)
        : fSrcProfile(srcProfile)
        , fDstProfile(dstProfile)
        , fPremulFormat(premulFormat) {
        skcms_EnsureUsableAsDestination(&fDstProfile, &skcms_sRGB_profile);
    }

    bool apply(ColorFormat, void*, ColorFormat, const void*, int, SkAlphaType) const override;

private:
    skcms_ICCProfile  fSrcProfile;
    skcms_ICCProfile  fDstProfile;
    skcms_AlphaFormat fPremulFormat;
};

static skcms_PixelFormat get_skcms_format(SkColorSpaceXform::ColorFormat fmt) {
    switch (fmt) {
        case SkColorSpaceXform::kRGBA_8888_ColorFormat:
            return skcms_PixelFormat_RGBA_8888;
        case SkColorSpaceXform::kBGRA_8888_ColorFormat:
            return skcms_PixelFormat_BGRA_8888;
        case SkColorSpaceXform::kRGB_U16_BE_ColorFormat:
            return skcms_PixelFormat_RGB_161616;
        case SkColorSpaceXform::kRGBA_U16_BE_ColorFormat:
            return skcms_PixelFormat_RGBA_16161616;
        case SkColorSpaceXform::kRGBA_F16_ColorFormat:
            return skcms_PixelFormat_RGBA_hhhh;
        case SkColorSpaceXform::kRGBA_F32_ColorFormat:
            return skcms_PixelFormat_RGBA_ffff;
        case SkColorSpaceXform::kBGR_565_ColorFormat:
            return skcms_PixelFormat_BGR_565;
        default:
            SkDEBUGFAIL("Invalid ColorFormat");
            return skcms_PixelFormat_RGBA_8888;
    }
}

bool SkColorSpaceXform_skcms::apply(ColorFormat dstFormat, void* dst,
                                    ColorFormat srcFormat, const void* src,
                                    int count, SkAlphaType alphaType) const {
    skcms_AlphaFormat srcAlpha = skcms_AlphaFormat_Unpremul;
    skcms_AlphaFormat dstAlpha = kPremul_SkAlphaType == alphaType ? fPremulFormat
                                                                  : skcms_AlphaFormat_Unpremul;

    return skcms_Transform(src, get_skcms_format(srcFormat), srcAlpha, &fSrcProfile,
                           dst, get_skcms_format(dstFormat), dstAlpha, &fDstProfile, count);
}

static bool cs_to_profile(const SkColorSpace* cs, skcms_ICCProfile* profile) {
    if (cs->profileData()) {
        bool result = skcms_Parse(cs->profileData()->data(), cs->profileData()->size(), profile);
        // We shouldn't encounter color spaces that were constructed from invalid profiles!
        SkASSERT(result);
        return result;
    }

    SkMatrix44 toXYZ;
    SkColorSpaceTransferFn tf;
    if (cs->toXYZD50(&toXYZ) && cs->isNumericalTransferFn(&tf)) {
        memset(profile, 0, sizeof(*profile));

        profile->has_trc = true;
        profile->trc[0].parametric.g = tf.fG;
        profile->trc[0].parametric.a = tf.fA;
        profile->trc[0].parametric.b = tf.fB;
        profile->trc[0].parametric.c = tf.fC;
        profile->trc[0].parametric.d = tf.fD;
        profile->trc[0].parametric.e = tf.fE;
        profile->trc[0].parametric.f = tf.fF;
        profile->trc[1].parametric = profile->trc[0].parametric;
        profile->trc[2].parametric = profile->trc[0].parametric;

        profile->has_toXYZD50 = true;
        for (int r = 0; r < 3; ++r) {
            for (int c = 0; c < 3; ++c) {
                profile->toXYZD50.vals[r][c] = toXYZ.get(r, c);
            }
        }

        return true;
    }

    // It should be impossible to make a color space that gets here with our available factories.
    // All ICC-based profiles have profileData. All remaining factories produce XYZ spaces with
    // a single (numerical) transfer function.
    SkDEBUGFAIL("How did we get here?");
    return false;
}

std::unique_ptr<SkColorSpaceXform> MakeSkcmsXform(SkColorSpace* src, SkColorSpace* dst,
                                                  SkTransferFunctionBehavior premulBehavior) {
    // Construct skcms_ICCProfiles from each color space. For now, support A2B and XYZ.
    // Eventually, only need to support XYZ. Map premulBehavior to one of the two premul formats
    // in skcms.
    skcms_ICCProfile srcProfile, dstProfile;

    if (!cs_to_profile(src, &srcProfile) || !cs_to_profile(dst, &dstProfile)) {
        return nullptr;
    }

    skcms_AlphaFormat premulFormat = SkTransferFunctionBehavior::kRespect == premulBehavior
            ? skcms_AlphaFormat_PremulLinear : skcms_AlphaFormat_PremulAsEncoded;
    return skstd::make_unique<SkColorSpaceXform_skcms>(srcProfile, dstProfile, premulFormat);
}

sk_sp<SkColorSpace> SkColorSpace::Make(const skcms_ICCProfile* profile) {
    if (!profile) {
        return nullptr;
    }

    if (!profile->has_toXYZD50 || !profile->has_trc) {
        return nullptr;
    }

    skcms_TransferFunction tf = skcms_BestSingleCurve(profile);
    SkColorSpaceTransferFn skia_tf;
    memcpy(&skia_tf, &tf, sizeof(skia_tf));

    SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
    toXYZD50.set3x3RowMajorf(&profile->toXYZD50.vals[0][0]);

    return SkColorSpace::MakeRGB(skia_tf, toXYZD50);
}
<|endoftext|>


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

#ifndef CATS_TEXTCAT_XML_DOM_HPP
#define CATS_TEXTCAT_XML_DOM_HPP

#include <cassert>
#include <cstring>

#include <new>
#include <iostream>

#include "Cats/Corecat/Data/Stream.hpp"
#include "Cats/Corecat/Text/String.hpp"
#include "Cats/Corecat/Util/Allocator.hpp"
#include "Cats/Corecat/Util/Exception.hpp"

#include "Handler.hpp"
#include "Parser.hpp"
#include "Serializer.hpp"

namespace Cats {
namespace Textcat{
inline namespace XML {

class XMLNode;
class XMLElement;
class XMLText;
class XMLCDATA;
class XMLComment;
class XMLProcessingInstruction;
class XMLDocument;

namespace Impl {

template <typename T>
class List {

public:

    struct ListElement {

        T* prev;
        T* next;
        XMLNode* parent;

        ListElement() : prev(), next(), parent() {}

    };

    class Iterator {

    private:

        List* list;
        ListElement* p;

    public:

        Iterator(List* list_, ListElement* p_) : list(list_), p(p_) {}
        Iterator(const Iterator& src) : list(src.list), p(src.p) {}

        Iterator& operator =(const Iterator& src) { list = src.list, p = src.p; return *this; }

        T& operator *() const { return static_cast<T&>(*p); }
        T* operator ->() const { return static_cast<T*>(p); }
        bool operator ==(const Iterator& it) { return p == it.p; }
        bool operator ==(XMLNode* q) { return p == q; }
        bool operator !=(const Iterator& it) { return p != it.p; }
        Iterator& operator ++() { p = p->next; return *this; }
        Iterator operator ++(int) { Iterator tmp = *this; ++*this; return tmp; }
        Iterator& operator --() { p = p ? p->prev : list->last; return *this; }
        Iterator operator --(int) { Iterator tmp = *this; --*this; return tmp; }

    };

private:

    T* first;
    T* last;

public:

    List() : first(), last() {}
    List(const List& src) = delete;
    ~List() = default;

    T& append(XMLNode& parent, T& child) {

        assert(!child.parent);
        if(first) { child.prev = last; last->next = &child; last = &child; }
        else first = last = &child;
        child.parent = &parent;
        return child;

    }

    T& insertBefore(T& child, T& ref) {

        assert(!child.parent && ref.parent);
        auto pPrev = ref.prev;
        child.prev = pPrev;
        child.next = &ref;
        if(pPrev) pPrev->next = &child;
        ref.prev = &child;
        child.parent = ref.parent;
        return child;

    }

    T& remove(T& child) {

        auto pPrev = child.prev;
        auto pNext = child.next;
        if(pPrev) pPrev->next = pNext;
        else first = pNext;
        if(pNext) pNext->next = pPrev;
        else last = pPrev;
        child.prev = nullptr;
        child.next = nullptr;
        child.parent = nullptr;
        return child;

    }

    T& getFirst() { return *first; }
    T& getLast() { return *last; }

    bool empty() const { return !first; }

    Iterator begin() { return Iterator(this, first); }
    Iterator end() { return Iterator(this, nullptr); }

};

}

class XMLDOMException : public Corecat::Exception {

public:

    XMLDOMException(const String8& data) : Exception("XMLDOMException: " + data) {}

};

enum class XMLNodeType : uint16_t {

    Element,
    Text,
    CDATA,
    Comment,
    ProcessingInstruction,
    Document,

};

class XMLNode : public Impl::List<XMLNode>::ListElement {

private:

    const XMLNodeType type;
    Impl::List<XMLNode> listChild;

public:

    XMLNode(XMLNodeType type_) : Impl::List<XMLNode>::ListElement(), type(type_), listChild() {}
    XMLNode(const XMLNode& src) = delete;

    XMLNodeType getType() const { return type; }

    Impl::List<XMLNode>& child() { return listChild; }

    XMLNode& getFirstChild() { return listChild.getFirst(); }
    XMLNode& getLastChild() { return listChild.getLast(); }

    XMLNode& appendChild(XMLNode& child) { return listChild.append(*this, child); }
    XMLNode& insertBefore(XMLNode& child, XMLNode& ref) { return listChild.insertBefore(child, ref); }
    XMLNode& removeChild(XMLNode& child) { return listChild.remove(child); }
    bool hasChildNodes() { return !listChild.empty(); }

    XMLElement& asElement() noexcept { return reinterpret_cast<XMLElement&>(*this); }
    const XMLElement& asElement() const noexcept { return reinterpret_cast<const XMLElement&>(*this); }
    XMLText& asText() noexcept { return reinterpret_cast<XMLText&>(*this); }
    const XMLText& asText() const noexcept { return reinterpret_cast<const XMLText&>(*this); }
    XMLCDATA& asCDATA() noexcept { return reinterpret_cast<XMLCDATA&>(*this); }
    const XMLCDATA& asCDATA() const noexcept { return reinterpret_cast<const XMLCDATA&>(*this); }
    XMLComment& asComment() noexcept { return reinterpret_cast<XMLComment&>(*this); }
    const XMLComment& asComment() const noexcept { return reinterpret_cast<const XMLComment&>(*this); }
    XMLProcessingInstruction& asProcessingInstruction() noexcept { return reinterpret_cast<XMLProcessingInstruction&>(*this); }
    const XMLProcessingInstruction& asProcessingInstruction() const noexcept { return reinterpret_cast<const XMLProcessingInstruction&>(*this); }
    XMLDocument& asDocument() noexcept { return reinterpret_cast<XMLDocument&>(*this); }
    const XMLDocument& asDocument() const noexcept { return reinterpret_cast<const XMLDocument&>(*this); }

};

class XMLAttribute : public Impl::List<XMLAttribute>::ListElement {

private:

    using StringView8 = Corecat::StringView8;

private:

    StringView8 name;
    StringView8 value;

public:

    XMLAttribute() : Impl::List<XMLAttribute>::ListElement(), name(), value() {}
    XMLAttribute(StringView8 name_, StringView8 value_) :
        Impl::List<XMLAttribute>::ListElement(), name(name_), value(value_) {}
    XMLAttribute(const XMLAttribute& src) = delete;

    StringView8 getName() const { return name; }
    void setName(StringView8 name_) { name = name_; }
    StringView8 getValue() const { return value; }
    void setValue(StringView8 value_) { value = value_; }

};

class XMLElement : public XMLNode {

private:

    using StringView8 = Corecat::StringView8;

private:

    Impl::List<XMLAttribute> listAttr;
    StringView8 name;

public:

    XMLElement() : XMLNode(XMLNodeType::Element), listAttr(), name() {}
    XMLElement(StringView8 name_) : XMLNode(XMLNodeType::Element), listAttr(), name(name_) {}
    XMLElement(const XMLElement& src) = delete;

    Impl::List<XMLAttribute>& attribute() { return listAttr; }

    StringView8 getName() const { return name; }
    void setName(StringView8 name_) { name = name_; }

    XMLAttribute& getFirstAttribute() { return listAttr.getFirst(); }
    XMLAttribute& getLastAttribute() { return listAttr.getLast(); }
    XMLAttribute& appendAttribute(XMLAttribute& attr) { return listAttr.append(*this, attr); }
    XMLAttribute& removeAttribute(XMLAttribute& attr) { return listAttr.remove(attr); }

};

class XMLText : public XMLNode {

private:

    using StringView8 = Corecat::StringView8;

private:

    StringView8 value;

public:

    XMLText() : XMLNode(XMLNodeType::Text), value() {}
    XMLText(StringView8 value_) : XMLNode(XMLNodeType::Text), value(value_) {}
    XMLText(const XMLText& src) = delete;

    StringView8 getValue() const { return value; }
    void setValue(StringView8 value_) { value = value_; }

};

class XMLCDATA : public XMLNode {

private:

    using StringView8 = Corecat::StringView8;

private:

    StringView8 value;

public:

    XMLCDATA() : XMLNode(XMLNodeType::CDATA), value() {}
    XMLCDATA(StringView8 value_) : XMLNode(XMLNodeType::CDATA), value(value_) {}
    XMLCDATA(const XMLCDATA& src) = delete;

    StringView8 getValue() const { return value; }
    void setValue(StringView8 value_) { value = value_; }

};

class XMLComment : public XMLNode {

private:

    using StringView8 = Corecat::StringView8;

private:

    StringView8 value;

public:

    XMLComment() : XMLNode(XMLNodeType::Comment), value() {}
    XMLComment(StringView8 value_) : XMLNode(XMLNodeType::Comment), value(value_) {}
    XMLComment(const XMLComment& src) = delete;

    StringView8 getValue() const { return value; }
    void setValue(StringView8 value_) { value = value_; }

};

class XMLProcessingInstruction : public XMLNode {

private:

    using StringView8 = Corecat::StringView8;

private:

    StringView8 name;
    StringView8 value;

public:

    XMLProcessingInstruction() : XMLNode(XMLNodeType::ProcessingInstruction), name(), value() {};
    XMLProcessingInstruction(StringView8& name_, StringView8& value_) :
        XMLNode(XMLNodeType::ProcessingInstruction), name(name_), value(value_) {}
    XMLProcessingInstruction(const XMLProcessingInstruction& src) = delete;

    StringView8 getName() const { return name; }
    void setName(StringView8 name_) { name = name_; }
    StringView8 getValue() const { return value; }
    void setValue(StringView8 value_) { value = value_; }

};

class XMLDocument : public XMLNode {

private:

    template <typename T>
    using OutputStream = Corecat::OutputStream<T>;
    using StringView8 = Corecat::StringView8;
    using FastAllocator = Corecat::FastAllocator<>;

private:

    FastAllocator allocator;

public:

    XMLDocument() : XMLNode(XMLNodeType::Document), allocator() {}
    XMLDocument(const XMLDocument& src) = delete;

    XMLElement& createElement(StringView8 name) {

        return *new(allocator.allocate(sizeof(XMLElement))) XMLElement(name);

    }
    XMLAttribute& createAttribute(StringView8 name, StringView8 value) {

        return *new(allocator.allocate(sizeof(XMLAttribute))) XMLAttribute(name, value);

    }
    XMLText& createText(StringView8 value) {

        return *new(allocator.allocate(sizeof(XMLText))) XMLText(value);

    }
    XMLCDATA& createCDATA(StringView8 value) {

        return *new(allocator.allocate(sizeof(XMLCDATA))) XMLCDATA(value);

    }
    XMLComment& createComment(StringView8 value) {

        return *new(allocator.allocate(sizeof(XMLComment))) XMLComment(value);

    }
    XMLProcessingInstruction& createProcessingInstruction(StringView8 name, StringView8 value) {

        return *new(allocator.allocate(sizeof(XMLProcessingInstruction))) XMLProcessingInstruction(name, value);

    }

    void clear() {

        allocator.clear();

    }

    XMLElement& getRootElement() {

        for(auto& node : child()) if(node.getType() == XMLNodeType::Element) return static_cast<XMLElement&>(node);
        throw XMLDOMException("Root element not found");

    }

    template <XMLParser::Flag F = XMLParser::Flag::Default>
    void parse(char* data) {

        class Handler : public XMLHandlerBase {

        private:

            XMLDocument* document;
            XMLNode* cur;

        public:

            Handler(XMLDocument* document_) : document(document_), cur(nullptr) {}

            void startDocument() { cur = document; }
            void startElement(StringView8 name) {

                auto& element = document->createElement(name);
                cur->appendChild(element);
                cur = &element;

            }
            void endElement(StringView8 /*name*/) {

                cur = cur->parent;

            }
            void endAttributes(bool empty) {

                if(empty) cur = cur->parent;

            }
            void attribute(StringView8 name, StringView8 value) {

                static_cast<XMLElement*>(cur)->appendAttribute(document->createAttribute(name, value));

            }
            void text(StringView8 value) {

                cur->appendChild(document->createText(value));

            }
            void cdata(StringView8 value) {

                cur->appendChild(document->createCDATA(value));

            }
            void comment(StringView8 value) {

                cur->appendChild(document->createComment(value));

            }
            void processingInstruction(StringView8 name, StringView8 value) {

                cur->appendChild(document->createProcessingInstruction(name, value));

            }

        };

        assert(data);

        clear();
        XMLParser parser;
        Handler handler(this);
        parser.parse<F>(data, handler);

    }

    template <typename H>
    void visit(H& handler) {

        handler.startDocument();
        if(hasChildNodes()) {

            XMLNode* cur = &getFirstChild();
            while(true) {

                switch(cur->getType()) {

                case XMLNodeType::Element: {

                    auto& element = static_cast<XMLElement&>(*cur);
                    handler.startElement(element.getName());
                    for(auto& attr : element.attribute())
                        handler.attribute(attr.getName(), attr.getValue());
                    bool empty = !cur->hasChildNodes();
                    handler.endAttributes(empty);
                    if(!empty) { cur = &cur->getFirstChild(); continue; }
                    break;

                }
                case XMLNodeType::Text: {

                    auto& text = static_cast<XMLText&>(*cur);
                    handler.text(text.getValue());
                    break;

                }
                case XMLNodeType::CDATA: {

                    auto& cdata = static_cast<XMLCDATA&>(*cur);
                    handler.cdata(cdata.getValue());
                    break;

                }
                case XMLNodeType::Comment: {

                    auto& comment = static_cast<XMLComment&>(*cur);
                    handler.comment(comment.getValue());
                    break;

                }
                case XMLNodeType::ProcessingInstruction: {

                    auto& pi = static_cast<XMLProcessingInstruction&>(*cur);
                    handler.processingInstruction(pi.getName(), pi.getValue());
                    break;

                }
                default: throw XMLDOMException("Invalid node type");

                }
                while(!cur->next) {

                    cur = cur->parent;
                    if(cur == this) break;
                    auto name = static_cast<XMLElement*>(cur)->getName();
                    handler.endElement(name);

                }
                if(cur == this) break;
                cur = cur->next;

            }

        }
        handler.endDocument();

    }
    void serialize(OutputStream<char>& stream) {

        XMLSerializer serializer(stream);
        visit(serializer);

    }

};

inline std::ostream& operator <<(std::ostream& stream, XMLDocument& document) {

    auto wrapper = Corecat::createWrapperOutputStream(stream);
    document.serialize(wrapper);
    return stream;

}

}
}
}

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

#ifndef CATS_TEXTCAT_XML_DOM_HPP
#define CATS_TEXTCAT_XML_DOM_HPP

#include <cassert>
#include <cstring>

#include <new>
#include <iostream>

#include "Cats/Corecat/Data/Allocator/FastAllocator.hpp"
#include "Cats/Corecat/Data/Stream.hpp"
#include "Cats/Corecat/Text/String.hpp"
#include "Cats/Corecat/Util/Exception.hpp"

#include "Handler.hpp"
#include "Parser.hpp"
#include "Serializer.hpp"

namespace Cats {
namespace Textcat{
inline namespace XML {

class XMLNode;
class XMLElement;
class XMLText;
class XMLCDATA;
class XMLComment;
class XMLProcessingInstruction;
class XMLDocument;

namespace Impl {

template <typename T>
class List {

public:

    struct ListElement {

        T* prev;
        T* next;
        XMLNode* parent;

        ListElement() : prev(), next(), parent() {}

    };

    class Iterator {

    private:

        List* list;
        ListElement* p;

    public:

        Iterator(List* list_, ListElement* p_) : list(list_), p(p_) {}
        Iterator(const Iterator& src) : list(src.list), p(src.p) {}

        Iterator& operator =(const Iterator& src) { list = src.list, p = src.p; return *this; }

        T& operator *() const { return static_cast<T&>(*p); }
        T* operator ->() const { return static_cast<T*>(p); }
        bool operator ==(const Iterator& it) { return p == it.p; }
        bool operator ==(XMLNode* q) { return p == q; }
        bool operator !=(const Iterator& it) { return p != it.p; }
        Iterator& operator ++() { p = p->next; return *this; }
        Iterator operator ++(int) { Iterator tmp = *this; ++*this; return tmp; }
        Iterator& operator --() { p = p ? p->prev : list->last; return *this; }
        Iterator operator --(int) { Iterator tmp = *this; --*this; return tmp; }

    };

private:

    T* first;
    T* last;

public:

    List() : first(), last() {}
    List(const List& src) = delete;
    ~List() = default;

    T& append(XMLNode& parent, T& child) {

        assert(!child.parent);
        if(first) { child.prev = last; last->next = &child; last = &child; }
        else first = last = &child;
        child.parent = &parent;
        return child;

    }

    T& insertBefore(T& child, T& ref) {

        assert(!child.parent && ref.parent);
        auto pPrev = ref.prev;
        child.prev = pPrev;
        child.next = &ref;
        if(pPrev) pPrev->next = &child;
        ref.prev = &child;
        child.parent = ref.parent;
        return child;

    }

    T& remove(T& child) {

        auto pPrev = child.prev;
        auto pNext = child.next;
        if(pPrev) pPrev->next = pNext;
        else first = pNext;
        if(pNext) pNext->next = pPrev;
        else last = pPrev;
        child.prev = nullptr;
        child.next = nullptr;
        child.parent = nullptr;
        return child;

    }

    T& getFirst() { return *first; }
    T& getLast() { return *last; }

    bool empty() const { return !first; }

    Iterator begin() { return Iterator(this, first); }
    Iterator end() { return Iterator(this, nullptr); }

};

}

class XMLDOMException : public Corecat::Exception {

public:

    XMLDOMException(const String8& data) : Exception("XMLDOMException: " + data) {}

};

enum class XMLNodeType : uint16_t {

    Element,
    Text,
    CDATA,
    Comment,
    ProcessingInstruction,
    Document,

};

class XMLNode : public Impl::List<XMLNode>::ListElement {

private:

    const XMLNodeType type;
    Impl::List<XMLNode> listChild;

public:

    XMLNode(XMLNodeType type_) : Impl::List<XMLNode>::ListElement(), type(type_), listChild() {}
    XMLNode(const XMLNode& src) = delete;

    XMLNodeType getType() const { return type; }

    Impl::List<XMLNode>& child() { return listChild; }

    XMLNode& getFirstChild() { return listChild.getFirst(); }
    XMLNode& getLastChild() { return listChild.getLast(); }

    XMLNode& appendChild(XMLNode& child) { return listChild.append(*this, child); }
    XMLNode& insertBefore(XMLNode& child, XMLNode& ref) { return listChild.insertBefore(child, ref); }
    XMLNode& removeChild(XMLNode& child) { return listChild.remove(child); }
    bool hasChildNodes() { return !listChild.empty(); }

    XMLElement& asElement() noexcept { return reinterpret_cast<XMLElement&>(*this); }
    const XMLElement& asElement() const noexcept { return reinterpret_cast<const XMLElement&>(*this); }
    XMLText& asText() noexcept { return reinterpret_cast<XMLText&>(*this); }
    const XMLText& asText() const noexcept { return reinterpret_cast<const XMLText&>(*this); }
    XMLCDATA& asCDATA() noexcept { return reinterpret_cast<XMLCDATA&>(*this); }
    const XMLCDATA& asCDATA() const noexcept { return reinterpret_cast<const XMLCDATA&>(*this); }
    XMLComment& asComment() noexcept { return reinterpret_cast<XMLComment&>(*this); }
    const XMLComment& asComment() const noexcept { return reinterpret_cast<const XMLComment&>(*this); }
    XMLProcessingInstruction& asProcessingInstruction() noexcept { return reinterpret_cast<XMLProcessingInstruction&>(*this); }
    const XMLProcessingInstruction& asProcessingInstruction() const noexcept { return reinterpret_cast<const XMLProcessingInstruction&>(*this); }
    XMLDocument& asDocument() noexcept { return reinterpret_cast<XMLDocument&>(*this); }
    const XMLDocument& asDocument() const noexcept { return reinterpret_cast<const XMLDocument&>(*this); }

};

class XMLAttribute : public Impl::List<XMLAttribute>::ListElement {

private:

    using StringView8 = Corecat::StringView8;

private:

    StringView8 name;
    StringView8 value;

public:

    XMLAttribute() : Impl::List<XMLAttribute>::ListElement(), name(), value() {}
    XMLAttribute(StringView8 name_, StringView8 value_) :
        Impl::List<XMLAttribute>::ListElement(), name(name_), value(value_) {}
    XMLAttribute(const XMLAttribute& src) = delete;

    StringView8 getName() const { return name; }
    void setName(StringView8 name_) { name = name_; }
    StringView8 getValue() const { return value; }
    void setValue(StringView8 value_) { value = value_; }

};

class XMLElement : public XMLNode {

private:

    using StringView8 = Corecat::StringView8;

private:

    Impl::List<XMLAttribute> listAttr;
    StringView8 name;

public:

    XMLElement() : XMLNode(XMLNodeType::Element), listAttr(), name() {}
    XMLElement(StringView8 name_) : XMLNode(XMLNodeType::Element), listAttr(), name(name_) {}
    XMLElement(const XMLElement& src) = delete;

    Impl::List<XMLAttribute>& attribute() { return listAttr; }

    StringView8 getName() const { return name; }
    void setName(StringView8 name_) { name = name_; }

    XMLAttribute& getFirstAttribute() { return listAttr.getFirst(); }
    XMLAttribute& getLastAttribute() { return listAttr.getLast(); }
    XMLAttribute& appendAttribute(XMLAttribute& attr) { return listAttr.append(*this, attr); }
    XMLAttribute& removeAttribute(XMLAttribute& attr) { return listAttr.remove(attr); }

};

class XMLText : public XMLNode {

private:

    using StringView8 = Corecat::StringView8;

private:

    StringView8 value;

public:

    XMLText() : XMLNode(XMLNodeType::Text), value() {}
    XMLText(StringView8 value_) : XMLNode(XMLNodeType::Text), value(value_) {}
    XMLText(const XMLText& src) = delete;

    StringView8 getValue() const { return value; }
    void setValue(StringView8 value_) { value = value_; }

};

class XMLCDATA : public XMLNode {

private:

    using StringView8 = Corecat::StringView8;

private:

    StringView8 value;

public:

    XMLCDATA() : XMLNode(XMLNodeType::CDATA), value() {}
    XMLCDATA(StringView8 value_) : XMLNode(XMLNodeType::CDATA), value(value_) {}
    XMLCDATA(const XMLCDATA& src) = delete;

    StringView8 getValue() const { return value; }
    void setValue(StringView8 value_) { value = value_; }

};

class XMLComment : public XMLNode {

private:

    using StringView8 = Corecat::StringView8;

private:

    StringView8 value;

public:

    XMLComment() : XMLNode(XMLNodeType::Comment), value() {}
    XMLComment(StringView8 value_) : XMLNode(XMLNodeType::Comment), value(value_) {}
    XMLComment(const XMLComment& src) = delete;

    StringView8 getValue() const { return value; }
    void setValue(StringView8 value_) { value = value_; }

};

class XMLProcessingInstruction : public XMLNode {

private:

    using StringView8 = Corecat::StringView8;

private:

    StringView8 name;
    StringView8 value;

public:

    XMLProcessingInstruction() : XMLNode(XMLNodeType::ProcessingInstruction), name(), value() {};
    XMLProcessingInstruction(StringView8& name_, StringView8& value_) :
        XMLNode(XMLNodeType::ProcessingInstruction), name(name_), value(value_) {}
    XMLProcessingInstruction(const XMLProcessingInstruction& src) = delete;

    StringView8 getName() const { return name; }
    void setName(StringView8 name_) { name = name_; }
    StringView8 getValue() const { return value; }
    void setValue(StringView8 value_) { value = value_; }

};

class XMLDocument : public XMLNode {

private:

    template <typename T>
    using OutputStream = Corecat::OutputStream<T>;
    using StringView8 = Corecat::StringView8;
    using FastAllocator = Corecat::FastAllocator<>;

private:

    FastAllocator allocator;

public:

    XMLDocument() : XMLNode(XMLNodeType::Document), allocator() {}
    XMLDocument(const XMLDocument& src) = delete;

    XMLElement& createElement(StringView8 name) {

        return *new(allocator.allocate(sizeof(XMLElement))) XMLElement(name);

    }
    XMLAttribute& createAttribute(StringView8 name, StringView8 value) {

        return *new(allocator.allocate(sizeof(XMLAttribute))) XMLAttribute(name, value);

    }
    XMLText& createText(StringView8 value) {

        return *new(allocator.allocate(sizeof(XMLText))) XMLText(value);

    }
    XMLCDATA& createCDATA(StringView8 value) {

        return *new(allocator.allocate(sizeof(XMLCDATA))) XMLCDATA(value);

    }
    XMLComment& createComment(StringView8 value) {

        return *new(allocator.allocate(sizeof(XMLComment))) XMLComment(value);

    }
    XMLProcessingInstruction& createProcessingInstruction(StringView8 name, StringView8 value) {

        return *new(allocator.allocate(sizeof(XMLProcessingInstruction))) XMLProcessingInstruction(name, value);

    }

    void clear() {

        allocator.clear();

    }

    XMLElement& getRootElement() {

        for(auto& node : child()) if(node.getType() == XMLNodeType::Element) return static_cast<XMLElement&>(node);
        throw XMLDOMException("Root element not found");

    }

    template <XMLParser::Flag F = XMLParser::Flag::Default>
    void parse(char* data) {

        class Handler : public XMLHandlerBase {

        private:

            XMLDocument* document;
            XMLNode* cur;

        public:

            Handler(XMLDocument* document_) : document(document_), cur(nullptr) {}

            void startDocument() { cur = document; }
            void startElement(StringView8 name) {

                auto& element = document->createElement(name);
                cur->appendChild(element);
                cur = &element;

            }
            void endElement(StringView8 /*name*/) {

                cur = cur->parent;

            }
            void endAttributes(bool empty) {

                if(empty) cur = cur->parent;

            }
            void attribute(StringView8 name, StringView8 value) {

                static_cast<XMLElement*>(cur)->appendAttribute(document->createAttribute(name, value));

            }
            void text(StringView8 value) {

                cur->appendChild(document->createText(value));

            }
            void cdata(StringView8 value) {

                cur->appendChild(document->createCDATA(value));

            }
            void comment(StringView8 value) {

                cur->appendChild(document->createComment(value));

            }
            void processingInstruction(StringView8 name, StringView8 value) {

                cur->appendChild(document->createProcessingInstruction(name, value));

            }

        };

        assert(data);

        clear();
        XMLParser parser;
        Handler handler(this);
        parser.parse<F>(data, handler);

    }

    template <typename H>
    void visit(H& handler) {

        handler.startDocument();
        if(hasChildNodes()) {

            XMLNode* cur = &getFirstChild();
            while(true) {

                switch(cur->getType()) {

                case XMLNodeType::Element: {

                    auto& element = static_cast<XMLElement&>(*cur);
                    handler.startElement(element.getName());
                    for(auto& attr : element.attribute())
                        handler.attribute(attr.getName(), attr.getValue());
                    bool empty = !cur->hasChildNodes();
                    handler.endAttributes(empty);
                    if(!empty) { cur = &cur->getFirstChild(); continue; }
                    break;

                }
                case XMLNodeType::Text: {

                    auto& text = static_cast<XMLText&>(*cur);
                    handler.text(text.getValue());
                    break;

                }
                case XMLNodeType::CDATA: {

                    auto& cdata = static_cast<XMLCDATA&>(*cur);
                    handler.cdata(cdata.getValue());
                    break;

                }
                case XMLNodeType::Comment: {

                    auto& comment = static_cast<XMLComment&>(*cur);
                    handler.comment(comment.getValue());
                    break;

                }
                case XMLNodeType::ProcessingInstruction: {

                    auto& pi = static_cast<XMLProcessingInstruction&>(*cur);
                    handler.processingInstruction(pi.getName(), pi.getValue());
                    break;

                }
                default: throw XMLDOMException("Invalid node type");

                }
                while(!cur->next) {

                    cur = cur->parent;
                    if(cur == this) break;
                    auto name = static_cast<XMLElement*>(cur)->getName();
                    handler.endElement(name);

                }
                if(cur == this) break;
                cur = cur->next;

            }

        }
        handler.endDocument();

    }
    void serialize(OutputStream<char>& stream) {

        XMLSerializer serializer(stream);
        visit(serializer);

    }

};

inline std::ostream& operator <<(std::ostream& stream, XMLDocument& document) {

    auto wrapper = Corecat::createWrapperOutputStream(stream);
    document.serialize(wrapper);
    return stream;

}

}
}
}

#endif
<|endoftext|>


<commit_before>/**
 * This file defines the `all_cycles` graph algorithm to find all the
 * cycles in a directed graph.
 */

#ifndef D2_DETAIL_ALL_CYCLES_HPP
#define D2_DETAIL_ALL_CYCLES_HPP

#include <boost/assert.hpp>
#include <boost/concept/assert.hpp>
#include <boost/concept_check.hpp>
#include <boost/foreach.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/unordered_map.hpp>
#include <deque>
#include <iostream>
#include <iterator>
#include <set>
#include <vector>

namespace d2 {
namespace detail {

// Note: You can enable/disable this to debug the algorithm. Some information
//       will be printed to stdout.
#define D2_DEBUG_ALL_CYCLES(statement) do { } while (false)
// #define D2_DEBUG_ALL_CYCLES(statement) do { statement; } while (false)

/**
 * Wrapper visitor for use within the `all_cycles` algorithm.
 *
 * Its use is twofold:
 *  Keep track of the cycles seen so far, so the `all_cycles` algorithm can
 *  start its dfs only on vertices implicated in a cycle.
 *
 *  Call a visitor complying to the `boost::tiernan_all_cycles` visitor
 *  interface on every cycle encountered.
 */
template <typename Visitor, typename Graph, typename SeenCycles>
class AllCyclesWrapper : public boost::dfs_visitor<> {
    Visitor visitor_;
    typedef typename boost::graph_traits<Graph>::edge_descriptor Edge;
    typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex;
    typedef typename SeenCycles::value_type Cycle;
    BOOST_CONCEPT_ASSERT((boost::FrontInsertionSequenceConcept<Cycle>));

    typedef boost::unordered_map<Vertex, Edge> PredecessorMap;
    PredecessorMap predecessors_;

    SeenCycles& seen_cycles;

public:
    AllCyclesWrapper(Visitor const& v, Graph const&, SeenCycles& seen)
        : visitor_(v), seen_cycles(seen)
    { }

    void tree_edge(Edge e, Graph const& g) {
        D2_DEBUG_ALL_CYCLES(std::cout <<
            "tree edge: " << e << '\n' <<
            "set predecessor of " << target(e, g) << " to " << e << '\n');
        predecessors_[target(e, g)] =  e;
    }

    void back_edge(Edge e, Graph const& g) {
        D2_DEBUG_ALL_CYCLES(std::cout <<
            "back edge: " << e << '\n' <<
            "making sure " << source(e, g) << " has a predecessor\n");
        BOOST_ASSERT_MSG(
            predecessors_.find(source(e,g)) != boost::end(predecessors_),
            "the predecessor edge of the source of the current edge is not "
            "defined, something's wrong");

        // Using the predecessor map maintained by the
        // edge_predecessor_recorder, we create a path of the form:
        // (u, v) (v, w) (w, x) ...
        // Representing the edges forming the cycle. We then call the adapted
        // visitor with that path, which is much easier to manipulate.
        Cycle cycle;
        cycle.push_front(e);
        typedef typename PredecessorMap::const_iterator PredecessorIterator;
        while (true) {
            PredecessorIterator it = predecessors_.find(source(e, g));
            if (it == boost::end(predecessors_))
                break;

            cycle.push_front(e = it->second);
        }

        // Since it is possible to have several connected components in the
        // graph, we must make sure we do not call the visitor with redundant
        // cycles that were already found in a previous search.
        if (seen_cycles.insert(cycle).second) {
            D2_DEBUG_ALL_CYCLES(
                std::cout << "Found cycle: ";
                std::copy(boost::begin(cycle), boost::end(cycle),
                    std::ostream_iterator<Edge>(std::cout, " "));
                std::cout << '\n';
            );
            visitor_.cycle(cycle, g);
        }
    }
};

/**
 * Dumb algorithm to compute all the cycles in a graph.
 *
 * It first does a depth first search and detects the cycles in the graph.
 * Then, it starts over a depth first search at each vertex implicated in a
 * cycle found during the first pass to find the cycles in every possible
 * direction.
 *
 * The visitor's `cycle` method is called with a constant reference to a
 * sequence of unspecified type and a constant reference to the graph being
 * visited.
 */
template <typename Graph, typename Visitor>
void all_cycles(Graph const& g, Visitor const& visitor) {
    typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex;
    typedef typename boost::graph_traits<Graph>::vertex_iterator VertexIter;
    typedef typename boost::graph_traits<Graph>::edge_descriptor Edge;
    typedef std::deque<Edge> Cycle;

    D2_DEBUG_ALL_CYCLES(std::cout <<
        "starting the all_cycles algorithm\n"
    <<  "    graph has " << num_vertices(g) << " vertices\n");

    if (num_vertices(g) == 0)
        return;

    std::set<Cycle> seen_cycles;
    typedef AllCyclesWrapper<Visitor, Graph, std::set<Cycle> > Wrapper;
    Wrapper wrapper(visitor, g, seen_cycles);

    // Perform a first dfs to find vertices implicated in a cycle.
    Vertex first_vertex = *vertices(g).first;
    boost::depth_first_search(g,
        boost::root_vertex(first_vertex).visitor(wrapper));

    D2_DEBUG_ALL_CYCLES(std::cout <<
        "first dfs done\n"
    <<  "    found " << seen_cycles.size() << " cycles\n");

    // Find all vertices implicated in a cycle.
    std::set<Vertex> vertices_in_a_cycle;
    BOOST_FOREACH(Cycle const& cycle, seen_cycles) {
        vertices_in_a_cycle.insert(source(cycle[0], g));
        BOOST_FOREACH(Edge const& edge, cycle)
            vertices_in_a_cycle.insert(target(edge, g));
    }

    D2_DEBUG_ALL_CYCLES(std::cout <<
    "    involving " << vertices_in_a_cycle.size() << " different vertices\n");

    // Start over a depth-first search at every vertex implicated in a cycle.
    // This allows us to find all the different cycles in the directed graph.
    // Let's say the first dfs found a->b->a;
    // the subsequent searches will find b->a->b
    vertices_in_a_cycle.erase(first_vertex); // We already visited that one
    BOOST_FOREACH(Vertex v, vertices_in_a_cycle)
        boost::depth_first_search(g, boost::root_vertex(v).visitor(wrapper));
}

} // end namespace detail
} // end namespace d2

#endif // !D2_DETAIL_ALL_CYCLES_HPP
<commit_msg>Fix grammar error. Implicated is not the same as involved :).<commit_after>/**
 * This file defines the `all_cycles` graph algorithm to find all the
 * cycles in a directed graph.
 */

#ifndef D2_DETAIL_ALL_CYCLES_HPP
#define D2_DETAIL_ALL_CYCLES_HPP

#include <boost/assert.hpp>
#include <boost/concept/assert.hpp>
#include <boost/concept_check.hpp>
#include <boost/foreach.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/unordered_map.hpp>
#include <deque>
#include <iostream>
#include <iterator>
#include <set>
#include <vector>

namespace d2 {
namespace detail {

// Note: You can enable/disable this to debug the algorithm. Some information
//       will be printed to stdout.
#define D2_DEBUG_ALL_CYCLES(statement) do { } while (false)
// #define D2_DEBUG_ALL_CYCLES(statement) do { statement; } while (false)

/**
 * Wrapper visitor for use within the `all_cycles` algorithm.
 *
 * Its use is twofold:
 *  Keep track of the cycles seen so far, so the `all_cycles` algorithm can
 *  start its dfs only on vertices involved in a cycle.
 *
 *  Call a visitor complying to the `boost::tiernan_all_cycles` visitor
 *  interface on every cycle encountered.
 */
template <typename Visitor, typename Graph, typename SeenCycles>
class AllCyclesWrapper : public boost::dfs_visitor<> {
    Visitor visitor_;
    typedef typename boost::graph_traits<Graph>::edge_descriptor Edge;
    typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex;
    typedef typename SeenCycles::value_type Cycle;
    BOOST_CONCEPT_ASSERT((boost::FrontInsertionSequenceConcept<Cycle>));

    typedef boost::unordered_map<Vertex, Edge> PredecessorMap;
    PredecessorMap predecessors_;

    SeenCycles& seen_cycles;

public:
    AllCyclesWrapper(Visitor const& v, Graph const&, SeenCycles& seen)
        : visitor_(v), seen_cycles(seen)
    { }

    void tree_edge(Edge e, Graph const& g) {
        D2_DEBUG_ALL_CYCLES(std::cout <<
            "tree edge: " << e << '\n' <<
            "set predecessor of " << target(e, g) << " to " << e << '\n');
        predecessors_[target(e, g)] =  e;
    }

    void back_edge(Edge e, Graph const& g) {
        D2_DEBUG_ALL_CYCLES(std::cout <<
            "back edge: " << e << '\n' <<
            "making sure " << source(e, g) << " has a predecessor\n");
        BOOST_ASSERT_MSG(
            predecessors_.find(source(e,g)) != boost::end(predecessors_),
            "the predecessor edge of the source of the current edge is not "
            "defined, something's wrong");

        // Using the predecessor map maintained by the
        // edge_predecessor_recorder, we create a path of the form:
        // (u, v) (v, w) (w, x) ...
        // Representing the edges forming the cycle. We then call the adapted
        // visitor with that path, which is much easier to manipulate.
        Cycle cycle;
        cycle.push_front(e);
        typedef typename PredecessorMap::const_iterator PredecessorIterator;
        while (true) {
            PredecessorIterator it = predecessors_.find(source(e, g));
            if (it == boost::end(predecessors_))
                break;

            cycle.push_front(e = it->second);
        }

        // Since it is possible to have several connected components in the
        // graph, we must make sure we do not call the visitor with redundant
        // cycles that were already found in a previous search.
        if (seen_cycles.insert(cycle).second) {
            D2_DEBUG_ALL_CYCLES(
                std::cout << "Found cycle: ";
                std::copy(boost::begin(cycle), boost::end(cycle),
                    std::ostream_iterator<Edge>(std::cout, " "));
                std::cout << '\n';
            );
            visitor_.cycle(cycle, g);
        }
    }
};

/**
 * Dumb algorithm to compute all the cycles in a graph.
 *
 * It first does a depth first search and detects the cycles in the graph.
 * Then, it starts over a depth first search at each vertex involved in a
 * cycle found during the first pass to find the cycles in every possible
 * direction.
 *
 * The visitor's `cycle` method is called with a constant reference to a
 * sequence of unspecified type and a constant reference to the graph being
 * visited.
 */
template <typename Graph, typename Visitor>
void all_cycles(Graph const& g, Visitor const& visitor) {
    typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex;
    typedef typename boost::graph_traits<Graph>::vertex_iterator VertexIter;
    typedef typename boost::graph_traits<Graph>::edge_descriptor Edge;
    typedef std::deque<Edge> Cycle;

    D2_DEBUG_ALL_CYCLES(std::cout <<
        "starting the all_cycles algorithm\n"
    <<  "    graph has " << num_vertices(g) << " vertices\n");

    if (num_vertices(g) == 0)
        return;

    std::set<Cycle> seen_cycles;
    typedef AllCyclesWrapper<Visitor, Graph, std::set<Cycle> > Wrapper;
    Wrapper wrapper(visitor, g, seen_cycles);

    // Perform a first dfs to find vertices involved in a cycle.
    Vertex first_vertex = *vertices(g).first;
    boost::depth_first_search(g,
        boost::root_vertex(first_vertex).visitor(wrapper));

    D2_DEBUG_ALL_CYCLES(std::cout <<
        "first dfs done\n"
    <<  "    found " << seen_cycles.size() << " cycles\n");

    // Find all vertices involved in a cycle.
    std::set<Vertex> vertices_in_a_cycle;
    BOOST_FOREACH(Cycle const& cycle, seen_cycles) {
        vertices_in_a_cycle.insert(source(cycle[0], g));
        BOOST_FOREACH(Edge const& edge, cycle)
            vertices_in_a_cycle.insert(target(edge, g));
    }

    D2_DEBUG_ALL_CYCLES(std::cout <<
    "    involving " << vertices_in_a_cycle.size() << " different vertices\n");

    // Start over a depth-first search at every vertex involved in a cycle.
    // This allows us to find all the different cycles in the directed graph.
    // Let's say the first dfs found a->b->a;
    // the subsequent searches will find b->a->b
    vertices_in_a_cycle.erase(first_vertex); // We already visited that one
    BOOST_FOREACH(Vertex v, vertices_in_a_cycle)
        boost::depth_first_search(g, boost::root_vertex(v).visitor(wrapper));
}

} // end namespace detail
} // end namespace d2

#endif // !D2_DETAIL_ALL_CYCLES_HPP
<|endoftext|>