<commit_before>//---------------------------------------------------------
// Copyright 2017 Ontario Institute for Cancer Research
// Written by Jared Simpson (jared.simpson@oicr.on.ca)
//---------------------------------------------------------
//
// nanopolish_progressive_align -- align squiggle events
// to kmers of a sequence using a banded,
// progressive algorithm
//
#include "nanopolish_progressive_align.h"
#include "nanopolish_profile_hmm.h"

void progressive_align(SquiggleRead& read,
                       const std::string& sequence)
{
    size_t strand_idx = 0;
    size_t EVENTS_PER_BLOCK = 200;
    size_t BASES_PER_BLOCK = 100;

    // Build a debug event->kmer map
    std::vector<size_t> kmer_for_event(read.events[strand_idx].size());
    for(size_t ki = 0; ki < read.base_to_event_map.size(); ++ki) {
        IndexPair& elem = read.base_to_event_map[ki].indices[0];
        if(elem.start == -1) {
            continue;
        }

        for(size_t j = elem.start; j <= elem.stop; ++j) {
            kmer_for_event[j] = ki;
        }
    }

    // For now we assume the first event in the array matches the first k-mer in the sequence
    size_t curr_k_idx = 0;
    size_t curr_event_idx = 0;

    fprintf(stderr, "aligning events for read %s [shift=%.2lf, scale=%.2lf]\n", read.read_name.substr(0, 6).c_str(),
                                                                                read.pore_model[0].shift,
                                                                                read.pore_model[0].scale);

    /*
    double events_per_base = (double)read.events[strand_idx].size() / sequence.size();
    double events_per_base_per_block_upper_bound = events_per_base * 1.5;
    fprintf(stderr, "parameters -- events_per_base: %.2lf upper bound: %.2lf\n", events_per_base, events_per_base_per_block_upper_bound);
    */

    while(1) {

        size_t end_k_idx = curr_k_idx + BASES_PER_BLOCK;
        size_t end_event_idx = curr_event_idx + EVENTS_PER_BLOCK;

        std::string block_seq = sequence.substr(curr_k_idx, end_k_idx - curr_k_idx);
        HMMInputSequence hmm_sequence(block_seq);

        HMMInputData input;
        input.read = &read;
        input.event_start_idx = curr_event_idx;
        input.event_stop_idx = end_event_idx;

        input.strand = strand_idx;
        input.event_stride = 1;
        input.rc = false;

        std::vector<HMMAlignmentState> event_alignment = profile_hmm_align(hmm_sequence, input, HAF_ALLOW_POST_CLIP);

        for(size_t eai = 0; eai < event_alignment.size(); ++eai) {

            HMMAlignmentState& as = event_alignment[eai];
            /*
            if(as.state == 'K') {
                continue;
            }
            */
            size_t k_idx = curr_k_idx + as.kmer_idx;
            fprintf(stderr, "Event %zu aligns to kmer %zu [debug: %zu, state: %c]\n", as.event_idx, k_idx, kmer_for_event[as.event_idx], as.state);
            /*
            EventAlignment ea;

            // ref
            ea.ref_name = ref_name;
            ea.ref_position = curr_start_ref + as.kmer_idx;
            ea.ref_kmer = ref_seq.substr(ea.ref_position - ref_offset, k);

            // event
            ea.read_idx = params.read_idx;
            ea.strand_idx = params.strand_idx;
            ea.event_idx = as.event_idx;
            ea.rc = input.rc;

            // hmm
            ea.hmm_state = as.state;

            if(ea.hmm_state != 'B') {
                ea.model_kmer = hmm_sequence.get_kmer(as.kmer_idx, k, input.rc);
            } else {
                ea.model_kmer = std::string(k, 'N');
            }

            // store
            alignment_output.push_back(ea);

            // update
            last_event_output = as.event_idx;
            last_ref_kmer_output = curr_start_ref + as.kmer_idx;

            num_output += 1;
            */
        }

        break;
    }

    exit(0);
}
<commit_msg>implemented very simple banded HMM for quickly aligning events to read kmers<commit_after>//---------------------------------------------------------
// Copyright 2017 Ontario Institute for Cancer Research
// Written by Jared Simpson (jared.simpson@oicr.on.ca)
//---------------------------------------------------------
//
// nanopolish_progressive_align -- align squiggle events
// to kmers of a sequence using a banded,
// progressive algorithm
//
#include "nanopolish_progressive_align.h"
#include "nanopolish_profile_hmm.h"

void progressive_align(SquiggleRead& read,
                       const std::string& sequence)
{
    size_t strand_idx = 0;
    size_t k = read.pore_model[strand_idx].k;
    const Alphabet* alphabet = read.pore_model[strand_idx].pmalphabet;

    // Build a debug event->kmer map
    std::vector<size_t> kmer_for_event(read.events[strand_idx].size());
    for(size_t ki = 0; ki < read.base_to_event_map.size(); ++ki) {
        IndexPair& elem = read.base_to_event_map[ki].indices[0];
        if(elem.start == -1) {
            continue;
        }

        for(size_t j = elem.start; j <= elem.stop; ++j) {
            kmer_for_event[j] = ki;
        }
    }

    const uint8_t FROM_D = 0;
    const uint8_t FROM_U = 1;
    const uint8_t FROM_L = 2;

    // banding
    int bandwidth = 1000;
    int half_band = bandwidth / 2;

    // transitions
    double lp_skip = log(0.001);
    double lp_stay = log(0.5);
    double lp_step = log(1.0 - exp(lp_skip) - exp(lp_stay));
    double lp_trim = log(0.1);

    size_t n_events = read.events[strand_idx].size();
    size_t n_kmers = sequence.size() - k + 1;

    // Calculate the minimum event index that is within the band for each read kmer
    // We determine this using the expected number of events observed per kmer
    double events_per_kmer = (double)n_events / n_kmers;
    std::vector<int> min_event_idx_by_kmer(n_kmers);
    for(size_t ki = 0; ki < n_kmers; ++ki) {
        int expected_event_idx = (double)(ki * events_per_kmer);
        min_event_idx_by_kmer[ki] = std::max(expected_event_idx - half_band, 0);
    }
    fprintf(stderr, "events per base: %.2lf\n", events_per_kmer);
    // Initialize DP matrices
    DoubleMatrix viterbi_matrix;
    UInt8Matrix backtrack_matrix;

    size_t n_rows = bandwidth;
    size_t n_cols = n_kmers + 1;
    allocate_matrix(viterbi_matrix, n_rows, n_cols);
    allocate_matrix(backtrack_matrix, n_rows, n_cols);

    for(size_t i = 0; i < bandwidth; ++i) {
        set(viterbi_matrix, i, 0, i * lp_trim);
        set(backtrack_matrix, i, 0, 0);
    }

    // Fill in the matrix
    for(int col = 1; col < n_cols; ++col) {
        int kmer_idx = col - 1;
        int min_event_idx = min_event_idx_by_kmer[kmer_idx];
        int min_event_idx_prev_col = kmer_idx > 0 ? min_event_idx_by_kmer[kmer_idx - 1] : 0;
        size_t kmer_rank = alphabet->kmer_rank(sequence.substr(kmer_idx, k).c_str(), k);

        for(int row = 0; row < n_rows; ++row) {

            int event_idx = min_event_idx + row;
            if(event_idx > n_events) {
                set(viterbi_matrix, row, col, -INFINITY);
            }

            // dp update
            // here we are calculating whether the event for each neighboring cell is within the band
            // and calculating its position within the column
            int row_up = event_idx - min_event_idx - 1;
            int row_diag = event_idx - min_event_idx_prev_col - 1;
            int row_left = event_idx - min_event_idx_prev_col;

            double up = row_up >= 0 && row_up < n_rows ?        get(viterbi_matrix, row_up, col) : -INFINITY;
            double diag = row_diag >= 0 && row_diag < n_rows ?  get(viterbi_matrix, row_diag, col - 1) : -INFINITY;
            double left = row_left >= 0 && row_left < n_rows ?  get(viterbi_matrix, row_left, col - 1) : -INFINITY;

            float lp_emission = log_probability_match_r9(read, kmer_rank, event_idx, strand_idx);

            double score_d = diag + lp_step + lp_emission;
            double score_u = up + lp_stay + lp_emission;
            double score_l = left + lp_skip;

            double max_score = score_d;
            uint8_t from = FROM_D;

            max_score = score_u > max_score ? score_u : max_score;
            from = max_score == score_u ? FROM_U : from;

            max_score = score_l > max_score ? score_l : max_score;
            from = max_score == score_l ? FROM_L : from;

            set(viterbi_matrix, row, col, max_score);
            set(backtrack_matrix, row, col, from);

            /*
            if(kmer_idx == 5000) {
                fprintf(stderr, "event: %zu kmer: %zu row: %d col: %d\n", event_idx, kmer_idx, row, col);
                fprintf(stderr, "\tmin_event: %d min_event_prev_col: %d\n", min_event_idx, min_event_idx_prev_col);
                fprintf(stderr, "\tdiag event: %d row: %d\n", min_event_idx + row_diag, row_diag);
                fprintf(stderr, "\tup event: %d row: %d\n", min_event_idx_prev_col + row_up, row_up);
                fprintf(stderr, "\tleft event: %d row: %d\n", min_event_idx_prev_col + row_left, row_left);
            }
            */
        }
    }

    // Backtrack

    // Initialize by finding best alignment between an event and the last kmer
    int curr_k_idx = n_kmers - 1;
    int curr_event_idx = 0;
    double max_score = -INFINITY;
    for(size_t row = 0; row < n_rows; ++row) {
        size_t col = curr_k_idx + 1;
        int ei = row + min_event_idx_by_kmer[curr_k_idx];
        double s = get(viterbi_matrix, row, col) + (n_events - ei - 1) * lp_trim;
        if(s > max_score && ei < n_events) {
            max_score = s;
            curr_event_idx = ei;
        }
    }

    // debug stats
    int sum_distance_from_debug = 0;
    int max_distance_from_debug = 0;
    int num_exact_matches = 0;
    int max_distance_from_expected = 0;
    int sum_distance_from_expected = 0;

    while(curr_k_idx >= 0) {
        // emit alignment
        fprintf(stderr, "k: %d e: %d d: %d\n", curr_k_idx, curr_event_idx, kmer_for_event[curr_event_idx]);

        // update debug stats
        int kd = abs(curr_k_idx - kmer_for_event[curr_event_idx]);
        sum_distance_from_debug += kd;
        max_distance_from_debug = std::max(kd, max_distance_from_debug);
        num_exact_matches += curr_k_idx == kmer_for_event[curr_event_idx];

        int expected_event = (double)(curr_k_idx * events_per_kmer);
        int ed = curr_event_idx - expected_event;
        max_distance_from_expected = std::max(abs(ed), max_distance_from_expected);
        sum_distance_from_expected += ed;

        //fprintf(stderr, "k: %d ed: %d\n", curr_k_idx, ed);

        // update indices using backtrack pointers
        int row = curr_event_idx - min_event_idx_by_kmer[curr_k_idx];
        int col = curr_k_idx + 1;

        uint8_t from = get(backtrack_matrix, row, col);
        if(from == FROM_D) {
            curr_k_idx -= 1;
            curr_event_idx -= 1;
        } else if(from == FROM_U) {
            curr_event_idx -= 1;
        } else {
            curr_k_idx -= 1;
        }
    }

    fprintf(stderr, "truth stats -- avg: %.2lf max: %d exact: %d\n", (double)sum_distance_from_debug / n_kmers, max_distance_from_debug, num_exact_matches);
    fprintf(stderr, "event stats -- avg: %.2lf max: %d\n", (double)sum_distance_from_expected / n_events, max_distance_from_expected);
    free_matrix(viterbi_matrix);
    free_matrix(backtrack_matrix);

    exit(EXIT_FAILURE);
}

