layer0/Tracker.cpp (1,199 lines of code) (raw):

/* A* ------------------------------------------------------------------- B* This file contains source code for the PyMOL computer program C* Copyright (c) Schrodinger, LLC. D* ------------------------------------------------------------------- E* It is unlawful to modify or remove this copyright notice. F* ------------------------------------------------------------------- G* Please see the accompanying LICENSE file for further information. H* ------------------------------------------------------------------- I* Additional authors of this source file include: -* -* -* Z* ------------------------------------------------------------------- */ #include"os_predef.h" #include"os_limits.h" #include"os_std.h" #include"Base.h" #include"MemoryDebug.h" #include"OOMac.h" #include"Tracker.h" #include"Util.h" #include"OVContext.h" #include"OVOneToOne.h" #define CAND_INFO 1 #define LIST_INFO 2 #define ITER_INFO 3 /* double-linked throughout */ typedef struct { int id; int type; int first, last; TrackerRef *ref; int length; int next, prev; } TrackerInfo; typedef struct { int cand_id, cand_index; int cand_next, cand_prev; int list_id, list_index; int list_next, list_prev; int hash_next, hash_prev; int priority; } TrackerMember; typedef struct { int id; int member; } TrackerIter; struct _CTracker { int next_id; int next_free_info; int next_free_member; int n_cand, n_list; int n_info; int n_member; int n_link; int n_iter; int cand_start; int list_start; int iter_start; TrackerInfo *info; OVOneToOne *id2info; OVOneToOne *hash2member; TrackerMember *member; }; #define TRACKER_HASH_KEY(a,b) (a^b) CTracker *TrackerNew(PyMOLGlobals * G) { OOAlloc(G, CTracker); UtilZeroMem(I, sizeof(CTracker)); I->next_id = 1; I->next_free_info = 0; I->n_info = 0; I->next_free_member = 0; I->n_member = 0; I->info = VLACalloc(TrackerInfo, 1); I->member = VLACalloc(TrackerMember, 1); I->id2info = OVOneToOne_New(G->Context->heap); I->hash2member = OVOneToOne_New(G->Context->heap); return (I); } static int GetNewInfo(CTracker * I) { int result = 0; if(!I->next_free_info) { I->n_info++; result = I->n_info; VLACheck(I->info, TrackerInfo, result); /* auto zeros -- NO ERROR CHECK */ } else { result = I->next_free_info; I->next_free_info = I->info[result].next; MemoryZero((char *) (I->info + result), (char *) (I->info + result + 1)); /* zero */ } return result; } static int GetNewMember(CTracker * I) { int result = 0; if(!(result = I->next_free_member)) { I->n_member++; result = I->n_member; VLACheck(I->member, TrackerMember, result); /* auto zeros -- NO ERROR CHECK */ } else { I->next_free_member = I->member[result].hash_next; MemoryZero((char *) (I->member + result), (char *) (I->member + result + 1)); /* zero */ } I->n_link++; return result; } static void ReleaseInfo(CTracker * I, int index) { I->info[index].next = I->next_free_info; I->next_free_info = index; } static void ReleaseMember(CTracker * I, int index) { I->member[index].hash_next = I->next_free_member; I->next_free_member = index; I->n_link--; } static int GetUniqueValidID(CTracker * I) { int result = I->next_id; while(OVreturn_IS_OK(OVOneToOne_GetForward(I->id2info, result))) { result = (result + 1) & 0x7FFFFFFF; if(!result) result = 1; } if(!(I->next_id = (result + 1) & 0x7FFFFFFF)) I->next_id = 1; return result; } static void ProtectIterators(CTracker * I, int member_index) { TrackerInfo *I_info = I->info; int iter_index; if((iter_index = I->iter_start) && member_index) { while(iter_index) { TrackerInfo *info = I_info + iter_index; if(info->first == member_index) { TrackerMember *member = I->member + member_index; switch (info->length) { case CAND_INFO: info->first = member->cand_next; break; case LIST_INFO: info->first = member->list_next; break; default: info->first = 0; break; } } else if(info->last == member_index) { TrackerMember *member = I->member + member_index; switch (info->length) { case CAND_INFO: info->last = member->cand_prev; break; case LIST_INFO: info->last = member->list_prev; break; default: info->last = 0; break; } } iter_index = info->next; } } } int TrackerNewCand(CTracker * I, TrackerRef * ref) { int result = 0; int index = GetNewInfo(I); int id; TrackerInfo *I_info = I->info; if(index) { TrackerInfo *info = I_info + index; info->ref = ref; info->next = I->cand_start; if(info->next) I_info[info->next].prev = index; I->cand_start = index; id = GetUniqueValidID(I); if(OVreturn_IS_OK(OVOneToOne_Set(I->id2info, id, index))) { info->id = (result = id); info->type = CAND_INFO; I->n_cand++; } else { ReleaseInfo(I, index); } } return result; } int TrackerNewList(CTracker * I, TrackerRef * ref) { int result = 0; int index = GetNewInfo(I); int id; TrackerInfo *I_info = I->info; if(index) { TrackerInfo *info = I_info + index; info->ref = ref; info->next = I->list_start; if(info->next) I_info[info->next].prev = index; I->list_start = index; id = GetUniqueValidID(I); if(OVreturn_IS_OK(OVOneToOne_Set(I->id2info, id, index))) { info->id = (result = id); info->type = LIST_INFO; I->n_list++; } else { ReleaseInfo(I, index); } } return result; } int TrackerNewListCopy(CTracker * I, int list_id, TrackerRef * ref) { int new_list_id = TrackerNewList(I, ref); int iter_id = TrackerNewIter(I, 0, list_id); if(iter_id) { int cand_id; while((cand_id = TrackerIterNextCandInList(I, iter_id, NULL))) { TrackerLink(I, cand_id, new_list_id, 1); } TrackerDelIter(I, iter_id); } return new_list_id; } int TrackerNewIter(CTracker * I, int cand_id, int list_id) { int result = 0; if((cand_id >= 0) || (list_id >= 0)) { int index = GetNewInfo(I); int id; TrackerInfo *I_info = I->info; if(index) { TrackerInfo *info = I_info + index; info->next = I->iter_start; if(info->next) I_info[info->next].prev = index; I->iter_start = index; id = GetUniqueValidID(I); if(OVreturn_IS_OK(OVOneToOne_Set(I->id2info, id, index))) { info->id = (result = id); info->type = ITER_INFO; I->n_iter++; if(cand_id && list_id) { /* seeking a specific member */ int hash_key = TRACKER_HASH_KEY(cand_id, list_id); OVreturn_word hash_start = OVOneToOne_GetForward(I->hash2member, hash_key); if(OVreturn_IS_OK(hash_start)) { int member_index = hash_start.word; TrackerMember *I_member = I->member; TrackerMember *member; while(member_index) { member = I_member + member_index; if((member->cand_id == cand_id) && (member->list_id == list_id)) { info->first = member_index; break; } member_index = member->hash_next; } } } else if(list_id) { /* for iterating over cands in a list */ OVreturn_word list_index = OVOneToOne_GetForward(I->id2info, list_id); if(OVreturn_IS_OK(list_index)) { TrackerInfo *list_info = I_info + list_index.word; info->first = list_info->first; } } else if(cand_id) { /* for iterating over lists in a cand */ OVreturn_word cand_index = OVOneToOne_GetForward(I->id2info, cand_id); if(OVreturn_IS_OK(cand_index)) { TrackerInfo *cand_info = I_info + cand_index.word; info->first = cand_info->first; } } } else { ReleaseInfo(I, index); } } } return result; } int TrackerDelCand(CTracker * I, int cand_id) { int result = false; if(cand_id >= 0) { OVreturn_word cand_index = OVOneToOne_GetForward(I->id2info, cand_id); TrackerInfo *I_info = I->info; if(OVreturn_IS_OK(cand_index)) { TrackerInfo *cand_info = I_info + cand_index.word; if(cand_info->type == CAND_INFO) { result = true; { /* first release all the members */ int iter_start = I->iter_start; TrackerMember *I_member = I->member; int member_index = cand_info->first; while(member_index) { TrackerMember *member = I_member + member_index; TrackerInfo *list_info = I_info + member->list_index; int cand_id = member->cand_id, list_id = member->list_id; /* if any iterators exist, then make sure they don't point at this member */ if(iter_start) { ProtectIterators(I, member_index); } { /* extract from hash chain */ int prev = member->hash_prev; int next = member->hash_next; int hash_key = TRACKER_HASH_KEY(cand_id, list_id); if(prev) { I_member[prev].hash_next = next; } else { OVOneToOne_DelForward(I->hash2member, hash_key); if(member->hash_next) { OVOneToOne_Set(I->hash2member, hash_key, member->hash_next); /* ASSUMING SUCCESS -- NOT TESTING FOR ERROR */ } } if(next) { I_member[next].hash_prev = prev; } } { /* extract from list chain */ int prev = member->list_prev; int next = member->list_next; if(prev) { I_member[prev].list_next = next; } else { list_info->first = next; } if(next) { I_member[next].list_prev = prev; } else { list_info->last = prev; } /* shorten length of the list */ list_info->length--; } { /* continue along the chain */ int member_index_copy = member_index; member_index = member->cand_next; ReleaseMember(I, member_index_copy); } } } /* delete the cand id */ OVOneToOne_DelForward(I->id2info, cand_id); /* remove the cand from the cand chain */ { int prev = cand_info->prev; int next = cand_info->next; if(prev) { I->info[prev].next = next; } else { I->cand_start = next; } if(next) { I->info[next].prev = prev; } /* shorten length of the list */ I->n_cand--; } /* and release the info record */ ReleaseInfo(I, cand_index.word); } } } return result; } int TrackerIterNextCandInList(CTracker * I, int iter_id, TrackerRef ** ref_ret) { /* returns the next cand_id in the list */ int result = 0; if(iter_id >= 0) { OVreturn_word iter_index = OVOneToOne_GetForward(I->id2info, iter_id); TrackerInfo *I_info = I->info; if(OVreturn_IS_OK(iter_index)) { TrackerInfo *iter_info = I_info + iter_index.word; int member_index; if((member_index = iter_info->first)) { TrackerMember *member = I->member + member_index; result = member->cand_id; if(ref_ret) { TrackerInfo *cand_info = I_info + member->cand_index; *ref_ret = cand_info->ref; } iter_info->last = iter_info->first; iter_info->first = member->list_next; } else if((member_index = iter_info->last)) { /* first is zero, so try last */ TrackerMember *member = I->member + member_index; if(member->list_next) { member = I->member + member->list_next; result = member->cand_id; if(ref_ret) { TrackerInfo *cand_info = I_info + member->cand_index; *ref_ret = cand_info->ref; } iter_info->last = iter_info->first; iter_info->first = member->list_next; } } iter_info->length = LIST_INFO; } } return result; } int TrackerIterNextListInCand(CTracker * I, int iter_id, TrackerRef ** ref_ret) { /* returns the next cand_id in the list */ int result = 0; if(iter_id >= 0) { OVreturn_word iter_index = OVOneToOne_GetForward(I->id2info, iter_id); TrackerInfo *I_info = I->info; if(OVreturn_IS_OK(iter_index)) { TrackerInfo *iter_info = I_info + iter_index.word; int member_index; if((member_index = iter_info->first)) { TrackerMember *member = I->member + member_index; result = member->list_id; if(ref_ret) { TrackerInfo *list_info = I_info + member->list_index; *ref_ret = list_info->ref; } iter_info->last = member_index; iter_info->first = member->cand_next; } else if((member_index = iter_info->last)) { /* first is zero, so try last */ TrackerMember *member = I->member + member_index; if(member->cand_next) { member = I->member + member->cand_next; result = member->list_id; if(ref_ret) { TrackerInfo *list_info = I_info + member->list_index; *ref_ret = list_info->ref; } iter_info->last = member_index; iter_info->first = member->cand_next; } } iter_info->length = CAND_INFO; } } return result; } int TrackerDelIter(CTracker * I, int iter_id) { int result = false; if(iter_id >= 0) { OVreturn_word iter_index = OVOneToOne_GetForward(I->id2info, iter_id); TrackerInfo *I_info = I->info; if(OVreturn_IS_OK(iter_index)) { /* remove the iter from the iter chain */ TrackerInfo *iter_info = I_info + iter_index.word; int prev = iter_info->prev; int next = iter_info->next; if(prev) { I->info[prev].next = next; } else { I->iter_start = next; } if(next) { I->info[next].prev = prev; } /* delete the iter id */ OVOneToOne_DelForward(I->id2info, iter_id); /* shorten length of the list */ I->n_iter--; result = true; ReleaseInfo(I, iter_index.word); } } return result; } int TrackerGetNList(CTracker * I) { return I->n_list; } int TrackerGetNCand(CTracker * I) { return I->n_cand; } int TrackerGetNLink(CTracker * I) { return I->n_link; } int TrackerGetNIter(CTracker * I) { return I->n_iter; } int TrackerGetCandRef(CTracker * I, int cand_id, TrackerRef ** ref_ret) { OVreturn_word cand_index = OVOneToOne_GetForward(I->id2info, cand_id); TrackerInfo *I_info = I->info; if(OVreturn_IS_OK(cand_index)) { TrackerInfo *info = I_info + cand_index.word; if(info->type == CAND_INFO) { *ref_ret = info->ref; return true; } } return false; } int TrackerGetNListForCand(CTracker * I, int cand_id) { OVreturn_word cand_index = OVOneToOne_GetForward(I->id2info, cand_id); TrackerInfo *I_info = I->info; if(OVreturn_IS_OK(cand_index)) { TrackerInfo *info = I_info + cand_index.word; if(info->type == CAND_INFO) return info->length; } return -1; } int TrackerGetNCandForList(CTracker * I, int list_id) { OVreturn_word list_index = OVOneToOne_GetForward(I->id2info, list_id); TrackerInfo *I_info = I->info; if(OVreturn_IS_OK(list_index)) { TrackerInfo *info = I_info + list_index.word; if(info->type == LIST_INFO) return info->length; } return -1; } int TrackerDelList(CTracker * I, int list_id) { int result = false; if(list_id >= 0) { OVreturn_word list_index = OVOneToOne_GetForward(I->id2info, list_id); TrackerInfo *I_info = I->info; if(OVreturn_IS_OK(list_index)) { TrackerInfo *list_info = I_info + list_index.word; if(list_info->type == LIST_INFO) { result = true; { /* first release all the members */ int iter_start = I->iter_start; TrackerMember *I_member = I->member; int member_index = list_info->first; while(member_index) { TrackerMember *member = I_member + member_index; TrackerInfo *cand_info = I_info + member->cand_index; int cand_id = member->cand_id, list_id = member->list_id; if(iter_start) { ProtectIterators(I, member_index); } { /* extract from hash chain */ int prev = member->hash_prev; int next = member->hash_next; int hash_key = TRACKER_HASH_KEY(cand_id, list_id); if(prev) { I_member[prev].hash_next = next; } else { OVOneToOne_DelForward(I->hash2member, hash_key); if(member->hash_next) { OVOneToOne_Set(I->hash2member, hash_key, member->hash_next); /* ASSUMING SUCCESS -- NOT TESTING FOR ERROR */ } } if(next) { I_member[next].hash_prev = prev; } } { /* extract from cand chain */ int prev = member->cand_prev; int next = member->cand_next; if(prev) { I_member[prev].cand_next = next; } else { cand_info->first = next; } if(next) { I_member[next].cand_prev = prev; } else { cand_info->last = prev; } /* shorten length of the list */ cand_info->length--; } /* if any iterators exist, then make sure they don't point at this member */ { /* continue along the chain */ int member_index_copy = member_index; member_index = member->list_next; ReleaseMember(I, member_index_copy); } } } /* delete the list id */ OVOneToOne_DelForward(I->id2info, list_id); /* remove the list from the list chain */ { int prev = list_info->prev; int next = list_info->next; if(prev) { I->info[prev].next = next; } else { I->list_start = next; } if(next) { I->info[next].prev = prev; } /* shorten length of the list */ I->n_list--; } /* and release the info record */ ReleaseInfo(I, list_index.word); } } } return result; } int TrackerLink(CTracker * I, int cand_id, int list_id, int priority) { int result = false; int hash_key = TRACKER_HASH_KEY(cand_id, list_id); int already_linked = false; OVreturn_word hash_start = OVOneToOne_GetForward(I->hash2member, hash_key); { if(OVreturn_IS_OK(hash_start)) { int member_index = hash_start.word; TrackerMember *I_member = I->member; TrackerMember *member; while(member_index) { member = I_member + member_index; if((member->cand_id == cand_id) && (member->list_id == list_id)) { already_linked = true; break; } member_index = member->hash_next; } } else { hash_start.word = 0; /* will be first entry in the hash chain */ } } if(!already_linked) { OVreturn_word cand_index = OVOneToOne_GetForward(I->id2info, cand_id); OVreturn_word list_index = OVOneToOne_GetForward(I->id2info, list_id); if(OVreturn_IS_OK(cand_index) && OVreturn_IS_OK(list_index)) { TrackerInfo *I_info = I->info; TrackerInfo *cand_info = I_info + cand_index.word; TrackerInfo *list_info = I_info + list_index.word; if(!already_linked) { int member_index = GetNewMember(I); if(member_index) { if(!hash_start.word) { /* not a */ if(OVreturn_IS_OK(OVOneToOne_Set(I->hash2member, hash_key, member_index))) hash_start.word = member_index; } if(!hash_start.word) { ReleaseMember(I, member_index); } else { /* cannot fail now */ TrackerMember *I_member = I->member; TrackerMember *member = I_member + member_index; result = true; cand_info->length++; list_info->length++; member = I->member + member_index; member->priority = priority; member->cand_id = cand_id; member->cand_index = cand_index.word; member->list_id = list_id; member->list_index = list_index.word; /* insert into the hash chain at start in second spot */ if(hash_start.word != member_index) { /* in the second spot */ member->hash_prev = hash_start.word; member->hash_next = I_member[hash_start.word].hash_next; I_member[hash_start.word].hash_next = member_index; if(member->hash_next) { I_member[member->hash_next].hash_prev = member_index; } } /* else, member is sole member of the hash chain, so no links are required */ /* insert at end of cand chain */ member->cand_prev = cand_info->last; cand_info->last = member_index; if(member->cand_prev) { I_member[member->cand_prev].cand_next = member_index; } else { cand_info->first = member_index; } /* insert at end of list chain */ member->list_prev = list_info->last; list_info->last = member_index; if(member->list_prev) { I_member[member->list_prev].list_next = member_index; } else { list_info->first = member_index; } } } } } } return result; } int TrackerUnlink(CTracker * I, int cand_id, int list_id) { int result = false; int hash_key = TRACKER_HASH_KEY(cand_id, list_id); int already_linked = false; OVreturn_word hash_start = OVOneToOne_GetForward(I->hash2member, hash_key); TrackerMember *I_member = I->member; TrackerMember *member = NULL; int member_index = hash_start.word; { if(OVreturn_IS_OK(hash_start)) { while(member_index) { member = I_member + member_index; if((member->cand_id == cand_id) && (member->list_id == list_id)) { already_linked = true; break; } member_index = member->hash_next; } } } if(already_linked) { /* member and member_index will be valid */ TrackerInfo *I_info = I->info; TrackerInfo *cand_info = I_info + member->cand_index; TrackerInfo *list_info = I_info + member->list_index; result = true; /* if any iterators exist, then make sure they don't point at this member */ if(I->iter_start) { ProtectIterators(I, member_index); } { /* extract from hash chain */ int prev = member->hash_prev; int next = member->hash_next; if(prev) { I_member[prev].hash_next = next; } else { OVOneToOne_DelForward(I->hash2member, hash_key); if(member->hash_next) { OVOneToOne_Set(I->hash2member, hash_key, member->hash_next); /* ASSUMING SUCCESS -- NOT TESTING FOR ERROR */ } } if(next) { I_member[next].hash_prev = prev; } } { /* extract from cand chain */ int prev = member->cand_prev; int next = member->cand_next; if(prev) { I_member[prev].cand_next = next; } else { cand_info->first = next; } if(next) { I_member[next].cand_prev = prev; } else { cand_info->last = prev; } /* shorten length of the list */ cand_info->length--; } { /* extract from list chain */ int prev = member->list_prev; int next = member->list_next; if(prev) { I_member[prev].list_next = next; } else { list_info->first = next; } if(next) { I_member[next].list_prev = prev; } else { list_info->last = prev; } /* shorten length of the list */ list_info->length--; } /* release the member for reuse */ ReleaseMember(I, member_index); } return result; } void TrackerFree(CTracker * I) { VLAFreeP(I->info); VLAFreeP(I->member); if(I->id2info) OVOneToOne_Del(I->id2info); if(I->hash2member) OVOneToOne_Del(I->hash2member); OOFreeP(I); } #ifdef TRACKER_UNIT_TEST #include"OVRandom.h" #define N_ID 100 int TrackerUnitTest(PyMOLGlobals * G) { int result = true; int cand_id[N_ID], list_id[N_ID], iter_id[N_ID], iter_start[N_ID]; int a; int tmp_int; OVRandom *ov_rand = OVRandom_NewBySeed(G->Context->heap, 12345678); CTracker *I = TrackerNew(G); for(a = 0; a < N_ID; a++) { iter_id[a] = 0; } /* first test simple new/del */ for(a = 0; a < N_ID; a++) { cand_id[a] = TrackerNewCand(I, NULL); if(!cand_id[a]) fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d==0\n", __LINE__, cand_id[a]); } for(a = 0; a < N_ID; a++) { list_id[a] = TrackerNewList(I, NULL); if(!list_id[a]) fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d==0\n", __LINE__, list_id[a]); } for(a = 0; a < N_ID; a++) { if(cand_id[a]) { if(TrackerDelCand(I, cand_id[a])) cand_id[a] = 0; else fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d\n", __LINE__); } if(list_id[a]) { if(TrackerDelList(I, list_id[a])) list_id[a] = 0; else fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d\n", __LINE__); } } if((tmp_int = TrackerGetNList(I))) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=0\n", __LINE__, tmp_int); } if((tmp_int = TrackerGetNCand(I))) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=0\n", __LINE__, tmp_int); } for(a = 0; a < N_ID; a++) { cand_id[a] = TrackerNewCand(I, NULL); list_id[a] = TrackerNewList(I, NULL); } /* test simple serial linking and unlinking */ { for(a = 0; a < N_ID; a++) { if(!TrackerLink(I, cand_id[a], list_id[a], 1)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; a=%d \n", __LINE__, a); } if(!TrackerUnlink(I, cand_id[a], list_id[a])) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; a=%d\n", __LINE__, a); } } for(a = 0; a < N_ID; a++) { if(!TrackerLink(I, cand_id[a], list_id[a], 1)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; a=%d\n", __LINE__, a); } } if(N_ID != TrackerGetNLink(I)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=%d\n", __LINE__, N_ID, TrackerGetNLink(I)); } for(a = 0; a < N_ID; a++) { if(!TrackerUnlink(I, cand_id[a], list_id[a])) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; a=%d\n", __LINE__, a); } } } /* next test random linking and unlinking, followed by systematic unlinking */ { int n_link = 0; int list_idx, cand_idx; int b; for(a = 0; a < (N_ID * N_ID); a++) { cand_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); list_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); if(TrackerLink(I, cand_id[cand_idx], list_id[list_idx], 0)) n_link++; } if(n_link != TrackerGetNLink(I)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=%d\n", __LINE__, n_link, TrackerGetNLink(I)); } for(a = 0; a < (N_ID * N_ID); a++) { cand_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); list_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); if(TrackerUnlink(I, cand_id[cand_idx], list_id[list_idx])) n_link--; } if(n_link != TrackerGetNLink(I)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=%d\n", __LINE__, n_link, TrackerGetNLink(I)); } for(a = 0; a < N_ID; a++) { for(b = 0; b < N_ID; b++) { if(TrackerUnlink(I, cand_id[a], list_id[b])) n_link--; } } if(n_link != TrackerGetNLink(I)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=%d\n", __LINE__, n_link, TrackerGetNLink(I)); } if(n_link != 0) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; 0!=%d\n", __LINE__, n_link); } } /* next test random linking and list deletion */ { int n_link = 0; int list_idx, cand_idx; for(a = 0; a < (N_ID * N_ID); a++) { cand_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); list_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); if(TrackerLink(I, cand_id[cand_idx], list_id[list_idx], 0)) n_link++; } if(n_link != TrackerGetNLink(I)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=%d\n", __LINE__, n_link, TrackerGetNLink(I)); } } for(a = 0; a < N_ID; a++) { if(cand_id[a]) { if(TrackerDelCand(I, cand_id[a])) cand_id[a] = 0; else fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d\n\n", __LINE__); } if(list_id[a]) { if(TrackerDelList(I, list_id[a])) list_id[a] = 0; else fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d\n\n", __LINE__); } } if(TrackerGetNLink(I)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; 0!=%d\n", __LINE__, TrackerGetNLink(I)); } /* make sure everyone was deleted */ for(a = 0; a < N_ID; a++) { if(cand_id[a]) fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=0\n", __LINE__, cand_id[a]); if(list_id[a]) fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=0\n", __LINE__, list_id[a]); } /* next test random linking and unlinking and list deletion */ { int n_link = 0; int list_idx, cand_idx; int b; for(a = 0; a < N_ID; a++) { for(b = 0; b < N_ID; b++) { cand_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); list_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); if(!cand_id[cand_idx]) { cand_id[cand_idx] = TrackerNewCand(I, NULL); } if(!list_id[list_idx]) { list_id[list_idx] = TrackerNewList(I, NULL); } if(cand_id[cand_idx] && list_id[list_idx]) { if(TrackerLink(I, cand_id[cand_idx], list_id[list_idx], 0)) n_link++; } } cand_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); list_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); if(cand_id[cand_idx] && list_id[list_idx]) { if(TrackerUnlink(I, cand_id[cand_idx], list_id[list_idx])) n_link--; } cand_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); list_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); if(cand_id[cand_idx]) { int len = TrackerGetNListForCand(I, cand_id[cand_idx]); if(len >= 0) { if(TrackerDelCand(I, cand_id[cand_idx])) { cand_id[cand_idx] = 0; n_link -= len; } else fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d\n\n", __LINE__); } } if(list_id[list_idx]) { int len = TrackerGetNCandForList(I, list_id[list_idx]); if(len >= 0) { if(TrackerDelList(I, list_id[list_idx])) { list_id[list_idx] = 0; n_link -= len; } else fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d\n\n", __LINE__); } } } if(n_link != TrackerGetNLink(I)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=%d\n", __LINE__, n_link, TrackerGetNLink(I)); } } for(a = 0; a < N_ID; a++) { if(cand_id[a]) { if(TrackerDelCand(I, cand_id[a])) cand_id[a] = 0; else fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d\n\n", __LINE__); } if(list_id[a]) { if(TrackerDelList(I, list_id[a])) list_id[a] = 0; else fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d\n\n", __LINE__); } } if(TrackerGetNLink(I)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; 0!=%d\n", __LINE__, TrackerGetNLink(I)); } /* make sure everyone was deleted */ for(a = 0; a < N_ID; a++) { if(cand_id[a]) fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=0\n", __LINE__, cand_id[a]); if(list_id[a]) fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=0\n", __LINE__, list_id[a]); } /* okay, now let's mix in some iterators... */ for(a = 0; a < N_ID; a++) { cand_id[a] = TrackerNewCand(I, NULL); list_id[a] = TrackerNewList(I, NULL); } if(TrackerGetNCand(I) != N_ID) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=%d\n", __LINE__, N_ID, TrackerGetNCand(I)); } if(TrackerGetNList(I) != N_ID) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=%d\n", __LINE__, N_ID, TrackerGetNList(I)); } { int n_link = 0; int list_idx, cand_idx; for(a = 0; a < (N_ID * N_ID); a++) { cand_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); list_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); if(TrackerLink(I, cand_id[cand_idx], list_id[list_idx], 0)) n_link++; } if(n_link != TrackerGetNLink(I)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=%d\n", __LINE__, n_link, TrackerGetNLink(I)); } } /* do iters iterate over the expected number of members? */ { int len; int list_idx, cand_idx; for(a = 0; a < N_ID; a++) { cand_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); if(!(iter_id[a] = TrackerNewIter(I, cand_id[cand_idx], 0))) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; 0==%d\n", __LINE__, iter_id[a]); } len = 0; while(TrackerIterNextListInCand(I, iter_id[a], NULL)) len++; if(len != TrackerGetNListForCand(I, cand_id[cand_idx])) fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=%d\n", __LINE__, len, TrackerGetNListForCand(I, cand_id[cand_idx])); if(TrackerDelIter(I, iter_id[a])) { iter_id[a] = 0; } else { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; \n", __LINE__); } } for(a = 0; a < N_ID; a++) { list_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); if(!(iter_id[a] = TrackerNewIter(I, list_id[list_idx], 0))) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; 0==%d\n", __LINE__, iter_id[a]); } len = 0; while(TrackerIterNextCandInList(I, iter_id[a], NULL)) len++; if(len != TrackerGetNCandForList(I, list_id[list_idx])) fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=%d\n", __LINE__, len, TrackerGetNCandForList(I, list_id[list_idx])); if(TrackerDelIter(I, iter_id[a])) { iter_id[a] = 0; } else { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; \n", __LINE__); } } } /* are iterators robust to member deletion? */ { int list_idx, cand_idx; int b; for(a = 0; a < N_ID; a++) { cand_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); if(!(iter_id[a] = TrackerNewIter(I, cand_id[cand_idx], 0))) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; 0==%d\n", __LINE__, iter_id[a]); } } { int cnt = 0; const int expected_cnt = 5341; /* THIS TEST IS FRAGILE -- result depends on N_ID, random seem, tests that have been run above and of course, the iterator recovery behavior */ for(a = 0; a < N_ID; a++) { cand_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); list_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); if(cand_id[cand_idx]) { if(TrackerDelCand(I, cand_id[cand_idx])) cand_id[cand_idx] = 0; else fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d\n\n", __LINE__); } if(list_id[list_idx]) { if(TrackerDelList(I, list_id[list_idx])) list_id[list_idx] = 0; else fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d\n\n", __LINE__); } for(b = 0; b < N_ID; b++) { if(TrackerIterNextListInCand(I, iter_id[b], NULL)) cnt++; } } if(cnt != expected_cnt) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=%d\n", __LINE__, expected_cnt, cnt); } } } if(TrackerGetNIter(I) != N_ID) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=%d\n", __LINE__, N_ID, TrackerGetNIter(I)); } /* delete iters */ for(a = 0; a < N_ID; a++) { if(iter_id[a]) { if(TrackerDelIter(I, iter_id[a])) { iter_id[a] = 0; } else { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; \n", __LINE__); } } } /* recreate cand, lists, links */ for(a = 0; a < N_ID; a++) { if(!cand_id[a]) cand_id[a] = TrackerNewCand(I, NULL); if(!list_id[a]) list_id[a] = TrackerNewList(I, NULL); } { int n_link = 0; int list_idx, cand_idx; for(a = 0; a < (N_ID * N_ID); a++) { cand_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); list_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); if(TrackerLink(I, cand_id[cand_idx], list_id[list_idx], 0)) n_link++; } } /* do iters iterate over the expected number of members? */ { int len; int list_idx, cand_idx; int diff; for(a = 0; a < N_ID; a++) { cand_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); iter_start[a] = cand_id[cand_idx]; if(!(iter_id[a] = TrackerNewIter(I, cand_id[cand_idx], 0))) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; 0==%d\n", __LINE__, iter_id[a]); } TrackerIterNextListInCand(I, iter_id[a], NULL); } for(a = 0; a < N_ID; a++) { list_idx = (int) (N_ID * OVRandom_Get_float64_exc1(ov_rand)); if(list_id[list_idx]) { if(TrackerDelList(I, list_id[list_idx])) list_id[list_idx] = 0; } } for(a = 0; a < N_ID; a++) { len = 1; while(TrackerIterNextListInCand(I, iter_id[a], NULL)) len++; diff = abs(len - TrackerGetNListForCand(I, iter_start[a])); /* shouldn't vary by more than 1 */ if(diff > 1) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d>1\n", __LINE__, diff); } if(TrackerDelIter(I, iter_id[a])) { iter_id[a] = 0; } else { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; \n", __LINE__); } } } /* delete everyone */ for(a = 0; a < N_ID; a++) { if(cand_id[a]) { if(TrackerDelCand(I, cand_id[a])) cand_id[a] = 0; else fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d\n\n", __LINE__); } if(list_id[a]) { if(TrackerDelList(I, list_id[a])) list_id[a] = 0; else fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d\n\n", __LINE__); } if(iter_id[a]) { if(TrackerDelIter(I, iter_id[a])) { iter_id[a] = 0; } else { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; \n", __LINE__); } } } if(TrackerGetNLink(I)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; 0!=%d\n", __LINE__, TrackerGetNLink(I)); } if(TrackerGetNCand(I)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; 0!=%d\n", __LINE__, TrackerGetNLink(I)); } if(TrackerGetNList(I)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; 0!=%d\n", __LINE__, TrackerGetNList(I)); } if(TrackerGetNIter(I)) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; 0!=%d\n", __LINE__, TrackerGetNIter(I)); } /* make sure everyone was deleted */ for(a = 0; a < N_ID; a++) { if(cand_id[a]) fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=0\n", __LINE__, cand_id[a]); if(list_id[a]) fprintf(stderr, "TRACKER_UNIT_TEST FAILED AT LINE %d; %d!=0\n", __LINE__, list_id[a]); } OVRandom_Del(ov_rand); TrackerFree(I); if(!result) { fprintf(stderr, "TRACKER_UNIT_TEST FAILED -- EXITING\n"); exit(0); } return result; } #endif