/* -*- mode: c; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /********************************************************************* * Clustal Omega - Multiple sequence alignment * * Copyright (C) 2010 University College Dublin * * Clustal-Omega is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This file is part of Clustal-Omega. * ********************************************************************/ /* * RCS $Id: hash-C.h 309 2016-06-13 14:10:11Z fabian $ */ // hash.C // Class for Hash data structure // * works in the same way as a hash in Perl // * keys are strings of type char* // * data elements are of type Typ // * objects have to be declared with maximal size, e.g. Hash hash1(10000) (num_slots should not be a power of 2) // * works also if maximal size is exceeded, but gets slower by a factor ~num_keys/num_slots /* * RCS $Id: hash-C.h 309 2016-06-13 14:10:11Z fabian $ */ #ifndef HASH #define HASH #ifndef MAIN #include // cin, cout, cerr #include // printf #include // exit #include // strcmp, strstr #include // sqrt, pow #include // INT_MIN #include // FLT_MIN #include // islower, isdigit etc #include // clock #include // perror() using std::cout; using std::cerr; using std::endl; using std::ios; using std::ifstream; using std::ofstream; #endif #ifndef JLIST #define JLIST #include "list.h" // List #include "list-C.h" ////////////////////////////////// DEBUG #endif #include "hash.h" //#include "new_new.h" /* memory tracking */ ////////////////////////////////////////////////////////////////////////////// ////////////////////// Methods of class Hash ///////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // Private Methods ////////////////////////////////////////////////////////////////////////////// // Constructor and Destructor of Hash ////////////////////////////////////////////////////////////////////////////// // Constructor of class Hash ////////////////////////////////////////////////////////////////////////////// template Hash::Hash() { num_keys=0; max_len=0; prev=curr=num_slots = 0; slot=NULL; } template Hash::Hash(int nslots) { num_keys=0; max_len=0; prev=curr=num_slots = nslots; slot = new Slot*[num_slots]; //Create array of num_slots slots for (int i=0; i(0); } template Hash::Hash(int nslots, Typ f) { num_keys=0; max_len=0; prev=curr=num_slots = nslots; slot = new Slot*[num_slots]; //Create array of num_slots slots for (int i=0; i Hash::~Hash() { RemoveAll(); delete[] slot; slot = NULL; } //////////////////////////////////////////////////////////////////////////////////////////// // Hash function //////////////////////////////////////////////////////////////////////////////////////////// template inline unsigned int Hash::HashValue(char* key) //returns the hash value for key { // Calculate a hash value by the division method: // Transform key into a natural number k = sum ( key[i]*128^(L-i) ) and calculate i= k % num_slots. // Since calculating k would lead to an overflow, i is calculated iteratively // and at each iteration the part divisible by num_slots is subtracted, i.e. (% num_slots is taken). if (key==NULL) {printf("Warning from hash.C: key=NULL\n"); return 0;} unsigned int i=0; // Start of iteration: k is zero char* c = key; while(*c) i = ((i<<7) + *(c++)) % num_slots; key_len = c - key; // cerr<<" Hash value for \'"< void Hash::New(int nslots, Typ f) { fail=f; RemoveAll(); delete[] slot; slot = NULL; num_keys=0; max_len=0; prev=curr=num_slots = nslots; slot = new Slot*[num_slots]; //Create array of num_slots slots for (int i=0; i Typ Hash::Show(char* key) { Slot* pslot; int i = HashValue(key); pslot = slot[i]; if (!pslot) return fail; pslot->Reset(); do{ if(!strcmp(pslot->ReadNext().key,key)) return pslot->ReadCurrent().data; } while(!pslot->End()); return fail; } //////////////////////////////////////////////////////////////////////////////////////////// // Add/replace key/data pair to hash and return address of data element //////////////////////////////////////////////////////////////////////////////////////////// template Typ* Hash::Add(char* key, Typ data) { Pair* pairp; Slot* pslot; int i = HashValue(key); pslot = slot[i]; if (!pslot) { num_keys++; KeyLen(); slot[i]=new(Slot); return slot[i]->Push(key_len,key,data);} pslot->Reset(); do { pairp = pslot->ReadNextAddress(); if(!strcmp(pairp->key,key)) { pairp->data=data; pslot->Overwrite(*pairp); return &(pairp->data); } } while(!pslot->End()); num_keys++; KeyLen(); return pslot->Push(key_len,key,data); } //////////////////////////////////////////////////////////////////////////////////////////// // Add key to hash and return address of data element. // If key exists leave data element unchanged, else set it to 'fail'. //////////////////////////////////////////////////////////////////////////////////////////// template Typ* Hash::Add(char* key) { Slot* pslot; int i = HashValue(key); pslot = slot[i]; if (!pslot) { num_keys++; KeyLen(); slot[i]=new(Slot); return slot[i]->Push(key_len,key,fail);} pslot->Reset(); do { if(!strcmp(pslot->ReadNext().key,key)) { return &((pslot->ReadCurrentAddress())->data); } } while(!pslot->End()); num_keys++; KeyLen(); return pslot->Push(key_len,key,fail); } ///////////////////////////////////////////////////////////////////////////////////////////// // Remove key from hash and return data element for key ('fail' if key does not exist) ///////////////////////////////////////////////////////////////////////////////////////////// template Typ Hash::Remove(char* key) { Slot* pslot; int i = HashValue(key); pslot = slot[i]; if (!pslot) return fail; pslot->Reset(); do { if(!strcmp(pslot->ReadNext().key,key)) { num_keys--; pslot->Delete(); // if key was the only element in pslot then delete whole list if (pslot->Size()==0) {delete pslot; pslot = NULL;slot[i]=0;} return pslot->ReadCurrent().data; } } while(!pslot->End()); return fail; } ////////////////////////////////////////////////////////////////////////////// // Remove all keys from hash; ////////////////////////////////////////////////////////////////////////////// template void Hash::RemoveAll() { for(int i=0; i Typ Hash::ReadNext() { Pair* pairp; Slot* pslot; if (curr>=num_slots) {return fail;} pslot = slot[curr]; // current list is never empty, except when current=num_slots pairp = pslot->ReadNextAddress(); if (pslot->End()) { prev=curr; do // move on to next non-empty list { if (++curr>=num_slots) return pairp->data; pslot = slot[curr]; } while (!pslot); pslot->Reset(); } return pairp->data; } ////////////////////////////////////////////////////////////////////////////// // Write next key into variable key and return data. Return 'fail' data and empty key if at end // Attention: 'key' must have memory of at least char[MaxLen()+1] allocated! ////////////////////////////////////////////////////////////////////////////// template Typ Hash::ReadNext(char* key) { Pair* pairp; Slot* pslot; if (curr>=num_slots) {*key='\0'; return fail;} pslot = slot[curr]; // current list is never empty, except when current=num_slots pairp = pslot->ReadNextAddress(); strcpy(key,pairp->key); if (pslot->End()) { prev=curr; do // move on to next non-empty list { if (++curr>=num_slots) return pairp->data; pslot = slot[curr]; } while (!pslot); pslot->Reset(); } return pairp->data; } ////////////////////////////////////////////////////////////////////////////// // Return data of current key ////////////////////////////////////////////////////////////////////////////// template Typ Hash::ReadCurrent() { Pair* pairp; Slot* pslot; curr=prev; if (curr>=num_slots) {return fail;} pslot = slot[curr]; // current list is never empty, except when current=num_slots pairp = &(pslot->ReadCurrent()); if (pslot->End()) { do // move on to next non-empty list { if (++curr>=num_slots) return pairp->data; pslot = slot[curr]; } while (!pslot); pslot->Reset(); } return pairp->data; } ////////////////////////////////////////////////////////////////////////////// // Write key last read into variable key and return data // Attention: 'key' must have memory of at least char[MaxLen()+1] allocated! ////////////////////////////////////////////////////////////////////////////// template Typ Hash::ReadCurrent(char* key) { Pair* pairp; Slot* pslot; curr=prev; if (curr>=num_slots) {*key='\0'; return fail;} pslot = slot[curr]; // current list is never empty, except when current=num_slots pairp = &(pslot->ReadCurrent()); strcpy(key,pairp->key); if (pslot->End()) { do // move on to next non-empty list { if (++curr>=num_slots) return pairp->data; pslot = slot[curr]; } while (!pslot); pslot->Reset(); } return pairp->data; } ////////////////////////////////////////////////////////////////////////////// // Remove current key, return data, and advance to next key (after Reset() remove first element) ////////////////////////////////////////////////////////////////////////////// template Typ Hash::RemoveCurrent() { Pair* pairp; Slot* pslot; curr=prev; if (curr>=num_slots) {return fail;} pslot = slot[curr]; // current list is never empty, except when current=num_slots pairp = &(pslot->Delete()); num_keys--; // if key was the only element in pslot then delete whole list if (pslot->Size()==0) {delete pslot; pslot = NULL; slot[curr]=0;} if (!pslot || pslot->End()) { do // move on to next non-empty list { if (++curr>=num_slots) {prev=curr; return pairp->data;} pslot = slot[curr]; } while (!pslot); pslot->Reset(); } prev=curr; return pairp->data; } ////////////////////////////////////////////////////////////////////////////// // Remove current key, return data, copy current key into key, and advance to next key // (After Reset() remove first element) // Attention: 'key' must have memory of at least char[MaxLen()+1] allocated! ////////////////////////////////////////////////////////////////////////////// template Typ Hash::RemoveCurrent(char* key) { Pair* pairp; Slot* pslot; curr=prev; if (curr>=num_slots) {*key='\0'; return fail;} pslot = slot[curr]; // current list is never empty, except when current=num_slots pairp = &(pslot->Delete()); strcpy(key,pairp->key); num_keys--; // if key was the only element in pslot then delete whole list if (pslot->Size()==0) {delete pslot; pslot = NULL; slot[curr]=0;} if (!pslot || pslot->End()) { do // move on to next non-empty list { if (++curr>=num_slots) {prev=curr; return pairp->data;} pslot = slot[curr]; } while (!pslot); pslot->Reset(); } prev=curr; return pairp->data; } ////////////////////////////////////////////////////////////////////////////// // Reset current position to beginning of hash ////////////////////////////////////////////////////////////////////////////// template void Hash::Reset() { curr=-1; Slot* pslot; do { curr++; if (curr>=num_slots) {prev=curr; return;} pslot = slot[curr]; } while (!pslot); pslot->Reset(); prev=curr; return; } ///////////////////////////////////////////////////////////////////////////////////////////// // Methods that return usefull information about the data stored in Hash: //////////////////////////////////////////////////////////////////////////////////////////// // Returns 1 if the hash contains key, 0 otherwise //////////////////////////////////////////////////////////////////////////////////////////// template int Hash::Contains(char* key) { Slot* pslot; int i = HashValue(key); pslot = slot[i]; if (!pslot) return 0; pslot->Reset(); do{ if(!strcmp(pslot->ReadNext().key,key)) return 1; } while(!pslot->End()); return 0; } ///////////////////////////////////////////////////////////////////////////////////////////// //print out list of keys and data ///////////////////////////////////////////////////////////////////////////////////////////// template void Hash::Print() { char key[MaxLen()+1]; cout<<"\nPrint hash:\n"; Reset(); while(!End()) cout<"< void Hash::DebugPrint() { Pair* pairp; Slot* pslot; cout<<"\n"; cout<<"Debug-print hash:"; for(int i=0; iReset(); while(!pslot->End()) { pairp = pslot->ReadNextAddress(); cout<<" "<key<<"->"<data; } } } cout<<"\n\n"; return; } #endif /* HASH */ //////////////////////////////////////////////////////////////////////////////// // Main program: test class Hash //////////////////////////////////////////////////////////////////////////////// // int main() // { // Hash ihash(5); // char* key=new char[128]; // int data; // ihash.Fail(1000); // ihash.Add("So many monsters",36); // ihash.Add("So many chickens",25); // // cerr<<"Address of ihash(\"So many monsters\") is "<"<"<"<