void progressive_align2(SquiggleRead& read,
                       const std::string& sequence)
{
    size_t strand_idx = 0;
    size_t EVENTS_PER_BLOCK = 200;
    size_t BASES_PER_BLOCK = 100;

    // Build a debug event->kmer map
    std::vector<size_t> kmer_for_event(read.events[strand_idx].size());
    for(size_t ki = 0; ki < read.base_to_event_map.size(); ++ki) {
        IndexPair& elem = read.base_to_event_map[ki].indices[0];
        if(elem.start == -1) {
            continue;
        }

        for(size_t j = elem.start; j <= elem.stop; ++j) {
            kmer_for_event[j] = ki;
        }
    }

    // For now we assume the first event in the array matches the first k-mer in the sequence
    size_t curr_k_idx = 0;
    size_t curr_event_idx = 0;

    fprintf(stderr, "aligning events for read %s [shift=%.2lf, scale=%.2lf]\n", read.read_name.substr(0, 6).c_str(),
                                                                                read.pore_model[0].shift,
                                                                                read.pore_model[0].scale);

    /*
    double events_per_base = (double)read.events[strand_idx].size() / sequence.size();
    double events_per_base_per_block_upper_bound = events_per_base * 1.5;
    fprintf(stderr, "parameters -- events_per_base: %.2lf upper bound: %.2lf\n", events_per_base, events_per_base_per_block_upper_bound);
    */

    while(1) {

        size_t end_k_idx = curr_k_idx + BASES_PER_BLOCK;
        size_t end_event_idx = curr_event_idx + EVENTS_PER_BLOCK;

        std::string block_seq = sequence.substr(curr_k_idx, end_k_idx - curr_k_idx);
        HMMInputSequence hmm_sequence(block_seq);

        HMMInputData input;
        input.read = &read;
        input.event_start_idx = curr_event_idx;
        input.event_stop_idx = end_event_idx;

        input.strand = strand_idx;
        input.event_stride = 1;
        input.rc = false;

        std::vector<HMMAlignmentState> event_alignment = profile_hmm_align(hmm_sequence, input, HAF_ALLOW_POST_CLIP);

        for(size_t eai = 0; eai < event_alignment.size(); ++eai) {

            HMMAlignmentState& as = event_alignment[eai];
            /*
            if(as.state == 'K') {
                continue;
            }
            */
            size_t k_idx = curr_k_idx + as.kmer_idx;
            fprintf(stderr, "Event %zu aligns to kmer %zu [debug: %zu, state: %c]\n", as.event_idx, k_idx, kmer_for_event[as.event_idx], as.state);
            /*
            EventAlignment ea;

            // ref
            ea.ref_name = ref_name;
            ea.ref_position = curr_start_ref + as.kmer_idx;
            ea.ref_kmer = ref_seq.substr(ea.ref_position - ref_offset, k);

            // event
            ea.read_idx = params.read_idx;
            ea.strand_idx = params.strand_idx;
            ea.event_idx = as.event_idx;
            ea.rc = input.rc;

            // hmm
            ea.hmm_state = as.state;

            if(ea.hmm_state != 'B') {
                ea.model_kmer = hmm_sequence.get_kmer(as.kmer_idx, k, input.rc);
            } else {
                ea.model_kmer = std::string(k, 'N');
            }

            // store
            alignment_output.push_back(ea);

            // update
            last_event_output = as.event_idx;
            last_ref_kmer_output = curr_start_ref + as.kmer_idx;

            num_output += 1;
            */
        }

        break;
    }

    exit(0);
}
<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <vexcl/vexcl.hpp>

using namespace std;

int main (int argc, char* argv[])
{
	//set some default values for when no commandline arguments are given
	int accuracy = 90;
	int polygons = 50;
	int vertices = 6;
    //read input commandline arguments
    for (int i = 1; i < argc; ++i)
    {
        if (std::string(argv[i]) == "-a")
            {
                //initialise desired accuracy variable according to commandline argument -a
                accuracy = ;

            }
        if (std::string(argv[i]) == "-p")
            {
                //initialise maximum polygons variable according to commandline argument -p
                polygons = ;

            }
        if (std::string(argv[i]) == "-v")
            {
                //initialise maximum verices per polygon variable according to commandline argument -v
                vertices = ;

            }
    }
    //use vexcl to initialise compute devices (gpu, cpu, apu)
    vex::Context ctx( vex::Filter::Type(CL_DEVICE_TYPE_GPU) && vex::Filter::DoublePrecision );

    if (!ctx) throw std::runtime_error("No devices available.");

    // Print out list of selected devices:
    std::cout << ctx << std::endl;
    //read specified input image file into the gpu memory

    importimage(argv[]);

    //initialise variables
	class DNA
	{
		public:
		string genome;
        int polygons;
        int vertices;
        polygon[polygons];

	}
    class polygon;
    {
        public:
        int x[vertices];
        int y[vertices];
        int R;
        int G;
        int B;
        int alpha;
    }

    //initialise DNA with a random seed
    leaderDNA = seedDNA();
    //run render loop until desired accuracy is reached
    while (leaderaccuracy<accuracy)
        {
            //mutate from the leaderDNA
            mutatedDNA = mutateDNA(leaderDNA)
            //compute fitness of mutation vs leaderDNA
            //check if it is fitter, if so overwrite leaderDNA
            if (computefitness(leaderDNA,mutatedDNA) == 1)
            {
                //overwrite leaderDNA
                leaderDNA = mutatedDNA;
            }
            //compute accuracy percent
            computefitnesspercent(leaderDNA, originalimage)
        }
    //perform final render, output svg and raster image
    saverender(leaderDNA);
}
int computefitnesspercent (DNA, originalimage)
{
    //compute what % match DNA is to original image
}
int computefitness (DNA0, DNA1)
{
	//compute the fitness of input DNA, i.e. how close is it to original image?

    //read leader dna

    //compare input dna to leader dna to find changed polygons
    compareDNA(leaderDNA,DNA);
    //create bounding box containing changed polygons

    //render leader dna within bounding box
    leaderrender = renderDNA(leaderDNA,boundx,boundy);
    //render input dna within bounding box
    inputrender = renderDNA(DNA,boundx,boundy);
    //compare leader and input dna rendered bounding boxes
    compareimage(leaderrender,inputrender);
    //returns 1 if dna1 is fitter than dna0, else returns 0
}
int seedDNA()
{
    //create a random seed dna
}
int compareDNA(DNA0,DNA1)
{
    //compare DNA0 to DNA1 to find changed polygons
}
int compareimage(image0,image1)
{
    //compare two raster images, return 1 if image1 fitter than image0, else return 0
    char *img1data, *img2data, fname[15];
    int s, n = 0, y = 0;

    sprintf(fname, "photo%d.bmp", p - 1);
    cout << "\n" << fname;
    ifstream img1(fname, ios::in|ios::binary|ios::ate);
    sprintf(fname, "photo%d.bmp", p);
    cout << "\n" << fname;
    ifstream img2(fname, ios::in|ios::binary|ios::ate);

    if (img1.is_open() && img2.is_open())
    {
        s = (int)img1.tellg();
        img1data = new char [s];
        img1.seekg (0, ios::beg);
        img1.read (img1data, s);
        img1.close();

        img2data = new char [s];
        img2.seekg (0, ios::beg);
        img2.read (img2data, s);
        img2.close();
    }

    for(int i=0; i<s; i++)
        if (img1data[i]==img2data[i]) y++;

    return (y);
}

int renderDNA (DNA, boundx0, boundy0, boundx1, boundy1)
{
	//render input DNA to a raster image and svg

    //render to raster image

    //read DNA from gpu memory

    //initialise a new opengl image and render the dna within bounding box into it
    cairo_set_source_rgb(cr, 1, 1, 1);
    cairo_rectangle(cr, 0, 0, WIDTH, HEIGHT);
    cairo_fill(cr);
    for(int i = 0; i < NUM_SHAPES; i++)
        draw_shape(dna, cr, i);
    //render to svg
}

void draw_shape(shape_t * dna, cairo_t * cr, int i)
{
	//render an individual shape within a DNA strand using opengl
    cairo_set_line_width(cr, 0);
    shape_t * shape = &dna[i];
    cairo_set_source_rgba(cr, shape->r, shape->g, shape->b, shape->a);
    cairo_move_to(cr, shape->points[0].x, shape->points[0].y);
    for(int j = 1; j < NUM_POINTS; j++)
        cairo_line_to(cr, shape->points[j].x, shape->points[j].y);
    cairo_fill(cr);
}

}

int mutateDNA (DNA)
{
	//mutate input DNA randomly
	    mutated_shape = RANDINT(NUM_SHAPES);
    double roulette = RANDDOUBLE(2.8);
    double drastic = RANDDOUBLE(2);

    // mutate color
    if(roulette<1)
    {
        if(dna_test[mutated_shape].a < 0.01 // completely transparent shapes are stupid
                || roulette<0.25)
        {
            if(drastic < 1)
            {
                dna_test[mutated_shape].a += RANDDOUBLE(0.1);
                dna_test[mutated_shape].a = CLAMP(dna_test[mutated_shape].a, 0.0, 1.0);
            }
            else
                dna_test[mutated_shape].a = RANDDOUBLE(1.0);
        }
        else if(roulette<0.50)
        {
            if(drastic < 1)
            {
                dna_test[mutated_shape].r += RANDDOUBLE(0.1);
                dna_test[mutated_shape].r = CLAMP(dna_test[mutated_shape].r, 0.0, 1.0);
            }
            else
                dna_test[mutated_shape].r = RANDDOUBLE(1.0);
        }
        else if(roulette<0.75)
        {
            if(drastic < 1)
            {
                dna_test[mutated_shape].g += RANDDOUBLE(0.1);
                dna_test[mutated_shape].g = CLAMP(dna_test[mutated_shape].g, 0.0, 1.0);
            }
            else
                dna_test[mutated_shape].g = RANDDOUBLE(1.0);
        }
        else
        {
            if(drastic < 1)
            {
                dna_test[mutated_shape].b += RANDDOUBLE(0.1);
                dna_test[mutated_shape].b = CLAMP(dna_test[mutated_shape].b, 0.0, 1.0);
            }
            else
                dna_test[mutated_shape].b = RANDDOUBLE(1.0);
        }
    }

    // mutate shape
    else if(roulette < 2.0)
    {
        int point_i = RANDINT(NUM_POINTS);
        if(roulette<1.5)
        {
            if(drastic < 1)
            {
                dna_test[mutated_shape].points[point_i].x += (int)RANDDOUBLE(WIDTH/10.0);
                dna_test[mutated_shape].points[point_i].x = CLAMP(dna_test[mutated_shape].points[point_i].x, 0, WIDTH-1);
            }
            else
                dna_test[mutated_shape].points[point_i].x = RANDDOUBLE(WIDTH);
        }
        else
        {
            if(drastic < 1)
            {
                dna_test[mutated_shape].points[point_i].y += (int)RANDDOUBLE(HEIGHT/10.0);
                dna_test[mutated_shape].points[point_i].y = CLAMP(dna_test[mutated_shape].points[point_i].y, 0, HEIGHT-1);
            }
            else
                dna_test[mutated_shape].points[point_i].y = RANDDOUBLE(HEIGHT);
        }
    }

    // mutate stacking
    else
    {
        int destination = RANDINT(NUM_SHAPES);
        shape_t s = dna_test[mutated_shape];
        dna_test[mutated_shape] = dna_test[destination];
        dna_test[destination] = s;
        return destination;
    }
    return -1;

}
}

int saveDNA(DNA)
{
	int result;

	while(1)
	{
		// start by writing a temp file.

		pFile = fopen ("volution.dna.temp","w");
		fprintf (pFile, DNA);
		fclose (pFile);

		// Then rename the real backup to a secondary backup.

		result = rename("volution.dna","volution_2.dna");

		// Then rename the temp file to the primary backup

		result = rename("volution.dna.temp","volution.dna");

		// Then delete the temp file

		result = remove("volution.dna.temp");

	}
}

int saverender(DNA)
{
    //render DNA and save resulting image to disk as .svg file and raster image (.png)

    //render image from DNA
    renderDNA(DNA);
    //save resultant image to disk as svg and png files
}

int importimage(image)
{
    //import specified image into the gpu memory
    // Load input image from file and load it into
    // an OpenCL image object
    int width, height;
    imageObjects[0] = LoadImage(context, argv[1], width, height);
    if (imageObjects[0] == 0)
    {
        std::cerr << "Error loading: " << std::string(argv[1]) << std::endl;
        Cleanup(context, commandQueue, program, kernel, imageObjects, sampler);
        return 1;
    }
}

int drawshape()
{
	//user draws shape from ui

    //start drawing shape vertex by vertex
    //until max number of vertices reached or user closes polygon by clicking on initial vertex

    //check if drawn shape gives a better fitness

    //if it does update the leaderDNA
}

int dragvertex()
{
	//user drags vertex from ui

    //user stops dragging vertex

    //check if dragged vertex improves fitness

    //if it does update the leaderDNA
}<commit_msg>Added method for getting the bounding box<commit_after>#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <vexcl/vexcl.hpp>

