/* * mp2_print_subroutines.h * * Created by Mike Auguston on 02/05/15. * modified 12/21/17 - added layout generation * 04/06/18 - updated layout * last update - 02/21/19 */ //======================================= // this declaration uses Event_name enum // it is a single object referred when needed by containers // does not leave anything on the trace Empty_producer Dummy(Dummy_event); //======================================= // print uses event_name_string[] //----------------------------------------- void Composite_producer::harvest(){ // calls traverse() to create the segments list Traversal_result result; int my_total = 0; int min_trace = 10000000; int max_trace = 0; int marked_traces_num = 0; // to clear after root/composite traces probability_list.clear(); report_list.clear(); graph_list.clear(); AD_flag_list.clear(); do { // reset Stack, relation and probability lists to start new trace assembly Stack.clear(); Follows.clear(); Inside.clear(); Equals.clear(); clear_UDR_sets(); // clears UDR sets and attribute maps // this subroutine is generated in .cpp UDRs.clear(); // container for storage of all UDR sets // clear attribute and visual object table containers number_attr_container.clear(); bool_attr_container.clear(); interval_attr_container.clear(); time_start_attributes.clear(); time_duration_attributes.clear(); time_end_attributes.clear(); report_container.clear(); graph_container.clear(); AD_flags.clear(); Mark = 'U'; // unMARKed is the default timing_is_up_to_date = false; tails.clear(); heads.clear(); // prepare stacks for traversal predecessor.clear(); predecessor.push_back(-1); // no predecessor for the event at beginning of a segment // it will be brought by containers in add_relations_for_leader() // add a Composite event instance to the trace: name, index, and length // the length of segment will be adjusted later // current segments.size() yields the index for new segment added to segments Stack.push_back(new Composite_event_instance(name, segments.size())); // add timing attributes for this event, initialized by [0..0] time_start_attributes.push_back(Interval()); time_duration_attributes.push_back(Interval()); time_end_attributes.push_back(Interval()); Stack[0]->type = target_event; segment_probability = 1.0; // prepare to start calculating for the new segment // set trace_id pre-defined attribute value for the use in following traverse() // yields trace id for trace segments, 0 for root and composite segments trace_id_attribute = (target_event == Schema_node)? segments.size() + 1 : 0; //********* this is the main step **************** // calls this Composite_producer::traverse() //************************************************ result = traverse(); // fills the Stack and relation lists // relations are assembled/processed in the container objects predecessor.pop_back(); // restore ordering for the previous nesting level if(result == failed){ if(completeness_count == element_count) break; // there are no more options to try else continue; } // store the assembled segment, its relation tables, and MARK segments.push_back(Stack); follows_lists.push_back(Follows); inside_lists.push_back(Inside); equals_lists.push_back(Equals); probability_list.push_back(segment_probability); // assembly of UDRs and attribute tables preparing to store in segment storage assemble_UDRs(); // this subroutine is generated in .cpp // add to the segment lists UDR_lists.push_back(UDRs); float_attribute_lists.push_back(number_attr_container); bool_attribute_lists.push_back(bool_attr_container); interval_attribute_lists.push_back(interval_attr_container); start_attribute_list.push_back(time_start_attributes); duration_attribute_list.push_back(time_duration_attributes); end_attribute_list.push_back(time_end_attributes); // output view objects report_list.push_back(report_container); report_container.clear(); // prepare for the next trace graph_list.push_back(graph_container); graph_container.clear(); // prepare for the next trace AD_flag_list.push_back(AD_flags); MARK_list.push_back(Mark); if(Mark == 'M') marked_traces_num++; // do statistics: for total number of events stored <<<<<<<<<<<< int segment_len = Stack.size(); total_events += segment_len; my_total += segment_len; if(segment_len < min_trace) min_trace = segment_len; if(segment_len > max_trace) max_trace = segment_len; //show_traces();//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< } while(result != success_and_completed); if(segments.size()){ cout<<"completed "< 1) cout<<"\t\taverage "<<(double)my_total/segments.size()<< " ev/trace \tmin "<< min_trace<< " \tmax "< 0){ for(int i = 0; i < probability_list.size(); i++){ probability_list[i] /= sum; } } } else { cout<<"no traces found for "< message ; } //------------- debugging print subroutines ----------------- /* void show_map(pair_list &x){ for(multimap:: iterator q = x.begin(); q != x.end(); q++){ cout<<" ("<< q->first<<", "<second<<")\n"; } } */ //---------------------------------------------------------- void Composite_producer::show_traces(){ cout<< "\nTotal "<< segments.size()<< " traces for Composite "<< event_name_string[name] << endl; cout<<"=========================\n"; for(int k =0; k < segments.size(); k++){ cout<< "trace #"<< k+1 <<" with " << segments[k].size() << " events (marked "<print_event(); // continue with timing attributes cout<< " \tstart<<"<< start_attribute_list[k][i]<< "=>dur"<< duration_attribute_list[k][i]<< "=>end"<< end_attribute_list[k][i]<< endl; } multimap :: iterator p; cout<<"\n FOLLOWS list for trace #"<< k+1<first<< " follows "<< p->second<first<< " inside "<< p->second<first<< " equals "<< p->second< UDR_lists[k] UDR_set:: iterator p1; for(p1 = UDR_lists[k].begin(); p1 != UDR_lists[k].end(); p1++ ){ cout<< "\n "<< p1->first << " list for trace #"<< k+1<second).begin(); p != (p1->second).end(); p++){ cout<< " "<< p->first<< " \t" << p1->first << " \t" << p->second<:: iterator p = table.begin(); p != table.end(); p++){ if(!invalid[p->first] && !invalid[p->second] && !(p->first == prev_first && p->second == prev_second)){ JSON<< comma; comma = ","; JSON<< "["<< p->first<< ","<< p->second<<"]"; prev_first = p->first; prev_second = p->second; } } JSON<<"]"; // end pair list } // prototype for generated generate_AD_json_list(); void generate_AD_json_list(char & comma, AD_flag_set s); //--------------------------------------------------------- // print trace[kk] .json for view objects //--------------------------------------------------------- void print_view_object_json_list(int kk){ char comma= ' '; JSON<::iterator p1; for(p1 = report_list[kk].begin(); p1 != report_list[kk].end(); p1++){ JSON<< comma; comma = ','; (p1 -> second).print_report_json(); } // print all graphs from graph_list[kk] map::iterator p2; for(p2 = graph_list[kk].begin(); p2 != graph_list[kk].end(); p2++){ JSON<< comma; comma = ','; (p2 -> second).print_graph_json(); } generate_AD_json_list(comma, AD_flag_list[kk]); JSON<<" ]}"< 0){ // need to start new column to_column = first_available_column; }; first_available_column++; layout[current_event].fcol = to_column; layout[current_event].row = 0; available_row[to_column] = 1; latest_accepted[to_column] = layout[current_event].event_id; current_event++; // process events within this root // until next root or global SAY while( current_event < layout.size() && !is_global_event[layout[current_event].event_id]) { proceed_with_column(to_column, in_matrx, follows_matrx, len); }; return; }; // process global SAY // local SAy does not have PRECEDES with the rest, // but to prevent from being processed as concurrent // should go in the column 0 //--------------------------------------------------- if(etype == SAY_message) { if(is_global_event[layout[current_event].event_id]) { // fcol == 0 -- by default layout[current_event++].row = available_row[0]++; return; } else { // non-local SAY with some E that E FOLLOWS SAY // should be moved to the next column, other local SAY will stay where they are bool has_follower = false; for(int i = 0; i < len; i++){ if(follows_matrx[ i * len + layout[current_event].event_id]){ has_follower = true; break; } }; if(has_follower){ // add this event to layout layout[current_event].fcol = to_column + 1; layout[current_event++].row = available_row[to_column + 1]++; first_available_column++; return; } } }; // processing concurrency //----------------------------------------------------------------- // check if it is concurrent with latest_accepted[to_column] if( latest_accepted[to_column] > 0 && (etype != SAY_message) && !(in_matrx[layout[current_event].event_id * len + latest_accepted[to_column] ] || follows_matrx[layout[current_event].event_id * len + latest_accepted[to_column] ] || follows_matrx[layout[current_event].event_id + latest_accepted[to_column] * len] ) ){ // add this event to layout in the first next column that is not concurrent with it; // find first next column that is not concurrent with it while( (available_row[to_column] > 0) && !(in_matrx[layout[current_event].event_id * len + latest_accepted[to_column] ] || follows_matrx[layout[current_event].event_id * len + latest_accepted[to_column] ] || follows_matrx[layout[current_event].event_id + latest_accepted[to_column] * len] ) ){ to_column++; } if(available_row[to_column] == 0){ first_available_column++; } // add this event to layout layout[current_event].fcol = to_column; layout[current_event].row = available_row[to_column]++; latest_accepted[to_column] = layout[current_event].event_id; current_event++; // check if this concurrent event is composite if(etype == Composite_event_instance_node){ int last_composite_event = layout[current_event - 1].event_id; // process all atomic, SAY and composites within body of this composite while(current_event < layout.size() && in_matrx[layout[current_event].event_id * len + last_composite_event] ){ proceed_with_column(to_column, in_matrx, follows_matrx, len); }; }; return; } // anything else continues in the same column // add this event to layout //-------------------------------------------- layout[current_event].fcol = to_column; layout[current_event].row = available_row[to_column]++; latest_accepted[to_column] = layout[current_event].event_id; current_event++; } // end of proceed_with_column() //============================================================ void sort_and_assign_columns(vector &layout ){ // sort layout by fcol // using selection sort, which is stable // we need to preserve layout order for elements assigned the same column //----------------------------------------------------------------------- int j; // temp var int layout_len = layout.size(); layout_node t; for(int i = 1; i < layout_len; i++){ t = layout[i]; j = i - 1; while(j >= 0 && layout[j].fcol > t.fcol){ layout[j+1] = layout[j]; j--; } layout[j+1] = t; } // assign int columns following sort results // and copy new column number into fcol (scaling up 100 times) for future adjustments //--------------------------------------------------------------- int col_num = 0; // available int column number int k;// another temp var j = 0; while(j < layout_len){ layout[j].column = col_num; // if fcol's are very close, assign the same column for( k = j + 1; k < layout.size() && (layout[j].fcol == layout[k].fcol); k++){ layout[k].fcol = 100 * col_num;// multiply to support column insertion layout[k].column = col_num; } layout[j].fcol = 100 * col_num; col_num++; j = k; } } //------------------------------------------------------------- void Composite_producer::output_JSON(){ string comma, comma2; JSON<< "{\"traces\":[" << endl; comma2 = ""; for(int kk =0; kk < segments.size(); kk++){ JSON<< comma2; comma2 = ",\n"; JSON<< "\n["; // start trace // output trace's MARK, trace probability, and start event list JSON<<"\""<< MARK_list[kk]<<"\", "; JSON<:: iterator p; multimap :: iterator q = equals_lists[kk].end(); // fill eq_matrx for(p = equals_lists[kk].begin(); p != q; p++){ eq_matrx[p->first * len + p->second] = eq_matrx[p->second * len + p->first] = 1; } // initialize is_global_event for global event marking in layout calculation is_global_event.assign(len, 0); // fill in_matrx and basic_in_matrx // later on basic_in_matrix will be cleaned up from invalid events q = inside_lists[kk].end(); for(p = inside_lists[kk].begin(); p != q; p++){ basic_in_matrx[p->first * len + p->second]= in_matrx[p->first * len + p->second] = 1; // mark global SAY (and roots) if(p->second == 0) is_global_event[p-> first] = 1; } // fill follows_matrx q = follows_lists[kk].end(); for(p = follows_lists[kk].begin(); p != q; p++){ follows_matrx[p->first * len + p->second ] = basic_follows_matrx[p->first * len + p->second ]= 1; } //-------------------------------------------------------------- // transitive closure is based on Floyd-Warshall algorithm // [Cormen et al. 3rd Edition, pp.699] //-------------------------------------------------------------- // work variables to speed up the loop int tlen, ilen, ilent, ilenj, tlenj; // do eq_matrx transitive closure //-------------------------------- for(int t = 0; t < len; t++){ tlen = t * len; for(int i = 0; i < len; i++){ ilen = i * len; ilent = ilen + t; for(int j = 0; j < len; j++){ ilenj = ilen + j; tlenj = tlen + j; eq_matrx[ilenj] = eq_matrx[ilenj] || (eq_matrx[ilent] && eq_matrx[tlenj]); } } }; // merge equal event rows for(int i = 0; i < len; i++){ for(int j = i + 1; j < len; j++){ if(eq_matrx[i * len + j]){ for(int k = 0; k < len; k++){ in_matrx[i * len + k] = in_matrx[j * len + k] = in_matrx[i * len + k] || in_matrx[j * len + k]; follows_matrx[i * len + k] = follows_matrx[j * len + k] = follows_matrx[i * len + k] || follows_matrx[j * len + k]; } } } }; // do in_matrx transitive closure //-------------------------------- for(int t = 0; t < len; t++){ tlen = t * len; for(int i = 0; i < len; i++){ ilen = i * len; ilent = ilen + t; for(int j = 0; j < len; j++){ ilenj = ilen + j; tlenj = tlen + j; in_matrx[ilenj] = in_matrx[ilenj] || (in_matrx[ilent] && in_matrx[tlenj]); } } }; // propagate FOLLOWS to the inner events for(int i = 0; i < len; i++){ for(int j = 0; j < len; j++){ // distributivity axioms 9-10 if(in_matrx[i * len + j]){ for(int k = 0; k < len; k++){ follows_matrx[k * len + i] = follows_matrx[k * len + i] || follows_matrx[k * len + j]; } for(int k = 0; k < len; k++){ follows_matrx[i * len + k] = follows_matrx[i * len + k] || follows_matrx[j * len + k]; } } } } // do follows_matrx transitive closure //-------------------------------- for(int t = 0; t < len; t++){ tlen = t * len; for(int i = 0; i < len; i++){ ilen = i * len; ilent = ilen + t; for(int j = 0; j < len; j++){ ilenj = ilen + j; tlenj = tlen + j; follows_matrx[ilenj] = follows_matrx[ilenj] || (follows_matrx[ilent] && follows_matrx[tlenj]); } } }; // fill the list of invalidated events // all but earliest equal are marked by 1 for(int k = 1; k < len; k++){ if(invalid[k]) continue; for(int i = 1; i < len; i++){ if(eq_matrx[k * len + i] && k != i){ invalid[i] = 1; } } } //================================================================== // prepare for layout calculation //================================================================== layout.clear(); available_row.clear(); latest_accepted.clear(); available_row.assign(len, 0); latest_accepted.clear(); latest_accepted.assign(len, -1); layout_node t; // for adding events to the layout vector t.column = t.row = 0; t.fcol = 0; //=================================== // create layout list from the trace //=================================== for(int i = 1; i < len; i++){ if(!invalid[i]){ // add valid event to the layout list, column and row are 0 so far t.event_id = i; t.type = segments[kk][i] ->type; // event name or SAY message will be retrieved // from segments[kk][event_id] for .json file output layout.push_back(t); } } int layout_len; layout_len = layout.size(); //=================================== // layout calculation //=================================== first_available_column = 1; // column 0 reserved for global SAY current_event = 0; // to start layout traverse // initial column assignment: root events, concurrent threads, global SAY //----------------------------------------------------------------------- do{ // loop over root events and global SAY, processing concurrency proceed_with_column(first_available_column, in_matrx, follows_matrx, len); } while(current_event < layout_len); // column propagation for shared events // uses basic_in_matrx to avoid redundant IN, // which may increase weight of columns for large column numbers //--------------------------------------------------------------- bool new_change; float sum1, sum2; int count1, count3; int e1, e2; set s1; // to store fcol participating in column adjustments count3 = layout_len; // to constrain the main loop do{ // sum up columns for shared events for(int i= 0; i < layout_len; i++){ e1 = layout[i].event_id;// index in the original trace (segments[kk]) sum1= count1= 0; s1.clear(); // search for IN involving event e1 for(int j= 0; j < layout_len; j++){ e2 = layout[j].event_id;// index in the original trace (segments[kk]) // to balance the position of e1 event, // each column participating in IN counts only once if(basic_in_matrx[e1 * len + e2] && s1.find(layout[j].fcol) == s1.end() ){ // this column was not yet considered // e1 IN e2 count1++; // count1 is the number of events sharing e1 sum1 += layout[j].fcol; s1.insert(layout[j].fcol); }; }; // end for j new_change = false; // do column adjustment for shared events if( count1 > 2){ // if more than 2 shareholders sum1 /= count1; // bring the body of e1 to the new column for(int m = 0; m < layout_len; m++){ if(in_matrx[layout[m].event_id * len + e1] && (layout[i].fcol == layout[m].fcol)) layout[m].fcol = sum1; } // adjust column for i layout[i].fcol = sum1; new_change = true; continue; // continue loop for i }; } // end for i count3--; } while(new_change && count3 > 0); // sort layout by fcol using selection sort, which is stable, // to preserve layout order for elements assigned the same column; // assign int columns following sort result sort_and_assign_columns( layout ); // propagate and re-assign rows following IN and FOLLOWS // columns have been assigned and don't change //*************************************************** count1 = layout_len;// to constrain the following main loop do{ new_change = false; for(int i= 0; i < layout_len; i++){ e1 = layout[i].event_id;// index in the original trace (segments[kk]) // search for IN and FOLLOWS involving event e1 for(int j= i + 1; j < layout_len; j++){ e2 = layout[j].event_id;// index in the original trace (segments[kk]) // for IN if(basic_in_matrx[e2 * len + e1] && layout[j].row <= layout[i].row){ // if e2 IN e1 and is on smaller/equal row layout[j].row = layout[i].row + 1; new_change = true; }; if(basic_in_matrx[e1 * len + e2] && layout[i].row <= layout[j].row){ // if e1 IN e2 and is on smaller/equal row layout[i].row =layout[j].row + 1; new_change = true; }; // for FOLLOWS if(basic_follows_matrx[e2 * len + e1] && layout[j].row <= layout[i].row){ // if e2 FOLLOWS e1 and is on smaller row layout[j].row = layout[i].row ; // make it == at least if(abs(layout[i].column - layout[j].column) > 1){ // increase row to awoid crossing neighbor boxes layout[j].row++; }; new_change = true; } if(basic_follows_matrx[e1 * len + e2] && layout[i].row <= layout[j].row){ // if e1 FOLLOWS e2 and is on smaller row layout[i].row = layout[j].row; // make it == at least if(abs(layout[i].column - layout[j].column) > 1){ // increase row layout[i].row++; }; new_change = true; } }// end for j }// end for i // eliminate equal row # in the same column //========================================= for(int i= 0; i < layout_len; i++){ for(int j= i + 1; j < layout_len; j++){ // detect conflicting rows in the same column if(layout[i].column == layout[j].column){ /* if(layout[j].type == SAY_message //&& !is_global_event[layout[j].event_id] ){ // global SAY are already in column 0 // move this local SAY forward layout[j].row = max(layout[i].row, layout[j].row); new_change = true; } */ if(basic_follows_matrx[layout[i].event_id * len + layout[j].event_id] && layout[i].row <= layout[j].row){ // if i follows j, and i.row is smaller or equal to j.row, move i to j + 1's row layout[i].row = layout[j].row + 1; new_change = true; }; if(basic_follows_matrx[layout[j].event_id * len + layout[i].event_id] && layout[j].row <= layout[i].row){ // if j follows i, and j.row is smaller or equal to i.row, move i to j + 1's row layout[j].row = layout[i].row + 1; new_change = true; }; if(layout[i].row == layout[j].row){ // shift j forward layout[j].row += 1; new_change = true; } }; }// end for j }// end for i count1--; } while(new_change && count1 > 0); //================================== // output event list to .json file //================================== JSON<<"\n[ "; // start event list comma = ""; int indx; for(int i = 0; i < layout_len; i++){ JSON<< comma; comma = ","; indx = layout[i].event_id;// index in the original trace (segments[kk]) // start event 5-tuple JSON<<"[\""; if(segments[kk][indx] -> type == SAY_message) JSON<< ((SAY_event *)(segments[kk][indx])) -> message<<"\",\""; else JSON<< event_name_string[segments[kk][indx] -> name]<<"\",\""; switch(segments[kk][indx] -> type){ case Composite_event_instance_node: JSON<<'C'; break; case Atom: JSON<<'A'; break; case ROOT_node: JSON<<'R'; break; case Schema_node: JSON<<'S'; break; case SAY_message: JSON<<'T'; break; default: JSON<< "unknown event type: "<< segments[kk][i] ->type; } JSON<<"\","<< indx << "," <second).empty()) continue; JSON<<",\n["; // finish previous table and start a new one // placing relation name first JSON<<"\""<< p1->first << "\", "; print_JSON_table(p1->second, invalid); } } print_view_object_json_list(kk); JSON<<"]"; // end single trace } JSON<<"\n]"<