This document describes the MCI file format for storing Petri net unfolding prefixes. Below, CHAR is a 1-byte char, and UINT4 is a little-endian (i.e. the least significant byte is at the lowest address, and the other bytes follow in increasing order of significance.) 4-byte unsigned int (Intel is little-endian). - the total number of conditions (UINT4) - the total number of events (UINT4) - for each event (from first to last): -- the number of the original transition (UINT4) - for each condition (from first to last): -- the number of the original place (UINT4) -- the number of the preset event (UINT4); it's 0 if the condition is initial -- 0(UINT4)-terminated list of postset events numbers (UINT4) - for each cut-off event -- the number of cut-off event (UINT4) -- the number of corresponding event (UINT4); it's 0(UINT4) if the corresponding event is the virtual initial event; it's 0xFFFFFFFF (i.e ~0U) if the corresponding event is unspecified for some reason, e.g. the corresponding configuration is non-local - separating 0(UINT4) - for each configuration: -- 0(UINT4)-terminated list of events numbers. (This is used by some ancient model checker; PUNF doesn't create it and just outputs the terminating 0(UINT4).) - separating 0(UINT4) - the total number of places in the original net (UINT4) - the total number of transitions the in original net (UINT4) - the maximal string length for place/transition names (UINT4) (useful for memory allocation in the parser) - for each place in original net: -- the name (0(CHAR)-terminated string) - separating 0(CHAR) - for each transition in original net: -- the name (0(CHAR)-terminated string) - terminating 0(CHAR) The following C++ code implements an MCI parser, and might hopefully be useful as a reference: void LLMCIProcess::readMCI(const char* filename){ FILE* f=fopen(filename,"rb"); if(!f){ Error(cannot_open_file_inp,filename); exit(error_level()); } unsigned num_cond=Read4bytesNum(f,filename); unsigned num_ev=Read4bytesNum(f,filename); // the sanity check: if the provided file is not little-endian // or not an mci-file at all, fail gracefully rather than // exhaust the memory; // if any of the file access functions fail during this check, // we simply skip the sanity check; // 1. try to determine the length of the file if(fseek(f,0,SEEK_END)==0){ long file_len=ftell(f); if(file_len>=0){ // the sanity check: the file should be least 4*num_ev+12*num_cond+30 bytes long if((VLONGUINT)file_len<4*(VLONGUINT)num_ev+12*(VLONGUINT)num_cond+30){ Error(wrong_mci_file,filename); exit(error_level()); } } } // rewind the file and skip the already read info rewind(f); Read4bytesNum(f,filename); Read4bytesNum(f,filename); this->arr_pl.destroy_all(); this->arr_pl.provide(num_cond); for(unsigned i=0;iarr_pl.add(new CONDITION(i+1)); } this->arr_tr.destroy_all(); this->arr_tr.provide(num_ev); for(unsigned i=0;iarr_tr.add(new EVENT(i+1,0)); } unsigned buf; for(unsigned i=0;iarr_tr.numit;i++){ buf=Read4bytesNum(f,filename); this->arr_tr[i]->orig_node=(typename ORIGINAL_NET::TRANSITION*)buf; } for(unsigned i=0;iarr_pl.numit;i++){ buf=Read4bytesNum(f,filename); this->arr_pl[i]->orig_node=(typename ORIGINAL_NET::PLACE*)buf; buf=Read4bytesNum(f,filename); if(buf){ this->arr_pl[i]->preset=this->arr_tr[buf-1]; this->arr_tr[buf-1]->postset.add(this->arr_pl[i]); } buf=Read4bytesNum(f,filename); while(buf!=0){ this->arr_pl[i]->postset.add(this->arr_tr[buf-1]); this->arr_tr[buf-1]->preset.add(this->arr_pl[i]); buf=Read4bytesNum(f,filename); } } // sort conditions postsets for(unsigned i=0;iarr_pl.numit;i++){ this->arr_pl[i]->sort_postset(cmp_numbers); } // sort events' presets and postsets for(unsigned i=0;iarr_tr.numit;i++){ this->arr_tr[i]->sort_preset(cmp_numbers); this->arr_tr[i]->sort_postset(cmp_numbers); } buf=Read4bytesNum(f,filename); while(buf!=0){ EVENT* ev=this->arr_tr[buf-1]; buf=Read4bytesNum(f,filename); if(buf==EVENT::CorrespondingConfiguration::get_unspecified_configuration_number()){ ev->cutoff_corr.assign_unspecified(); } else if(buf==0){ ev->cutoff_corr.assign_empty(); } else{ ev->cutoff_corr.assign(this->arr_tr[buf-1]); } buf=Read4bytesNum(f,filename); } buf=Read4bytesNum(f,filename); while(buf!=0){ while(buf!=0){ buf=Read4bytesNum(f,filename); } buf=Read4bytesNum(f,filename); } if(!this->net) this->net = new ORIGINAL_NET(); buf=Read4bytesNum(f,filename); this->net->arr_pl.destroy_all(); this->net->arr_pl.provide(buf); for(unsigned i=0;inet->arr_pl.add(new typename ORIGINAL_NET::PLACE(0,i+1)); } buf=Read4bytesNum(f,filename); this->net->arr_tr.destroy_all(); this->net->arr_tr.provide(buf); for(unsigned i=0;inet->arr_tr.add(new typename ORIGINAL_NET::TRANSITION(0,i+1)); } unsigned len=Read4bytesNum(f,filename); char* str=new char[len+1]; for(unsigned i=0;inet->arr_pl.numit;i++){ ReadStr(str,f,filename); this->net->arr_pl[i]->name=_strdup(str); } ReadChar(f,filename); for(unsigned i=0;inet->arr_tr.numit;i++){ ReadStr(str,f,filename); this->net->arr_tr[i]->name=_strdup(str); } delete[] str; // fix the orig_node field of all mp-conditions and mp-events for(unsigned i=0;iarr_pl.numit;i++){ CONDITION* cond=this->arr_pl[i]; cond->orig_node=this->net->arr_pl[(unsigned)cond->orig_node-1]; } for(unsigned i=0;iarr_tr.numit;i++){ EVENT* ev=this->arr_tr[i]; ev->orig_node=this->net->arr_tr[(unsigned)ev->orig_node-1]; } for(unsigned i=0;iarr_tr.numit;i++){ EVENT* ev=this->arr_tr[i]; typename ORIGINAL_NET::TRANSITION* tr=ev->orig_node; if(tr->preset.numit || tr->postset.numit) continue; for(unsigned j=0;jpreset.numit;j++){ typename ORIGINAL_NET::PLACE* pl=ev->preset[j]->orig_node; tr->preset.add(pl); pl->postset.add(tr); } for(unsigned j=0;jpostset.numit;j++){ typename ORIGINAL_NET::PLACE* pl=ev->postset[j]->orig_node; tr->postset.add(pl); pl->preset.add(tr); } } this->net->sort_nodes(); // calculate the initial marking for(unsigned i=0;iarr_pl.numit;i++){ CONDITION* cond=this->arr_pl[i]; if(!cond->preset){ cond->orig_node->init_marking.add(new PNToken(PNToken::t_dot)); } } fclose(f); }