using namespace std;

int main (int argc, char* argv[])
{
	//set some default values for when no commandline arguments are given
	int accuracy = 90;
	int polygons = 50;
	int vertices = 6;
    //read input commandline arguments
    for (int i = 1; i < argc; ++i)
    {
        if (std::string(argv[i]) == "-a")
            {
                //initialise desired accuracy variable according to commandline argument -a
                accuracy = ;

            }
        if (std::string(argv[i]) == "-p")
            {
                //initialise maximum polygons variable according to commandline argument -p
                polygons = ;

            }
        if (std::string(argv[i]) == "-v")
            {
                //initialise maximum verices per polygon variable according to commandline argument -v
                vertices = ;

            }
    }
    //use vexcl to initialise compute devices (gpu, cpu, apu)
    vex::Context ctx( vex::Filter::Type(CL_DEVICE_TYPE_GPU) && vex::Filter::DoublePrecision );

    if (!ctx) throw std::runtime_error("No devices available.");

    // Print out list of selected devices:
    std::cout << ctx << std::endl;
    //read specified input image file into the gpu memory

    importimage(argv[]);

    //initialise variables
	class DNA
	{
		public:
		string genome;
        int polygons;
        int vertices;
        polygon[polygons];

	}
    class polygon;
    {
        public:
        int x[vertices];
        int y[vertices];
        int R;
        int G;
        int B;
        int alpha;
    }

    //initialise DNA with a random seed
    leaderDNA = seedDNA();
    //run render loop until desired accuracy is reached
    while (leaderaccuracy<accuracy)
        {
            //mutate from the leaderDNA
            mutatedDNA = mutateDNA(leaderDNA)
            //compute fitness of mutation vs leaderDNA
            //check if it is fitter, if so overwrite leaderDNA
            if (computefitness(leaderDNA,mutatedDNA) == 1)
            {
                //overwrite leaderDNA
                leaderDNA = mutatedDNA;
            }
            //compute accuracy percent
            computefitnesspercent(leaderDNA, originalimage)
        }
    //perform final render, output svg and raster image
    saverender(leaderDNA);
}
int computefitnesspercent (DNA, originalimage)
{
    //compute what % match DNA is to original image
}
int computefitness (DNA0, DNA1)
{
	//compute the fitness of input DNA, i.e. how close is it to original image?

    //read leader dna

    //compare input dna to leader dna to find changed polygons
    compareDNA(leaderDNA,DNA);
    //create bounding box containing changed polygons

    //render leader dna within bounding box
    leaderrender = renderDNA(leaderDNA,boundx,boundy);
    //render input dna within bounding box
    inputrender = renderDNA(DNA,boundx,boundy);
    //compare leader and input dna rendered bounding boxes
    compareimage(leaderrender,inputrender);
    //returns 1 if dna1 is fitter than dna0, else returns 0
}
int seedDNA()
{
    //create a random seed dna
}
int compareDNA(DNA0,DNA1)
{
    //compare DNA0 to DNA1 to find changed polygons
}
int compareimage(image0,image1)
{
    //compare two raster images, return 1 if image1 fitter than image0, else return 0
    char *img1data, *img2data, fname[15];
    int s, n = 0, y = 0;

    sprintf(fname, "photo%d.bmp", p - 1);
    cout << "\n" << fname;
    ifstream img1(fname, ios::in|ios::binary|ios::ate);
    sprintf(fname, "photo%d.bmp", p);
    cout << "\n" << fname;
    ifstream img2(fname, ios::in|ios::binary|ios::ate);

    if (img1.is_open() && img2.is_open())
    {
        s = (int)img1.tellg();
        img1data = new char [s];
        img1.seekg (0, ios::beg);
        img1.read (img1data, s);
        img1.close();

        img2data = new char [s];
        img2.seekg (0, ios::beg);
        img2.read (img2data, s);
        img2.close();
    }

    for(int i=0; i<s; i++)
        if (img1data[i]==img2data[i]) y++;

    return (y);
}

int renderDNA (DNA, boundx0, boundy0, boundx1, boundy1)
{
	//render input DNA to a raster image and svg

    //render to raster image

    //read DNA from gpu memory

    //initialise a new opengl image and render the dna within bounding box into it
    cairo_set_source_rgb(cr, 1, 1, 1);
    cairo_rectangle(cr, 0, 0, WIDTH, HEIGHT);
    cairo_fill(cr);
    for(int i = 0; i < NUM_SHAPES; i++)
        draw_shape(dna, cr, i);
    //render to svg
}

void draw_shape(shape_t * dna, cairo_t * cr, int i)
{
	// Set variables that define the boundary of the bounding box in
	// computefitness and renderDNA
	int xmax = 0, xmin = 0, ymax = 0, ymin = 0;

	//render an individual shape within a DNA strand using opengl
    cairo_set_line_width(cr, 0);
    shape_t * shape = &dna[i];
    cairo_set_source_rgba(cr, shape->r, shape->g, shape->b, shape->a);
    cairo_move_to(cr, shape->points[0].x, shape->points[0].y);
    for(int j = 1; j < NUM_POINTS; j++)
		// Check the X and Y values of the boundary box & the polygon
		// Expand the bounding box edges if necessary
		xmax = (shape->points[j].x > xmax) ? shape->points[j].x : xmax ;
		xmin = (shape->points[j].x < xmin) ? shape->points[j].x : xmin ;
		ymax = (shape->points[j].y > ymax) ? shape->points[j].y : ymax ;
		ymin = (shape->points[j].y < ymin) ? shape->points[j].y : ymin ;

        cairo_line_to(cr, shape->points[j].x, shape->points[j].y);

    cairo_fill(cr);
}

}

int mutateDNA (DNA)
{
	//mutate input DNA randomly
	    mutated_shape = RANDINT(NUM_SHAPES);
    double roulette = RANDDOUBLE(2.8);
    double drastic = RANDDOUBLE(2);

    // mutate color
    if(roulette<1)
    {
        if(dna_test[mutated_shape].a < 0.01 // completely transparent shapes are stupid
                || roulette<0.25)
        {
            if(drastic < 1)
            {
                dna_test[mutated_shape].a += RANDDOUBLE(0.1);
                dna_test[mutated_shape].a = CLAMP(dna_test[mutated_shape].a, 0.0, 1.0);
            }
            else
                dna_test[mutated_shape].a = RANDDOUBLE(1.0);
        }
        else if(roulette<0.50)
        {
            if(drastic < 1)
            {
                dna_test[mutated_shape].r += RANDDOUBLE(0.1);
                dna_test[mutated_shape].r = CLAMP(dna_test[mutated_shape].r, 0.0, 1.0);
            }
            else
                dna_test[mutated_shape].r = RANDDOUBLE(1.0);
        }
        else if(roulette<0.75)
        {
            if(drastic < 1)
            {
                dna_test[mutated_shape].g += RANDDOUBLE(0.1);
                dna_test[mutated_shape].g = CLAMP(dna_test[mutated_shape].g, 0.0, 1.0);
            }
            else
                dna_test[mutated_shape].g = RANDDOUBLE(1.0);
        }
        else
        {
            if(drastic < 1)
            {
                dna_test[mutated_shape].b += RANDDOUBLE(0.1);
                dna_test[mutated_shape].b = CLAMP(dna_test[mutated_shape].b, 0.0, 1.0);
            }
            else
                dna_test[mutated_shape].b = RANDDOUBLE(1.0);
        }
    }

    // mutate shape
    else if(roulette < 2.0)
    {
        int point_i = RANDINT(NUM_POINTS);
        if(roulette<1.5)
        {
            if(drastic < 1)
            {
                dna_test[mutated_shape].points[point_i].x += (int)RANDDOUBLE(WIDTH/10.0);
                dna_test[mutated_shape].points[point_i].x = CLAMP(dna_test[mutated_shape].points[point_i].x, 0, WIDTH-1);
            }
            else
                dna_test[mutated_shape].points[point_i].x = RANDDOUBLE(WIDTH);
        }
        else
        {
            if(drastic < 1)
            {
                dna_test[mutated_shape].points[point_i].y += (int)RANDDOUBLE(HEIGHT/10.0);
                dna_test[mutated_shape].points[point_i].y = CLAMP(dna_test[mutated_shape].points[point_i].y, 0, HEIGHT-1);
            }
            else
                dna_test[mutated_shape].points[point_i].y = RANDDOUBLE(HEIGHT);
        }
    }

    // mutate stacking
    else
    {
        int destination = RANDINT(NUM_SHAPES);
        shape_t s = dna_test[mutated_shape];
        dna_test[mutated_shape] = dna_test[destination];
        dna_test[destination] = s;
        return destination;
    }
    return -1;

}
}

int saveDNA(DNA)
{
	int result;

	while(1)
	{
		// start by writing a temp file.

		pFile = fopen ("volution.dna.temp","w");
		fprintf (pFile, DNA);
		fclose (pFile);

		// Then rename the real backup to a secondary backup.

		result = rename("volution.dna","volution_2.dna");

		// Then rename the temp file to the primary backup

		result = rename("volution.dna.temp","volution.dna");

		// Then delete the temp file

		result = remove("volution.dna.temp");

	}
}

int saverender(DNA)
{
    //render DNA and save resulting image to disk as .svg file and raster image (.png)

    //render image from DNA
    renderDNA(DNA);
    //save resultant image to disk as svg and png files
}

int importimage(image)
{
    //import specified image into the gpu memory
    // Load input image from file and load it into
    // an OpenCL image object
    int width, height;
    imageObjects[0] = LoadImage(context, argv[1], width, height);
    if (imageObjects[0] == 0)
    {
        std::cerr << "Error loading: " << std::string(argv[1]) << std::endl;
        Cleanup(context, commandQueue, program, kernel, imageObjects, sampler);
        return 1;
    }
}

int drawshape()
{
	//user draws shape from ui

    //start drawing shape vertex by vertex
    //until max number of vertices reached or user closes polygon by clicking on initial vertex

    //check if drawn shape gives a better fitness

    //if it does update the leaderDNA
}

int dragvertex()
{
	//user drags vertex from ui

    //user stops dragging vertex

    //check if dragged vertex improves fitness

    //if it does update the leaderDNA
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <cctype>
#include <algorithm>

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/optional.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/identify_client.hpp"
#include "libtorrent/fingerprint.hpp"

namespace
{

	using namespace libtorrent;

	int decode_digit(char c)
	{
		if (std::isdigit(c)) return c - '0';
		return unsigned(c) - 'A' + 10;
	}

	// takes a peer id and returns a valid boost::optional
	// object if the peer id matched the azureus style encoding
	// the returned fingerprint contains information about the
	// client's id
	boost::optional<fingerprint> parse_az_style(const peer_id& id)
	{
		fingerprint ret("..", 0, 0, 0, 0);

		if (id[0] != '-' || !std::isprint(id[1]) || (id[2] < '0')
			|| (id[3] < '0') || (id[4] < '0')
			|| (id[5] < '0') || (id[6] < '0')
			|| id[7] != '-')
			return boost::optional<fingerprint>();

		ret.name[0] = id[1];
		ret.name[1] = id[2];
		ret.major_version = decode_digit(id[3]);
		ret.minor_version = decode_digit(id[4]);
		ret.revision_version = decode_digit(id[5]);
		ret.tag_version = decode_digit(id[6]);

		return boost::optional<fingerprint>(ret);
	}

	// checks if a peer id can possibly contain a shadow-style
	// identification
	boost::optional<fingerprint> parse_shadow_style(const peer_id& id)
	{
		fingerprint ret("..", 0, 0, 0, 0);

		if (!std::isalnum(id[0]))
			return boost::optional<fingerprint>();

		if (std::equal(id.begin()+4, id.begin()+6, "--"))
		{
			if ((id[1] < '0') || (id[2] < '0')
				|| (id[3] < '0'))
				return boost::optional<fingerprint>();
			ret.major_version = decode_digit(id[1]);
			ret.minor_version = decode_digit(id[2]);
			ret.revision_version = decode_digit(id[3]);
		}
		else
		{
			if (id[8] != 0 || id[1] > 127 || id[2] > 127 || id[3] > 127)
				return boost::optional<fingerprint>();
			ret.major_version = id[1];
			ret.minor_version = id[2];
			ret.revision_version = id[3];
		}

		ret.name[0] = id[0];
		ret.name[1] = 0;

		ret.tag_version = 0;
		return boost::optional<fingerprint>(ret);
	}

	// checks if a peer id can possibly contain a mainline-style
	// identification
	boost::optional<fingerprint> parse_mainline_style(const peer_id& id)
	{
		char ids[21];
		std::copy(id.begin(), id.end(), ids);
		ids[20] = 0;
		fingerprint ret("..", 0, 0, 0, 0);
		ret.name[1] = 0;
		ret.tag_version = 0;
		if (sscanf(ids, "%c%d-%d-%d--", &ret.name[0], &ret.major_version, &ret.minor_version
			, &ret.revision_version) != 4
			|| !std::isprint(ret.name[0]))
			return boost::optional<fingerprint>();

		return boost::optional<fingerprint>(ret);
	}

	typedef std::pair<char const*, char const*> map_entry;

	// only support BitTorrentSpecification
	// must be ordered alphabetically
	map_entry name_map[] =
	{
		map_entry("A",  "ABC")
		, map_entry("AR", "Arctic Torrent")
		, map_entry("AX", "BitPump")
		, map_entry("AZ", "Azureus")
		, map_entry("BB", "BitBuddy")
		, map_entry("BC", "BitComet")
		, map_entry("BS", "BTSlave")
		, map_entry("BX", "BittorrentX")
		, map_entry("CD", "Enhanced CTorrent")
		, map_entry("CT", "CTorrent")
		, map_entry("KT", "KTorrent")
		, map_entry("LP", "lphant")
		, map_entry("LT", "libtorrent")
		, map_entry("M",  "Mainline")
		, map_entry("MP", "MooPolice")
		, map_entry("MT", "Moonlight Torrent")
		, map_entry("O",  "Osprey Permaseed")
		, map_entry("R",  "Tribler")
		, map_entry("S",  "Shadow")
		, map_entry("SB", "Swiftbit")
		, map_entry("SN", "ShareNet")
		, map_entry("SS", "SwarmScope")
		, map_entry("SZ", "Shareaza")
		, map_entry("T",  "BitTornado")
		, map_entry("TN", "Torrent.NET")
		, map_entry("TR", "Transmission")
		, map_entry("TS", "TorrentStorm")
		, map_entry("U",  "UPnP")
		, map_entry("UT", "MicroTorrent")
		, map_entry("XT", "XanTorrent")
		, map_entry("ZT", "ZipTorrent")
		, map_entry("lt", "libTorrent (libtorrent.rakshasa.no/)")
		, map_entry("pX", "pHoeniX")
	};

	bool compare_first_string(map_entry const& lhs, map_entry const& rhs)
	{
		return lhs.first[0] < rhs.first[0]
			|| ((lhs.first[0] == rhs.first[0]) && (lhs.first[1] < rhs.first[1]));
	}

	std::string lookup(fingerprint const& f)
	{
		std::stringstream identity;

		const int size = sizeof(name_map)/sizeof(name_map[0]);
		map_entry* i =
			std::lower_bound(name_map, name_map + size
				, map_entry(f.name, ""), &compare_first_string);

#ifndef NDEBUG
		for (int i = 1; i < size; ++i)
		{
			assert(compare_first_string(name_map[i-1]
				, name_map[i]));
		}
#endif

		if (i < name_map + size && std::equal(f.name, f.name + 2, i->first))
			identity << i->second;
		else
		{
			identity << f.name[0];
			if (f.name[1] != 0) identity << f.name[1];
		}

		identity << " " << (int)f.major_version
			<< "." << (int)f.minor_version
			<< "." << (int)f.revision_version;

		if (f.name[1] != 0)
			identity << "." << (int)f.tag_version;

		return identity.str();
	}

	bool find_string(unsigned char const* id, char const* search)
	{
		return std::equal(search, search + std::strlen(search), id);
	}
}

namespace libtorrent
{

	boost::optional<fingerprint> client_fingerprint(peer_id const& p)
	{
		// look for azureus style id
		boost::optional<fingerprint> f;
		f = parse_az_style(p);
		if (f) return f;

		// look for shadow style id
		f = parse_shadow_style(p);
		if (f) return f;

		// look for mainline style id
		f = parse_mainline_style(p);
		if (f) return f;
		return f;
	}

	std::string identify_client(peer_id const& p)
	{
		peer_id::const_iterator PID = p.begin();
		boost::optional<fingerprint> f;

		if (p.is_all_zeros()) return "Unknown";

		// ----------------------
		// non standard encodings
		// ----------------------

		if (find_string(PID, "Deadman Walking-")) return "Deadman";
		if (find_string(PID + 5, "Azureus")) return "Azureus 2.0.3.2";
		if (find_string(PID, "DansClient")) return "XanTorrent";
		if (find_string(PID + 4, "btfans")) return "SimpleBT";
		if (find_string(PID, "PRC.P---")) return "Bittorrent Plus! II";
		if (find_string(PID, "P87.P---")) return "Bittorrent Plus!";
		if (find_string(PID, "S587Plus")) return "Bittorrent Plus!";
		if (find_string(PID, "martini")) return "Martini Man";
		if (find_string(PID, "Plus---")) return "Bittorrent Plus";
		if (find_string(PID, "turbobt")) return "TurboBT";
		if (find_string(PID, "a00---0")) return "Swarmy";
		if (find_string(PID, "a02---0")) return "Swarmy";
		if (find_string(PID, "T00---0")) return "Teeweety";
		if (find_string(PID, "BTDWV-")) return "Deadman Walking";
		if (find_string(PID + 2, "BS")) return "BitSpirit";
		if (find_string(PID, "btuga")) return "BTugaXP";
		if (find_string(PID, "oernu")) return "BTugaXP";
		if (find_string(PID, "Mbrst")) return "Burst!";
		if (find_string(PID, "Plus")) return "Plus!";
		if (find_string(PID, "-Qt-")) return "Qt";
		if (find_string(PID, "exbc")) return "BitComet";
		if (find_string(PID, "-G3")) return "G3 Torrent";
		if (find_string(PID, "XBT")) return "XBT";
		if (find_string(PID, "OP")) return "Opera";

		if (find_string(PID, "-BOW") && PID[7] == '-')
			return "Bits on Wheels " + std::string(PID + 4, PID + 7);

		if (find_string(PID, "eX"))
		{
			std::string user(PID + 2, PID + 14);
			return std::string("eXeem ('") + user.c_str() + "')";
		}

		if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97"))
			return "Experimental 3.2.1b2";

		if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0"))
			return "Experimental 3.1";

		// look for azureus style id
		f = parse_az_style(p);
		if (f) return lookup(*f);

		// look for shadow style id
		f = parse_shadow_style(p);
		if (f) return lookup(*f);

		// look for mainline style id
		f = parse_mainline_style(p);
		if (f) return lookup(*f);

		if (std::equal(PID, PID + 12, "\0\0\0\0\0\0\0\0\0\0\0\0"))
			return "Generic";

		std::string unknown("Unknown [");
		for (peer_id::const_iterator i = p.begin(); i != p.end(); ++i)
		{
			unknown += std::isprint(*i)?*i:'.';
		}
		unknown += "]";
		return unknown;
	}

}
<commit_msg>added electric sheep as an identified client<commit_after>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <cctype>
#include <algorithm>

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/optional.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/identify_client.hpp"
#include "libtorrent/fingerprint.hpp"

namespace
{

	using namespace libtorrent;

	int decode_digit(char c)
	{
		if (std::isdigit(c)) return c - '0';
		return unsigned(c) - 'A' + 10;
	}

	// takes a peer id and returns a valid boost::optional
	// object if the peer id matched the azureus style encoding
	// the returned fingerprint contains information about the
	// client's id
	boost::optional<fingerprint> parse_az_style(const peer_id& id)
	{
		fingerprint ret("..", 0, 0, 0, 0);

		if (id[0] != '-' || !std::isprint(id[1]) || (id[2] < '0')
			|| (id[3] < '0') || (id[4] < '0')
			|| (id[5] < '0') || (id[6] < '0')
			|| id[7] != '-')
			return boost::optional<fingerprint>();

		ret.name[0] = id[1];
		ret.name[1] = id[2];
		ret.major_version = decode_digit(id[3]);
		ret.minor_version = decode_digit(id[4]);
		ret.revision_version = decode_digit(id[5]);
		ret.tag_version = decode_digit(id[6]);

		return boost::optional<fingerprint>(ret);
	}

	// checks if a peer id can possibly contain a shadow-style
	// identification
	boost::optional<fingerprint> parse_shadow_style(const peer_id& id)
	{
		fingerprint ret("..", 0, 0, 0, 0);

		if (!std::isalnum(id[0]))
			return boost::optional<fingerprint>();

		if (std::equal(id.begin()+4, id.begin()+6, "--"))
		{
			if ((id[1] < '0') || (id[2] < '0')
				|| (id[3] < '0'))
				return boost::optional<fingerprint>();
			ret.major_version = decode_digit(id[1]);
			ret.minor_version = decode_digit(id[2]);
			ret.revision_version = decode_digit(id[3]);
		}
		else
		{
			if (id[8] != 0 || id[1] > 127 || id[2] > 127 || id[3] > 127)
				return boost::optional<fingerprint>();
			ret.major_version = id[1];
			ret.minor_version = id[2];
			ret.revision_version = id[3];
		}

		ret.name[0] = id[0];
		ret.name[1] = 0;

		ret.tag_version = 0;
		return boost::optional<fingerprint>(ret);
	}

	// checks if a peer id can possibly contain a mainline-style
	// identification
	boost::optional<fingerprint> parse_mainline_style(const peer_id& id)
	{
		char ids[21];
		std::copy(id.begin(), id.end(), ids);
		ids[20] = 0;
		fingerprint ret("..", 0, 0, 0, 0);
		ret.name[1] = 0;
		ret.tag_version = 0;
		if (sscanf(ids, "%c%d-%d-%d--", &ret.name[0], &ret.major_version, &ret.minor_version
			, &ret.revision_version) != 4
			|| !std::isprint(ret.name[0]))
			return boost::optional<fingerprint>();

		return boost::optional<fingerprint>(ret);
	}

	typedef std::pair<char const*, char const*> map_entry;

	// only support BitTorrentSpecification
	// must be ordered alphabetically
	map_entry name_map[] =
	{
		map_entry("A",  "ABC")
		, map_entry("AR", "Arctic Torrent")
		, map_entry("AX", "BitPump")
		, map_entry("AZ", "Azureus")
		, map_entry("BB", "BitBuddy")
		, map_entry("BC", "BitComet")
		, map_entry("BS", "BTSlave")
		, map_entry("BX", "BittorrentX")
		, map_entry("CD", "Enhanced CTorrent")
		, map_entry("CT", "CTorrent")
		, map_entry("ES", "electric sheep")
		, map_entry("KT", "KTorrent")
		, map_entry("LP", "lphant")
		, map_entry("LT", "libtorrent")
		, map_entry("M",  "Mainline")
		, map_entry("MP", "MooPolice")
		, map_entry("MT", "Moonlight Torrent")
		, map_entry("O",  "Osprey Permaseed")
		, map_entry("R",  "Tribler")
		, map_entry("S",  "Shadow")
		, map_entry("SB", "Swiftbit")
		, map_entry("SN", "ShareNet")
		, map_entry("SS", "SwarmScope")
		, map_entry("SZ", "Shareaza")
		, map_entry("T",  "BitTornado")
		, map_entry("TN", "Torrent.NET")
		, map_entry("TR", "Transmission")
		, map_entry("TS", "TorrentStorm")
		, map_entry("U",  "UPnP")
		, map_entry("UT", "MicroTorrent")
		, map_entry("XT", "XanTorrent")
		, map_entry("ZT", "ZipTorrent")
		, map_entry("lt", "libTorrent (libtorrent.rakshasa.no/)")
		, map_entry("pX", "pHoeniX")
	};

	bool compare_first_string(map_entry const& lhs, map_entry const& rhs)
	{
		return lhs.first[0] < rhs.first[0]
			|| ((lhs.first[0] == rhs.first[0]) && (lhs.first[1] < rhs.first[1]));
	}

	std::string lookup(fingerprint const& f)
	{
		std::stringstream identity;

		const int size = sizeof(name_map)/sizeof(name_map[0]);
		map_entry* i =
			std::lower_bound(name_map, name_map + size
				, map_entry(f.name, ""), &compare_first_string);

#ifndef NDEBUG
		for (int i = 1; i < size; ++i)
		{
			assert(compare_first_string(name_map[i-1]
				, name_map[i]));
		}
#endif

		if (i < name_map + size && std::equal(f.name, f.name + 2, i->first))
			identity << i->second;
		else
		{
			identity << f.name[0];
			if (f.name[1] != 0) identity << f.name[1];
		}

		identity << " " << (int)f.major_version
			<< "." << (int)f.minor_version
			<< "." << (int)f.revision_version;

		if (f.name[1] != 0)
			identity << "." << (int)f.tag_version;

		return identity.str();
	}

	bool find_string(unsigned char const* id, char const* search)
	{
		return std::equal(search, search + std::strlen(search), id);
	}
}

namespace libtorrent
{

	boost::optional<fingerprint> client_fingerprint(peer_id const& p)
	{
		// look for azureus style id
		boost::optional<fingerprint> f;
		f = parse_az_style(p);
		if (f) return f;

		// look for shadow style id
		f = parse_shadow_style(p);
		if (f) return f;

		// look for mainline style id
		f = parse_mainline_style(p);
		if (f) return f;
		return f;
	}

	std::string identify_client(peer_id const& p)
	{
		peer_id::const_iterator PID = p.begin();
		boost::optional<fingerprint> f;

		if (p.is_all_zeros()) return "Unknown";

		// ----------------------
		// non standard encodings
		// ----------------------

		if (find_string(PID, "Deadman Walking-")) return "Deadman";
		if (find_string(PID + 5, "Azureus")) return "Azureus 2.0.3.2";
		if (find_string(PID, "DansClient")) return "XanTorrent";
		if (find_string(PID + 4, "btfans")) return "SimpleBT";
		if (find_string(PID, "PRC.P---")) return "Bittorrent Plus! II";
		if (find_string(PID, "P87.P---")) return "Bittorrent Plus!";
		if (find_string(PID, "S587Plus")) return "Bittorrent Plus!";
		if (find_string(PID, "martini")) return "Martini Man";
		if (find_string(PID, "Plus---")) return "Bittorrent Plus";
		if (find_string(PID, "turbobt")) return "TurboBT";
		if (find_string(PID, "a00---0")) return "Swarmy";
		if (find_string(PID, "a02---0")) return "Swarmy";
		if (find_string(PID, "T00---0")) return "Teeweety";
		if (find_string(PID, "BTDWV-")) return "Deadman Walking";
		if (find_string(PID + 2, "BS")) return "BitSpirit";
		if (find_string(PID, "btuga")) return "BTugaXP";
		if (find_string(PID, "oernu")) return "BTugaXP";
		if (find_string(PID, "Mbrst")) return "Burst!";
		if (find_string(PID, "Plus")) return "Plus!";
		if (find_string(PID, "-Qt-")) return "Qt";
		if (find_string(PID, "exbc")) return "BitComet";
		if (find_string(PID, "-G3")) return "G3 Torrent";
		if (find_string(PID, "XBT")) return "XBT";
		if (find_string(PID, "OP")) return "Opera";

		if (find_string(PID, "-BOW") && PID[7] == '-')
			return "Bits on Wheels " + std::string(PID + 4, PID + 7);

		if (find_string(PID, "eX"))
		{
			std::string user(PID + 2, PID + 14);
			return std::string("eXeem ('") + user.c_str() + "')";
		}

		if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97"))
			return "Experimental 3.2.1b2";

		if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0"))
			return "Experimental 3.1";

		// look for azureus style id
		f = parse_az_style(p);
		if (f) return lookup(*f);

		// look for shadow style id
		f = parse_shadow_style(p);
		if (f) return lookup(*f);

		// look for mainline style id
		f = parse_mainline_style(p);
		if (f) return lookup(*f);

		if (std::equal(PID, PID + 12, "\0\0\0\0\0\0\0\0\0\0\0\0"))
			return "Generic";

		std::string unknown("Unknown [");
		for (peer_id::const_iterator i = p.begin(); i != p.end(); ++i)
		{
			unknown += std::isprint(*i)?*i:'.';
		}
		unknown += "]";
		return unknown;
	}

}
<|endoftext|>


<commit_before>// @(#)root/cont:$Name:  $:$Id: TClassTable.cxx,v 1.7 2001/04/21 17:21:44 rdm Exp $
// Author: Fons Rademakers   11/08/95

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// This class registers for all classes their name, id and dictionary   //
// function in a hash table. Classes are automatically added by the     //
// ctor of a special init class when a global of this init class is     //
// initialized when the program starts (see the ClassImp macro).        //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include <stdlib.h>

#include "TClassTable.h"
#include "TClass.h"
#include "TROOT.h"
#include "TMath.h"
#include "TString.h"
#include "TError.h"
#include "TRegexp.h"

TClassTable *gClassTable;

ClassRec_t **TClassTable::fgTable;
ClassRec_t **TClassTable::fgSortedTable;
int          TClassTable::fgSize;
int          TClassTable::fgTally;
Bool_t       TClassTable::fgSorted;
int          TClassTable::fgCursor;

ClassImp(TClassTable)

//______________________________________________________________________________
TClassTable::TClassTable()
{
   // TClassTable is a singleton (i.e. only one can exist per application).

   if (gClassTable)
      Error("TClassTable", "only one instance of TClassTable allowed");
   fgSize  = (int)TMath::NextPrime(1000);
   fgTable = new ClassRec_t* [fgSize];
   memset(fgTable, 0, fgSize*sizeof(ClassRec_t*));
}

//______________________________________________________________________________
TClassTable::~TClassTable()
{
   // TClassTable singleton is deleted in Terminate().
}

//______________________________________________________________________________
void TClassTable::Print(Option_t *option) const
{
   // Print the class table. Before printing the table is sorted
   // alphabetically. Only classes specified in option are listed.
   // default is to list all classes.
   // Standard wilcarding notation supported.

   if (fgTally == 0 || !fgTable)
      return;

   SortTable();

   int n = 0, ninit = 0, nl = 0;

   int nch = strlen(option);
   TRegexp re(option,kTRUE);
   Printf("");
   Printf("Defined classes");
   Printf("class                              version  bits  initialized");
   Printf("=============================================================");
   for (int i = 0; i < fgTally; i++) {
      ClassRec_t *r = fgSortedTable[i];
      n++;
      TString s = r->name;
      if (nch && strcmp(option,r->name) && s.Index(re) == kNPOS) continue;
      nl++;
      if (gROOT->GetClass(r->name, kFALSE)) {
         ninit++;
         Printf("%-32s %6d %7d       Yes", r->name, r->id, r->bits);
      } else
         Printf("%-32s %6d %7d       No",  r->name, r->id, r->bits);
   }
   Printf("-------------------------------------------------------------");
   Printf("Listed Classes: %4d  Total classes: %4d   initialized: %4d",nl, n, ninit);
   Printf("=============================================================");

   Printf("");
}

//---- static members --------------------------------------------------------

//______________________________________________________________________________
int   TClassTable::Classes() { return fgTally; }
//______________________________________________________________________________
void  TClassTable::Init() { fgCursor = 0; SortTable(); }

//______________________________________________________________________________
void TClassTable::Add(const char *cname, Version_t id, VoidFuncPtr_t dict,
                      Int_t pragmabits)
{
   // Add a class to the class table (this is a static function).

   if (!gClassTable)
      gClassTable = new TClassTable;

   // check if already in table, if so return
   ClassRec_t *r = FindElement(cname, kTRUE);
   if (r->name) {
      ::Warning("Add", "class %s allready in TClassTable", cname);
      return;
   }

   r->name = StrDup(cname);
   r->id   = id;
   r->bits = pragmabits;
   r->dict = dict;

   fgTally++;
   fgSorted = kFALSE;
}

//______________________________________________________________________________
void TClassTable::Remove(const char *cname)
{
   // Remove a class from the class table. This happens when a shared library
   // is unloaded (i.e. the dtor's of the global init objects are called).

   if (!gClassTable || !fgTable) return;

   int slot = 0;
   const char *p = cname;

   while (*p) slot = slot<<1 ^ *p++;
   if (slot < 0) slot = -slot;
   slot %= fgSize;

   ClassRec_t *r;
   ClassRec_t *prev = 0;
   for (r = fgTable[slot]; r; r = r->next) {
      if (!strcmp(r->name, cname)) {
         if (prev)
            prev->next = r->next;
         else
            fgTable[slot] = r->next;
         delete [] r->name;
         delete r;
         fgTally--;
         fgSorted = kFALSE;
         break;
      }
      prev = r;
   }
}

//______________________________________________________________________________
ClassRec_t *TClassTable::FindElement(const char *cname, Bool_t insert)
{
   // Find a class by name in the class table (using hash of name). Returns
   // 0 if the class is not in the table. Unless arguments insert is true in
   // which case a new entry is created and returned.

   if (!fgTable) return 0;

   int slot = 0;
   const char *p = cname;

   while (*p) slot = slot<<1 ^ *p++;
   if (slot < 0) slot = -slot;
   slot %= fgSize;

   ClassRec_t *r;

   for (r = fgTable[slot]; r; r = r->next)
      if (!strcmp(r->name, cname)) return r;

   if (!insert) return 0;

   r = new ClassRec_t;
   r->name = 0;
   r->id   = 0;
   r->dict = 0;
   r->next = fgTable[slot];
   fgTable[slot] = r;

   return r;
}

//______________________________________________________________________________
Version_t TClassTable::GetID(const char *cname)
{
   // Returns the ID of a class.

   ClassRec_t *r = FindElement(cname);
   if (r) return r->id;
   return -1;
}

//______________________________________________________________________________
Int_t TClassTable::GetPragmaBits(const char *cname)
{
   // Returns the pragma bits as specified in the LinkDef.h file.

   ClassRec_t *r = FindElement(cname);
   if (r) return r->bits;
   return 0;
}

//______________________________________________________________________________
VoidFuncPtr_t TClassTable::GetDict(const char *cname)
{
   // Given the class name returns the Dictionary() function of a class
   // (uses hash of name).

   ClassRec_t *r = FindElement(cname);
   if (r) return r->dict;
   return 0;
}

extern "C" {
   static int ClassComp(const void *a, const void *b);
}

//______________________________________________________________________________
static int ClassComp(const void *a, const void *b)
{
   // Function used for sorting classes alphabetically.

   return strcmp((*(ClassRec_t **)a)->name, (*(ClassRec_t **)b)->name);
}

//______________________________________________________________________________
char *TClassTable::Next()
{
    // Returns next class from sorted class table.

    if (fgCursor < fgTally) {
       ClassRec_t *r = fgSortedTable[fgCursor++];
       return r->name;
    } else
       return 0;
}

//______________________________________________________________________________
void TClassTable::PrintTable()
{
   // Print the class table. Before printing the table is sorted
   // alphabetically.

   if (fgTally == 0 || !fgTable)
      return;

   SortTable();

   int n = 0, ninit = 0;

   Printf("");
   Printf("Defined classes");
   Printf("class                              version  bits  initialized");
   Printf("=============================================================");
   for (int i = 0; i < fgTally; i++) {
      ClassRec_t *r = fgSortedTable[i];
      n++;
      if (gROOT->GetClass(r->name, kFALSE)) {
         ninit++;
         Printf("%-32s %6d %7d       Yes", r->name, r->id, r->bits);
      } else
         Printf("%-32s %6d %7d       No",  r->name, r->id, r->bits);
   }
   Printf("-------------------------------------------------------------");
   Printf("Total classes: %4d   initialized: %4d", n, ninit);
   Printf("=============================================================");

   Printf("");
}

//______________________________________________________________________________
void TClassTable::SortTable()
{
   // Sort the class table by ascending class ID's.

   if (!fgSorted) {
      if (fgSortedTable) delete [] fgSortedTable;
      fgSortedTable = new ClassRec_t* [fgTally];

      int j = 0;
      for (int i = 0; i < fgSize; i++)
         for (ClassRec_t *r = fgTable[i]; r; r = r->next)
            fgSortedTable[j++] = r;

      ::qsort(fgSortedTable, fgTally, sizeof(ClassRec_t *), ::ClassComp);
      fgSorted = kTRUE;
   }
}

//______________________________________________________________________________
void TClassTable::Terminate()
{
   // Deletes the class table (this static class function calls the dtor).

   if (gClassTable) {
      for (int i = 0; i < fgSize; i++)
         for (ClassRec_t *r = fgTable[i]; r; ) {
            ClassRec_t *t = r;
            r = r->next;
            delete [] t->name;
            delete t;
         }
      delete [] fgTable; fgTable = 0;
      SafeDelete(gClassTable);
   }
}

//______________________________________________________________________________
void AddClass(const char *cname, Version_t id, VoidFuncPtr_t dict,
              Int_t pragmabits)
{
   // Global function called by the ctor of a class's init class
   // (see the ClassImp macro).

   TClassTable::Add(cname, id, dict, pragmabits);
}

//______________________________________________________________________________
void RemoveClass(const char *cname)
{
   // Global function called by the dtor of a class's init class
   // (see the ClassImp macro).

#if 1
   // don't delete class information since it is needed by the I/O system
   // to write the StreamerInfo to file
   if (cname) { }
#else
   TClassTable::Remove(cname);
   if (gROOT && gROOT->GetListOfClasses()) {
      TClass *cl = gROOT->GetClass(cname, kFALSE);
      delete cl;  // interesting, for delete to call TClass::~TClass
   }              // TClass.h needs to be included
#endif
}
<commit_msg>correct warning message.<commit_after>// @(#)root/cont:$Name:  $:$Id: TClassTable.cxx,v 1.8 2001/06/22 16:10:17 rdm Exp $
// Author: Fons Rademakers   11/08/95

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// This class registers for all classes their name, id and dictionary   //
// function in a hash table. Classes are automatically added by the     //
// ctor of a special init class when a global of this init class is     //
// initialized when the program starts (see the ClassImp macro).        //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include <stdlib.h>

#include "TClassTable.h"
#include "TClass.h"
#include "TROOT.h"
#include "TMath.h"
#include "TString.h"
#include "TError.h"
#include "TRegexp.h"

TClassTable *gClassTable;

ClassRec_t **TClassTable::fgTable;
ClassRec_t **TClassTable::fgSortedTable;
int          TClassTable::fgSize;
int          TClassTable::fgTally;
Bool_t       TClassTable::fgSorted;
int          TClassTable::fgCursor;

ClassImp(TClassTable)

//______________________________________________________________________________
TClassTable::TClassTable()
{
   // TClassTable is a singleton (i.e. only one can exist per application).

   if (gClassTable)
      Error("TClassTable", "only one instance of TClassTable allowed");
   fgSize  = (int)TMath::NextPrime(1000);
   fgTable = new ClassRec_t* [fgSize];
   memset(fgTable, 0, fgSize*sizeof(ClassRec_t*));
}

//______________________________________________________________________________
TClassTable::~TClassTable()
{
   // TClassTable singleton is deleted in Terminate().
}

//______________________________________________________________________________
void TClassTable::Print(Option_t *option) const
{
   // Print the class table. Before printing the table is sorted
   // alphabetically. Only classes specified in option are listed.
   // default is to list all classes.
   // Standard wilcarding notation supported.

   if (fgTally == 0 || !fgTable)
      return;

   SortTable();

   int n = 0, ninit = 0, nl = 0;

   int nch = strlen(option);
   TRegexp re(option,kTRUE);
   Printf("");
   Printf("Defined classes");
   Printf("class                              version  bits  initialized");
   Printf("=============================================================");
   for (int i = 0; i < fgTally; i++) {
      ClassRec_t *r = fgSortedTable[i];
      n++;
      TString s = r->name;
      if (nch && strcmp(option,r->name) && s.Index(re) == kNPOS) continue;
      nl++;
      if (gROOT->GetClass(r->name, kFALSE)) {
         ninit++;
         Printf("%-32s %6d %7d       Yes", r->name, r->id, r->bits);
      } else
         Printf("%-32s %6d %7d       No",  r->name, r->id, r->bits);
   }
   Printf("-------------------------------------------------------------");
   Printf("Listed Classes: %4d  Total classes: %4d   initialized: %4d",nl, n, ninit);
   Printf("=============================================================");

   Printf("");
}

//---- static members --------------------------------------------------------

//______________________________________________________________________________
int   TClassTable::Classes() { return fgTally; }
//______________________________________________________________________________
void  TClassTable::Init() { fgCursor = 0; SortTable(); }

//______________________________________________________________________________
void TClassTable::Add(const char *cname, Version_t id, VoidFuncPtr_t dict,
                      Int_t pragmabits)
{
   // Add a class to the class table (this is a static function).

   if (!gClassTable)
      gClassTable = new TClassTable;

   // check if already in table, if so return
   ClassRec_t *r = FindElement(cname, kTRUE);
   if (r->name) {
      ::Warning("TClassTable::Add", "class %s allready in TClassTable", cname);
      return;
   }

   r->name = StrDup(cname);
   r->id   = id;
   r->bits = pragmabits;
   r->dict = dict;

   fgTally++;
   fgSorted = kFALSE;
}

//______________________________________________________________________________
void TClassTable::Remove(const char *cname)
{
   // Remove a class from the class table. This happens when a shared library
   // is unloaded (i.e. the dtor's of the global init objects are called).

   if (!gClassTable || !fgTable) return;

   int slot = 0;
   const char *p = cname;

   while (*p) slot = slot<<1 ^ *p++;
   if (slot < 0) slot = -slot;
   slot %= fgSize;

   ClassRec_t *r;
   ClassRec_t *prev = 0;
   for (r = fgTable[slot]; r; r = r->next) {
      if (!strcmp(r->name, cname)) {
         if (prev)
            prev->next = r->next;
         else
            fgTable[slot] = r->next;
         delete [] r->name;
         delete r;
         fgTally--;
         fgSorted = kFALSE;
         break;
      }
      prev = r;
   }
}

//______________________________________________________________________________
ClassRec_t *TClassTable::FindElement(const char *cname, Bool_t insert)
{
   // Find a class by name in the class table (using hash of name). Returns
   // 0 if the class is not in the table. Unless arguments insert is true in
   // which case a new entry is created and returned.

   if (!fgTable) return 0;

   int slot = 0;
   const char *p = cname;

   while (*p) slot = slot<<1 ^ *p++;
   if (slot < 0) slot = -slot;
   slot %= fgSize;

   ClassRec_t *r;

   for (r = fgTable[slot]; r; r = r->next)
      if (!strcmp(r->name, cname)) return r;

   if (!insert) return 0;

   r = new ClassRec_t;
   r->name = 0;
   r->id   = 0;
   r->dict = 0;
   r->next = fgTable[slot];
   fgTable[slot] = r;

   return r;
}

//______________________________________________________________________________
Version_t TClassTable::GetID(const char *cname)
{
   // Returns the ID of a class.

   ClassRec_t *r = FindElement(cname);
   if (r) return r->id;
   return -1;
}

//______________________________________________________________________________
Int_t TClassTable::GetPragmaBits(const char *cname)
{
   // Returns the pragma bits as specified in the LinkDef.h file.

   ClassRec_t *r = FindElement(cname);
   if (r) return r->bits;
   return 0;
}

//______________________________________________________________________________
VoidFuncPtr_t TClassTable::GetDict(const char *cname)
{
   // Given the class name returns the Dictionary() function of a class
   // (uses hash of name).

   ClassRec_t *r = FindElement(cname);
   if (r) return r->dict;
   return 0;
}

extern "C" {
   static int ClassComp(const void *a, const void *b);
}

//______________________________________________________________________________
static int ClassComp(const void *a, const void *b)
{
   // Function used for sorting classes alphabetically.

   return strcmp((*(ClassRec_t **)a)->name, (*(ClassRec_t **)b)->name);
}

//______________________________________________________________________________
char *TClassTable::Next()
{
    // Returns next class from sorted class table.

    if (fgCursor < fgTally) {
       ClassRec_t *r = fgSortedTable[fgCursor++];
       return r->name;
    } else
       return 0;
}

//______________________________________________________________________________
void TClassTable::PrintTable()
{
   // Print the class table. Before printing the table is sorted
   // alphabetically.

   if (fgTally == 0 || !fgTable)
      return;

   SortTable();

   int n = 0, ninit = 0;

   Printf("");
   Printf("Defined classes");
   Printf("class                              version  bits  initialized");
   Printf("=============================================================");
   for (int i = 0; i < fgTally; i++) {
      ClassRec_t *r = fgSortedTable[i];
      n++;
      if (gROOT->GetClass(r->name, kFALSE)) {
         ninit++;
         Printf("%-32s %6d %7d       Yes", r->name, r->id, r->bits);
      } else
         Printf("%-32s %6d %7d       No",  r->name, r->id, r->bits);
   }
   Printf("-------------------------------------------------------------");
   Printf("Total classes: %4d   initialized: %4d", n, ninit);
   Printf("=============================================================");

   Printf("");
}

//______________________________________________________________________________
void TClassTable::SortTable()
{
   // Sort the class table by ascending class ID's.

   if (!fgSorted) {
      if (fgSortedTable) delete [] fgSortedTable;
      fgSortedTable = new ClassRec_t* [fgTally];

      int j = 0;
      for (int i = 0; i < fgSize; i++)
         for (ClassRec_t *r = fgTable[i]; r; r = r->next)
            fgSortedTable[j++] = r;

      ::qsort(fgSortedTable, fgTally, sizeof(ClassRec_t *), ::ClassComp);
      fgSorted = kTRUE;
   }
}

//______________________________________________________________________________
void TClassTable::Terminate()
{
   // Deletes the class table (this static class function calls the dtor).

   if (gClassTable) {
      for (int i = 0; i < fgSize; i++)
         for (ClassRec_t *r = fgTable[i]; r; ) {
            ClassRec_t *t = r;
            r = r->next;
            delete [] t->name;
            delete t;
         }
      delete [] fgTable; fgTable = 0;
      SafeDelete(gClassTable);
   }
}

//______________________________________________________________________________
void AddClass(const char *cname, Version_t id, VoidFuncPtr_t dict,
              Int_t pragmabits)
{
   // Global function called by the ctor of a class's init class
   // (see the ClassImp macro).

   TClassTable::Add(cname, id, dict, pragmabits);
}

//______________________________________________________________________________
void RemoveClass(const char *cname)
{
   // Global function called by the dtor of a class's init class
   // (see the ClassImp macro).

#if 1
   // don't delete class information since it is needed by the I/O system
   // to write the StreamerInfo to file
   if (cname) { }
#else
   TClassTable::Remove(cname);
   if (gROOT && gROOT->GetListOfClasses()) {
      TClass *cl = gROOT->GetClass(cname, kFALSE);
      delete cl;  // interesting, for delete to call TClass::~TClass
   }              // TClass.h needs to be included
#endif
}
<|endoftext|>


<commit_before>/* -*- mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
// vim:sts=4:sw=4:ts=4:noet:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s

/*
 * Copyright (C) FFLAS-FFPACK
 * Written by David Lucas
 * This file is Free Software and part of FFLAS-FFPACK.
 *
 * ========LICENCE========
 * This file is part of the library FFLAS-FFPACK.
 *
 * FFLAS-FFPACK is free software: you can redistribute it and/or modify
 * it under the terms of the  GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * ========LICENCE========
 *.
 */

//--------------------------------------------------------------------------
//                        Test for minpoly
//
//--------------------------------------------------------------------------
// David Lucas
//-------------------------------------------------------------------------

#include <iomanip>
#include <iostream>

#include "fflas-ffpack/fflas-ffpack-config.h"
#include "fflas-ffpack/fflas/fflas.h"
#include "fflas-ffpack/ffpack/ffpack.h"
#include "fflas-ffpack/utils/args-parser.h"
#include "fflas-ffpack/utils/fflas_randommatrix.h"
#include "test-utils.h"
#include <givaro/modular.h>
#include <givaro/modular-balanced.h>
#include <givaro/modular-integer.h>
#include <givaro/givpoly1factor.h>
#include <givaro/givpoly1.h>

using namespace std;
using namespace FFPACK;
using Givaro::Modular;
using Givaro::ModularBalanced;

typedef Givaro::ModularBalanced<double> Field;
typedef vector<Field::Element> Polynomial;

/* Computes P(A)*V and stores it in E using Horner's scheme for
 * polynomial evaluation. */
template<typename Field, typename Element>
void horner_matrix_vector(const Field &F, size_t n, Element *A, size_t lda, Element *V,
						  Element *E, Polynomial P)
{

	//TODO: Quite a few copies. Could be improved.
	size_t deg = P.size() - 1;
	Element *E_tmp;
	E_tmp = FFLAS::fflas_new(F, 1, n);

	FFLAS::fassign(F, n, V, 1, E, 1);
	FFLAS::fscalin(F, n, P[deg], E, 1); // E <- P[deg+1] * V

    for(long i = deg; i > 0; --i)
    {
		FFLAS::fassign(F, n, E, 1, E_tmp, 1); //E_tmp <- E
		FFLAS::fgemv(F, FFLAS::FflasNoTrans, n, n, F.one, A, lda, E_tmp, 1, F.zero, E, 1);//E <- A * E_tmp
		FFLAS::faxpy(F, 1, n, P[i-1], V, 1, E, 1); //E <- minP[i-1] * V + E
    }
	FFLAS::fflas_delete(E_tmp);
}

template<typename Field, class RandIter>
bool check_minpoly(const Field &F, size_t n, RandIter& G)
{
	cout<<"Entering check_minpoly"<<endl;
	typedef typename Field::Element Element;
	size_t lda, ldv;
	Element *A, *V, *Vcst;

	//Default
	lda = n;
	ldv = n;

	/*Create variables used for testing (matrices, vectors and polynomials) */

    A = FFLAS::fflas_new(F, n, n);
    V = FFLAS::fflas_new(F, n+1, n);
	Vcst = FFLAS::fflas_new(F, 1, n);
    Polynomial minP;

    FFPACK::RandomMatrix (F, n, n, A, lda, G);

	FFPACK::NonZeroRandomMatrix(F, 1, n, V, n, G);
	FFLAS::fassign(F, n, V, 1, Vcst, 1); //MatVecMinPoly modifies V, we store it in Vcst beforehand

	FFPACK::MatVecMinPoly(F, minP, n, A, lda, V, ldv);
	FFLAS::fflas_delete(V);

	/*Check that minP is monic*/

	size_t deg = minP.size() - 1;
	if(!(minP[deg]==F.one))
		return false;

	/*Check that minP(A).V is zero*/

	Element *E;
    E = FFLAS::fflas_new(F, 1, n);

	horner_matrix_vector(F, n, A, lda, Vcst, E, minP);

    if (!FFLAS::fiszero(F, n, E, 1))
	{
		cout<<"NONZEROERROR"<<endl;
		FFLAS::fflas_delete(E);
		return false;
	}

	FFLAS::fflas_delete(E);

	/* Check minimality of minP */

	// Krylov matrix computation
	Element *K, *tmp;
	size_t ldk = n;
	K = FFLAS::fflas_new(F, deg+1, ldk);
	tmp = FFLAS::fflas_new(F, 1, n);
	FFLAS::fassign(F, n, K, 1, Vcst, 1);
	Element *Kptr = K;

	for(size_t i = 0; i < deg; ++i, Kptr += ldk)
	{
		FFLAS::fgemv(F, FFLAS::FflasNoTrans, n, n, F.one, A, lda, Kptr, 1, F.zero, tmp, 1);
		FFLAS::fassign(F, n, tmp, 1, Kptr+ldk, 1);
	}
	FFLAS::fflas_delete(tmp);

	//minP factorization
	//typedef typename Givaro::Poly1FactorDom<Field, Givaro::Dense>::Element FieldPoly;
	//vector<FieldPoly> factors;
	//vector<size_t> powers;

	//Givaro::CZfactor(factors, powers, minP);

	//factorized minP checks
	//divide minP by each factor, and evaluate it. None shall pass eval==0.
	//call horner_matrix_vector to evaluate.

	FFLAS::fflas_delete(A);
	FFLAS::fflas_delete(Vcst);
	FFLAS::fflas_delete(K);
	return true;
}

template <class Field>
bool run_with_field (Givaro::Integer q, size_t b, size_t n, size_t iters, uint64_t seed)
{
	bool ok = true;
	int nbiter = iters;

	while (ok && nbiter)
	{
		Field* F = chooseField<Field>(q, b); // F, characteristic q of b bits
		typename Field::RandIter G(*F, 0, seed); //random generator over F

		if(F == nullptr)
			return true; //if F is null, nothing to test, just pass

		cout<<"Checking with "; F->write(cout)<<endl;

		ok = ok && check_minpoly(*F, n, G);

		if(!ok)
			cout<<"FAILED"<<endl;
		else
			cout<<"PASS"<<endl;

		delete F;
		nbiter--;
	}

	return ok;
}

int main(int argc, char** argv)
{
    /* Test parameters */
	Givaro::Integer q = -1;
	size_t b = 0;
    size_t n = 128;
	size_t iters = 1;
	bool loop = false;
    uint64_t seed = time(NULL);

	Argument as[] = {
		{ 'q', "-q Q", "Set the field characteristic (-1 for random).",	TYPE_INTEGER, &q },
		{ 'b', "-b B", "Set the bitsize of the field characteristic.", TYPE_INT, &b },
		{ 'n', "-n N", "Set the order of the matrix.", TYPE_INT, &b },
		{ 'i', "-i, R", "set the number of repetitions.", TYPE_INT, &iters },
		{ 'l', "-loop Y/N", "run the test in an infinite loop.", TYPE_BOOL , &loop },
		{ 's', "-s seed", "set seed for the random generator.", TYPE_INT, &seed },
			END_OF_ARGUMENTS
		};

	FFLAS::parseArguments(argc, argv, as);

	bool ok = true;

	do
	{
		ok &= run_with_field<Modular<double>>(q,b,n,iters,seed);
		//more tests?
	} while(ok && loop);

	return !ok ;
}
<commit_msg>Working minP factorization<commit_after>/* -*- mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
// vim:sts=4:sw=4:ts=4:noet:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s

/*
 * Copyright (C) FFLAS-FFPACK
 * Written by David Lucas
 * This file is Free Software and part of FFLAS-FFPACK.
 *
 * ========LICENCE========
 * This file is part of the library FFLAS-FFPACK.
 *
 * FFLAS-FFPACK is free software: you can redistribute it and/or modify
 * it under the terms of the  GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * ========LICENCE========
 *.
 */

//--------------------------------------------------------------------------
//                        Test for minpoly
//
//--------------------------------------------------------------------------
// David Lucas
//-------------------------------------------------------------------------

#include <iomanip>
#include <iostream>

#include "fflas-ffpack/fflas-ffpack-config.h"
#include "fflas-ffpack/fflas/fflas.h"
#include "fflas-ffpack/ffpack/ffpack.h"
#include "fflas-ffpack/utils/args-parser.h"
#include "fflas-ffpack/utils/fflas_randommatrix.h"
#include "test-utils.h"
#include <givaro/modular.h>
#include <givaro/modular-balanced.h>
#include <givaro/modular-integer.h>
#include <givaro/givpoly1factor.h>
#include <givaro/givpoly1.h>

using namespace std;
using namespace FFPACK;
using Givaro::Modular;
using Givaro::ModularBalanced;

typedef Givaro::ModularBalanced<double> Field;
typedef vector<Field::Element> Polynomial;

/* Computes P(A)*V and stores it in E using Horner's scheme for
 * polynomial evaluation. */
template<typename Field, typename Element>
void horner_matrix_vector(const Field &F, size_t n, Element *A, size_t lda, Element *V,
						  Element *E, Polynomial P)
{

	//TODO: Quite a few copies. Could be improved.
	size_t deg = P.size() - 1;
	Element *E_tmp;
	E_tmp = FFLAS::fflas_new(F, 1, n);

	FFLAS::fassign(F, n, V, 1, E, 1);
	FFLAS::fscalin(F, n, P[deg], E, 1); // E <- P[deg+1] * V

    for(long i = deg; i > 0; --i)
    {
		FFLAS::fassign(F, n, E, 1, E_tmp, 1); //E_tmp <- E
		FFLAS::fgemv(F, FFLAS::FflasNoTrans, n, n, F.one, A, lda, E_tmp, 1, F.zero, E, 1);//E <- A * E_tmp
		FFLAS::faxpy(F, 1, n, P[i-1], V, 1, E, 1); //E <- minP[i-1] * V + E
    }
	FFLAS::fflas_delete(E_tmp);
}

template<typename Field, class RandIter>
bool check_minpoly(const Field &F, size_t n, RandIter& G)
{
	cout<<"Entering check_minpoly"<<endl;
	typedef typename Field::Element Element;
	size_t lda, ldv;
	Element *A, *V, *Vcst;

	//Default
	lda = n;
	ldv = n;

	/*Create variables used for testing (matrices, vectors and polynomials) */

    A = FFLAS::fflas_new(F, n, n);
    V = FFLAS::fflas_new(F, n+1, n);
	Vcst = FFLAS::fflas_new(F, 1, n);
    Polynomial minP;

    FFPACK::RandomMatrix (F, n, n, A, lda, G);

	FFPACK::NonZeroRandomMatrix(F, 1, n, V, n, G);
	FFLAS::fassign(F, n, V, 1, Vcst, 1); //MatVecMinPoly modifies V, we store it in Vcst beforehand

	FFPACK::MatVecMinPoly(F, minP, n, A, lda, V, ldv);
	FFLAS::fflas_delete(V);

	/*Check that minP is monic*/

	size_t deg = minP.size() - 1;
	if(!(minP[deg]==F.one))
		return false;

	/*Check that minP(A).V is zero*/

	Element *E;
    E = FFLAS::fflas_new(F, 1, n);

	horner_matrix_vector(F, n, A, lda, Vcst, E, minP);

    if (!FFLAS::fiszero(F, n, E, 1))
	{
		cout<<"NONZEROERROR"<<endl;
		FFLAS::fflas_delete(E);
		return false;
	}

	FFLAS::fflas_delete(E);

	/* Check minimality of minP */

	// Krylov matrix computation
	Element *K, *tmp;
	size_t ldk = n;
	K = FFLAS::fflas_new(F, deg+1, ldk);
	tmp = FFLAS::fflas_new(F, 1, n);
	FFLAS::fassign(F, n, K, 1, Vcst, 1);
	Element *Kptr = K;

	for(size_t i = 0; i < deg; ++i, Kptr += ldk)
	{
		FFLAS::fgemv(F, FFLAS::FflasNoTrans, n, n, F.one, A, lda, Kptr, 1, F.zero, tmp, 1);
		FFLAS::fassign(F, n, tmp, 1, Kptr+ldk, 1);
	}
	FFLAS::fflas_delete(tmp);

	// minP factorization
	typedef Givaro::Poly1FactorDom<Field, Givaro::Dense> PolyDom; //defines a polynomial domain for Givaro
	typedef typename PolyDom::Element FieldPoly; //defines an element over this polynomial domain (casting purposes)
	vector<FieldPoly> factors;
	vector<size_t> powers;

	PolyDom PD(F);
	FieldPoly FP = FieldPoly(minP.begin(), minP.end());
	PD.factor(factors, powers, FP);

	// Factorized minP checks
	//divide minP by each factor, and evaluate it. None shall pass eval==0.
	//call horner_matrix_vector to evaluate.

	FFLAS::fflas_delete(A);
	FFLAS::fflas_delete(Vcst);
	FFLAS::fflas_delete(K);
	return true;
}

template <class Field>
bool run_with_field (Givaro::Integer q, size_t b, size_t n, size_t iters, uint64_t seed)
{
	bool ok = true;
	int nbiter = iters;

	while (ok && nbiter)
	{
		Field* F = chooseField<Field>(q, b); // F, characteristic q of b bits
		typename Field::RandIter G(*F, 0, seed); //random generator over F

		if(F == nullptr)
			return true; //if F is null, nothing to test, just pass

		cout<<"Checking with "; F->write(cout)<<endl;

		ok = ok && check_minpoly(*F, n, G);

		if(!ok)
			cout<<"FAILED"<<endl;
		else
			cout<<"PASS"<<endl;

		delete F;
		nbiter--;
	}

	return ok;
}

int main(int argc, char** argv)
{
    /* Test parameters */
	Givaro::Integer q = -1;
	size_t b = 0;
    size_t n = 128;
	size_t iters = 1;
	bool loop = false;
    uint64_t seed = time(NULL);

	Argument as[] = {
		{ 'q', "-q Q", "Set the field characteristic (-1 for random).",	TYPE_INTEGER, &q },
		{ 'b', "-b B", "Set the bitsize of the field characteristic.", TYPE_INT, &b },
		{ 'n', "-n N", "Set the order of the matrix.", TYPE_INT, &b },
		{ 'i', "-i, R", "set the number of repetitions.", TYPE_INT, &iters },
		{ 'l', "-loop Y/N", "run the test in an infinite loop.", TYPE_BOOL , &loop },
		{ 's', "-s seed", "set seed for the random generator.", TYPE_INT, &seed },
			END_OF_ARGUMENTS
		};

	FFLAS::parseArguments(argc, argv, as);

	bool ok = true;

	do
	{
		ok &= run_with_field<Modular<double>>(q,b,n,iters,seed);
		//more tests?
	} while(ok && loop);

	return !ok ;
}
<|endoftext|>


<commit_before>//
// Created by fanyang on 7/21/2016.
//

#include "../graph.h"
#include "../utils.h"

#include <ctime>
#include <getopt.h>
#ifdef _OPENMP
#include <omp.h>
#endif

using namespace std;

int getTestTime(size_t k, double epsilon, double delta) {
    double result = 1.0;

    for (size_t i = 1; i <= k; ++i) {
        result *= k;
        result /= i;
    }

    result /= epsilon * epsilon * delta;

    return int(result);
}

struct OptionArgs {
    bool help = false;
    bool showInfo = false;
    string gFileName;
    string qFileName;

    int threadNum = -1;
    int testTimes = -1;

    double epsilon = 0.1;
    double delta = 0.1;
};

void parseOpt(int argc, char* argv[], OptionArgs* optionArgs) {
    const char optString[] = "g:q:e:d:t:p:hi";

    int opt;

    while ((opt = getopt(argc, argv, optString)) != -1) {
        switch (opt) {
            case 'g':
                optionArgs->gFileName = optarg;
                break;
            case 'q':
                optionArgs->qFileName = optarg;
                break;
            case 'e':
                optionArgs->epsilon = atof(optarg);
                break;
            case 'd':
                optionArgs->delta = atof(optarg);
                break;
            case 'h':
                optionArgs->help = true;
                break;
            case 't':
                optionArgs->testTimes = atoi(optarg);
                break;
            case 'p':
                optionArgs->threadNum = atoi(optarg);
            case 'i':
                optionArgs->showInfo = true;
                break;
            default:
                break;
        }
    }
}

void usage() {
    printf(
        "Usage\n"
        "testEgonet -g gFile -q qFile [-e epsilon] [-d delta] [-t testTimes] [-p threadNum] [-i] [-h]\n"
        "\n"
        "Do an Ego Network query.\n"
        "\n"
        "Options:\n"
        "    -g gFile        the file name of graph G\n"
        "    -q qFile        the file name of graph Q\n"
        "    -e epsilon      the value of epsilon (default is 0.1)\n"
        "    -d delta        the value of delta (default is 0.1)\n"
        "    -t testTimes    the number of test times (override -e and -d)\n"
        "    -p threadNum    the number of thread in parallel sampling (warning: more threads will cost more space)\n"
        "    -i              show query details\n"
        "    -h              print this help and exit\n"
        "\n"
    );

    exit(0);
}

int main(int argc, char** argv) {
    srand((unsigned int)time(nullptr));

    OptionArgs optionArgs;
    parseOpt(argc, argv, &optionArgs);

    if (optionArgs.help) {
        usage();
    }

    if (optionArgs.gFileName == "") {
        cerr << "Error: G file name must be given" << endl;
        return 0;
    }
    if (optionArgs.qFileName == "") {
        cerr << "Error: Q file name must be given" << endl;
        return 0;
    }
    auto pG = Graph::fromFileByNamedVertices(optionArgs.gFileName, "RealDataSet");
    auto pEgonet = Graph::fromFileByNamedVertices(optionArgs.qFileName, "GeneratedEgonet");

    if (optionArgs.showInfo) {
        pG->showGraphInfo();
        pEgonet->showGraphInfo();
    }

    if (optionArgs.testTimes == -1) {
        optionArgs.testTimes = getTestTime(pEgonet->size(), optionArgs.epsilon, optionArgs.delta);
    }

    if(optionArgs.showInfo) {
        cout << "Test times = " << optionArgs.testTimes << endl;
    }

    vector<Graph::DecomposeTree2Node> decompose;
    auto haveDecompose = pEgonet->decomposeEgonet(decompose);

    if (!haveDecompose) {
        cout << "Treewidth > 2, do not have decomposition!" << endl;
        return 0;
    }

#ifndef _OPENMP
    if (optionArgs.threadNum > 1) {
        cerr << "The environment does not support OpenMP, thread number is omitted" << endl;
    }

    mpz_class result(0);

    auto timeBefore = clock();
    if (pEgonet->isStar())
        result = pG->getSubgraphNumber_Star(*pEgonet);
    else if (pEgonet->edgeNum() == pEgonet->size())
        result = pG->getSubgraphNumber_StarAnd1Edge(*pEgonet);
    else
        result = pG->getSubgraphNumber_2Treewidth_Decompose(*pEgonet, decompose, optionArgs.testTimes);
    auto timeAfter = clock();

    cout << result << endl;

    if (optionArgs.showInfo) {
        cout << "Time: " << double(timeAfter - timeBefore) / CLOCKS_PER_SEC << "s" << endl;
    }

#else
    if (optionArgs.threadNum == -1)
        optionArgs.threadNum = max(1, omp_get_num_procs() - 2);
	if (optionArgs.showInfo) {
		cout << "Thread number = " << optionArgs.threadNum << endl;
	}

    omp_set_num_threads(optionArgs.threadNum);
    mpz_class sum(0);

    auto timeBefore = clock();

    if (pEgonet->isStar())
        sum = pEgonet->getSubgraphNumber_Star(*pEgonet);
    else if (pEgonet->edgeNum() == pEgonet->size())
        sum = pEgonet->getSubgraphNumber_StarAnd1Edge(*pEgonet);
    else {
        mpz_class* total = new mpz_class[optionArgs.testTimes];

#pragma omp parallel for default(shared)
        for (int i = 0; i < optionArgs.testTimes; ++i) {
            auto localEgonet = *pEgonet;
            auto localDecompose = decompose;
            auto localG = *pG;

            total[i] += localG.getSubgraphNumber_2Treewidth_Decompose(localEgonet, localDecompose, 1);
        }

        for (auto i = 0; i < optionArgs.testTimes; ++i)
            sum += total[i];

        sum /= optionArgs.testTimes;

        delete[] total;
    }

    auto timeAfter = clock();

    cout << sum << endl;

	if (optionArgs.showInfo) {
        cout << "Time: " << double(timeAfter - timeBefore) / CLOCKS_PER_SEC << "s" << endl;
    }
#endif

    return 0;
}
<commit_msg>A bug fix in testEgonet.cpp<commit_after>//
// Created by fanyang on 7/21/2016.
//

#include "../graph.h"
#include "../utils.h"

#include <ctime>
#include <getopt.h>
#ifdef _OPENMP
#include <omp.h>
#endif

using namespace std;

int getTestTime(size_t k, double epsilon, double delta) {
    double result = 1.0;

    for (size_t i = 1; i <= k; ++i) {
        result *= k;
        result /= i;
    }

    result /= epsilon * epsilon * delta;

    return int(result);
}

struct OptionArgs {
    bool help = false;
    bool showInfo = false;
    string gFileName;
    string qFileName;

    int threadNum = -1;
    int testTimes = -1;

    double epsilon = 0.1;
    double delta = 0.1;
};

void parseOpt(int argc, char* argv[], OptionArgs* optionArgs) {
    const char optString[] = "g:q:e:d:t:p:hi";

    int opt;

    while ((opt = getopt(argc, argv, optString)) != -1) {
        switch (opt) {
            case 'g':
                optionArgs->gFileName = optarg;
                break;
            case 'q':
                optionArgs->qFileName = optarg;
                break;
            case 'e':
                optionArgs->epsilon = atof(optarg);
                break;
            case 'd':
                optionArgs->delta = atof(optarg);
                break;
            case 'h':
                optionArgs->help = true;
                break;
            case 't':
                optionArgs->testTimes = atoi(optarg);
                break;
            case 'p':
                optionArgs->threadNum = atoi(optarg);
				break;
            case 'i':
                optionArgs->showInfo = true;
                break;
            default:
                break;
        }
    }
}

void usage() {
    printf(
        "Usage\n"
        "testEgonet -g gFile -q qFile [-e epsilon] [-d delta] [-t testTimes] [-p threadNum] [-i] [-h]\n"
        "\n"
        "Do an Ego Network query.\n"
        "\n"
        "Options:\n"
        "    -g gFile        the file name of graph G\n"
        "    -q qFile        the file name of graph Q\n"
        "    -e epsilon      the value of epsilon (default is 0.1)\n"
        "    -d delta        the value of delta (default is 0.1)\n"
        "    -t testTimes    the number of test times (override -e and -d)\n"
        "    -p threadNum    the number of thread in parallel sampling (warning: more threads will cost more space)\n"
        "    -i              show query details\n"
        "    -h              print this help and exit\n"
        "\n"
    );

    exit(0);
}

int main(int argc, char** argv) {
    srand((unsigned int)time(nullptr));

    OptionArgs optionArgs;
    parseOpt(argc, argv, &optionArgs);

    if (optionArgs.help) {
        usage();
    }

    if (optionArgs.gFileName == "") {
        cerr << "Error: G file name must be given" << endl;
        return 0;
    }
    if (optionArgs.qFileName == "") {
        cerr << "Error: Q file name must be given" << endl;
        return 0;
    }
    auto pG = Graph::fromFileByNamedVertices(optionArgs.gFileName, "RealDataSet");
    auto pEgonet = Graph::fromFileByNamedVertices(optionArgs.qFileName, "GeneratedEgonet");

    if (optionArgs.showInfo) {
        pG->showGraphInfo();
        pEgonet->showGraphInfo();
    }

    if (optionArgs.testTimes == -1) {
        optionArgs.testTimes = getTestTime(pEgonet->size(), optionArgs.epsilon, optionArgs.delta);
    }

    if(optionArgs.showInfo) {
        cout << "Test times = " << optionArgs.testTimes << endl;
    }

    vector<Graph::DecomposeTree2Node> decompose;
    auto haveDecompose = pEgonet->decomposeEgonet(decompose);

    if (!haveDecompose) {
        cout << "Treewidth > 2, do not have decomposition!" << endl;
        return 0;
    }

#ifndef _OPENMP
    if (optionArgs.threadNum > 1) {
        cerr << "The environment does not support OpenMP, thread number is omitted" << endl;
    }

    mpz_class result(0);

    auto timeBefore = clock();
    if (pEgonet->isStar())
        result = pG->getSubgraphNumber_Star(*pEgonet);
    else if (pEgonet->edgeNum() == pEgonet->size())
        result = pG->getSubgraphNumber_StarAnd1Edge(*pEgonet);
    else
        result = pG->getSubgraphNumber_2Treewidth_Decompose(*pEgonet, decompose, optionArgs.testTimes);
    auto timeAfter = clock();

    cout << result << endl;

    if (optionArgs.showInfo) {
        cout << "Time: " << double(timeAfter - timeBefore) / CLOCKS_PER_SEC << "s" << endl;
    }

#else
    if (optionArgs.threadNum == -1)
        optionArgs.threadNum = max(1, omp_get_num_procs() - 2);
	if (optionArgs.showInfo) {
		cout << "Thread number = " << optionArgs.threadNum << endl;
	}

    omp_set_num_threads(optionArgs.threadNum);
    mpz_class sum(0);

    auto timeBefore = clock();

    if (pEgonet->isStar())
        sum = pG->getSubgraphNumber_Star(*pEgonet);
    else if (pEgonet->edgeNum() == pEgonet->size())
        sum = pG->getSubgraphNumber_StarAnd1Edge(*pEgonet);
    else {
        mpz_class* total = new mpz_class[optionArgs.testTimes];

#pragma omp parallel for default(shared)
        for (int i = 0; i < optionArgs.testTimes; ++i) {
            auto localEgonet = *pEgonet;
            auto localDecompose = decompose;
            auto localG = *pG;

            total[i] += localG.getSubgraphNumber_2Treewidth_Decompose(localEgonet, localDecompose, 1);
        }

        for (auto i = 0; i < optionArgs.testTimes; ++i)
            sum += total[i];

        sum /= optionArgs.testTimes;

        delete[] total;
    }

    auto timeAfter = clock();

    cout << sum << endl;

	if (optionArgs.showInfo) {
        cout << "Time: " << double(timeAfter - timeBefore) / CLOCKS_PER_SEC << "s" << endl;
    }
#endif

    return 0;
}
<|endoftext|>