diff --git a/APP_Framework/Applications/app_test/test_hash/CompileSuccess.png b/APP_Framework/Applications/app_test/test_hash/CompileSuccess.png index 04caae4d1..77a0f2e1c 100644 Binary files a/APP_Framework/Applications/app_test/test_hash/CompileSuccess.png and b/APP_Framework/Applications/app_test/test_hash/CompileSuccess.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/ConfigOpen.png b/APP_Framework/Applications/app_test/test_hash/ConfigOpen.png index b62acec61..249c741d7 100644 Binary files a/APP_Framework/Applications/app_test/test_hash/ConfigOpen.png and b/APP_Framework/Applications/app_test/test_hash/ConfigOpen.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/README.md b/APP_Framework/Applications/app_test/test_hash/README.md index 876cd5d08..e83e75d82 100644 --- a/APP_Framework/Applications/app_test/test_hash/README.md +++ b/APP_Framework/Applications/app_test/test_hash/README.md @@ -1,16 +1,16 @@ -# 基于cortex-m3-emulator实现哈希表并测试验证## +# 基于cortex-m3-emulator实现哈希表并测试验证 ## 1. 简介 -利用c语言实现了哈希表(HashMap),包括添加键值对(Put),获取键对应的值(Get), 删除健(Delete),清空哈希表(Clear), 迭代遍历哈希表(hashMapIterator)等功能 -操作。 +利用 c 语言实现了哈希表(HashMap),包括添加键值对(Put),获取键对应的值(Get), 删除健(Delete),清空哈希表(Clear), 迭代遍历哈希表(hashMapIterator)等功能操作。 -利用数组(Entry)作为存储空间,利用链表(*next)解决冲突。当哈希表的大小超过数组大小后,为避免发生冲突过多的情况,可以对哈希表扩容。 +利用数组(Entry)作为存储空间,使用 DJB 哈希算法,利用线性探测法解决冲突,对哈希表中的数据进行逻辑删除。当哈希表的大小超过数组大小后,为避免发生冲突过多的情况,可以对哈希表扩容。当哈希表的大小小于数组大小的一半时,会释放一半的内存。 ## 2. 数据结构设计说明 键值对结构 typedef struct entry { void * key; // 键 void * value; // 值 + Boolean isDelete; // 逻辑删除 struct entry * next; // 冲突链表 }*Entry; @@ -26,11 +26,12 @@ typedef struct hashMap { Remove remove; // 删除键 Clear clear; // 清空Map Exists exists; // 判断键是否存在 - Boolean autoAssign; // 设定是否根据当前数据量动态调整内存大小,默认开启 + Boolean autoAssign; // 设定是否根据当前数据量动态调整内存大小,默认开启 }*HashMap; 包括以下函数功能,分别为: `createHashMap`:创建一个哈希结构 +`defaultHashCode`:生成一个哈希值 `defaultPut`:添加键值对 `defaultGet`:获取键对应值 `defaultRemove`:删除指定键的键值对 @@ -41,7 +42,7 @@ typedef struct hashMap { ## 3. 测试程序说明 测试了哈希表的插入键值对(Put),判断键是否存在(Exist),获取键对应的值(Get), 删除健(Delete),迭代遍历哈希表(hashMapIterator),清空哈希表(Clear)等操作。 -并展示了利用链地址法解决哈希冲突的示例, 两个不同的人(Bob和Li Ming)的hashcode相同。 +并展示了利用线性探测法解决哈希冲突的示例, 多个原hashcode相同的人(Bob和Luna;Annie、Daniel和Li Ming)的新hashcode(存放的地址)不同。 ## 4. 运行结果(##需结合运行测试截图按步骤说明##) ![image](ConfigOpen.png) diff --git a/APP_Framework/Applications/app_test/test_hash/ShellCommand.png b/APP_Framework/Applications/app_test/test_hash/ShellCommand.png index f9338e7e1..c70b98689 100644 Binary files a/APP_Framework/Applications/app_test/test_hash/ShellCommand.png and b/APP_Framework/Applications/app_test/test_hash/ShellCommand.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/TestHash.png b/APP_Framework/Applications/app_test/test_hash/TestHash.png index 3170edfef..b8cb859e6 100644 Binary files a/APP_Framework/Applications/app_test/test_hash/TestHash.png and b/APP_Framework/Applications/app_test/test_hash/TestHash.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/test_hash.c b/APP_Framework/Applications/app_test/test_hash/test_hash.c index d6eabc4c7..8ed248eb5 100644 --- a/APP_Framework/Applications/app_test/test_hash/test_hash.c +++ b/APP_Framework/Applications/app_test/test_hash/test_hash.c @@ -11,282 +11,265 @@ #include"test_hash.h" int defaultHashCode(HashMap hashMap, let key) { - char * k = (char *)key; - unsigned long h = 0; - while (*k) { - h = (h << 4) + *k++; - unsigned long g = h & 0xF0000000L; - if (g) { - h ^= g >> 24; - } - h &= ~g; - } - return h % hashMap->listSize; + char *k = (char *) key; + // DJB哈希算法 + unsigned long hash = 5381; + int c; + while (c = *k++) { + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + } + return hash % hashMap->listSize; } Boolean defaultEqual(let key1, let key2) { - return strcmp((string)key1, (string)key2) ? False : True; + return strcmp((string) key1, (string) key2) ? False : True; } void resetHashMap(HashMap hashMap, int listSize) { - if (listSize < 8) return; + if (listSize < 8) return; - // 键值对临时存储空间 - Entry tempList = newEntryList(hashMap->size); + // 键值对临时存储空间 + Entry tempList = newEntryList(hashMap->size); - HashMapIterator iterator = createHashMapIterator(hashMap); - int length = hashMap->size; - for (int index = 0; hasNextHashMapIterator(iterator); index++) { - // 迭代取出所有键值对 - iterator = nextHashMapIterator(iterator); - tempList[index].key = iterator->entry->key; - tempList[index].value = iterator->entry->value; - tempList[index].next = NULL; - } - freeHashMapIterator(&iterator); + HashMapIterator iterator = createHashMapIterator(hashMap); + int length = hashMap->size; + for (int index = 0; hasNextHashMapIterator(iterator); index++) { + // 迭代取出所有没有被逻辑删除的键值对 + iterator = nextHashMapIterator(iterator); + if(!iterator->entry->isDelete){ + tempList[index].key = iterator->entry->key; + tempList[index].value = iterator->entry->value; + tempList[index].isDelete = False; + tempList[index].next = NULL; + } + } + freeHashMapIterator(&iterator); - // 清除原有键值对数据 - hashMap->size = 0; - for (int i = 0; i < hashMap->listSize; i++) { - Entry current = &hashMap->list[i]; - current->key = NULL; - current->value = NULL; - if (current->next != NULL) { - while (current->next != NULL) { - Entry temp = current->next->next; - free(current->next); - current->next = temp; - } - } - } + // 清除原有键值对数据 + hashMap->size = 0; + for (int i = 0; i < hashMap->listSize; i++) { + Entry current = &hashMap->list[i]; + current->key = NULL; + current->value = NULL; + current->isDelete = False; + if (current->next != NULL) { + while (current->next != NULL) { + Entry temp = current->next->next; + free(current->next); + current->next = temp; + } + } + } - // 更改内存大小 - hashMap->listSize = listSize; - Entry relist = (Entry)realloc(hashMap->list, hashMap->listSize * sizeof(struct entry)); - if (relist != NULL) { - hashMap->list = relist; - relist = NULL; - } +// 更改内存大小 + hashMap->listSize = listSize; + Entry relist = (Entry) realloc(hashMap->list, hashMap->listSize * sizeof(struct entry)); + if (relist != NULL) { + hashMap->list = relist; + relist = NULL; + } - // 初始化数据 - for (int i = 0; i < hashMap->listSize; i++) { - hashMap->list[i].key = NULL; - hashMap->list[i].value = NULL; - hashMap->list[i].next = NULL; - } +// 初始化数据 + for (int i = 0; i < hashMap->listSize; i++) { + hashMap->list[i].key = NULL; + hashMap->list[i].value = NULL; + hashMap->list[i].isDelete = False; + hashMap->list[i].next = NULL; + } - // 将所有键值对重新写入内存 - for (int i = 0; i < length; i++) { - hashMap->put(hashMap, tempList[i].key, tempList[i].value); - } - free(tempList); +// 将所有键值对重新写入内存 + for (int i = 0; i < length; i++) { + hashMap->put(hashMap, tempList[i].key, tempList[i].value); + } + free(tempList); } -void defaultPut(HashMap hashMap, let key, let value) { - // 获取哈希值 - int index = hashMap->hashCode(hashMap, key); +Boolean defaultPut(HashMap hashMap, let key, let value) { + // 获取哈希值 + int index = hashMap->hashCode(hashMap, key); + // 判断线性探测法是否成功插入 + Boolean result = False; + // 如果发生冲突将根据线性探测法探测相邻的下一个单元是否可以存储,直到超出表长 + for (int indexNext = index; indexNext < hashMap->listSize; ++indexNext) { + if (hashMap->list[indexNext].key == NULL) { + hashMap->size++; + // 该地址为空时直接存储 + hashMap->list[indexNext].key = key; + hashMap->list[indexNext].value = value; + result = True; + break; + } else { + Entry current = &hashMap->list[indexNext]; + while (current != NULL) { + if (hashMap->equal(key, current->key)) { + // 对于键值已经存在的直接覆盖 + current->value = value; + return True; + } + current = current->next; + } + } + } + if (hashMap->autoAssign && hashMap->size >= hashMap->listSize) { - if (hashMap->list[index].key == NULL) { - hashMap->size++; - // 该地址为空时直接存储 - hashMap->list[index].key = key; - hashMap->list[index].value = value; - } - else { + // 内存扩充至原来的两倍 + // *注: 扩充时考虑的是当前存储元素数量与存储空间的大小关系,而不是存储空间是否已经存满, + // 例如: 存储空间为10,存入了10个键值对,但是全部冲突了,所以存储空间空着9个,其余的全部挂在一个上面, + // 这样检索的时候和遍历查询没有什么区别了,可以简单这样理解,当我存入第11个键值对的时候一定会发生冲突, + // 这是由哈希函数本身的特性(取模)决定的,冲突就会导致检索变慢,所以这时候扩充存储空间,对原有键值对进行 + // 再次散列,会把冲突的数据再次分散开,加快索引定位速度。 + resetHashMap(hashMap, hashMap->listSize * 2); + } - Entry current = &hashMap->list[index]; - while (current != NULL) { - if (hashMap->equal(key, current->key)) { - // 对于键值已经存在的直接覆盖 - current->value = value; - return; - } - current = current->next; - }; - - // 发生冲突则创建节点挂到相应位置的next上 - Entry entry = newEntry(); - entry->key = key; - entry->value = value; - entry->next = hashMap->list[index].next; - hashMap->list[index].next = entry; - hashMap->size++; - } - - if (hashMap->autoAssign && hashMap->size >= hashMap->listSize) { - - // 内存扩充至原来的两倍 - // *注: 扩充时考虑的是当前存储元素数量与存储空间的大小关系,而不是存储空间是否已经存满, - // 例如: 存储空间为10,存入了10个键值对,但是全部冲突了,所以存储空间空着9个,其余的全部挂在一个上面, - // 这样检索的时候和遍历查询没有什么区别了,可以简单这样理解,当我存入第11个键值对的时候一定会发生冲突, - // 这是由哈希函数本身的特性(取模)决定的,冲突就会导致检索变慢,所以这时候扩充存储空间,对原有键值对进行 - // 再次散列,会把冲突的数据再次分散开,加快索引定位速度。 - resetHashMap(hashMap, hashMap->listSize * 2); - } + return result; } let defaultGet(HashMap hashMap, let key) { - if (hashMap->exists(hashMap, key)) { - int index = hashMap->hashCode(hashMap, key); - Entry entry = &hashMap->list[index]; - while (entry != NULL) { - if (hashMap->equal(entry->key, key)) { - return entry->value; - } - entry = entry->next; - } - } - return NULL; + if (hashMap->exists(hashMap, key)) { + int index = hashMap->hashCode(hashMap, key); + for (int indexNext = index; indexNext < hashMap->listSize; ++indexNext) { + Entry entry = &hashMap->list[indexNext]; + if (entry == NULL) { + return NULL; + } + if (hashMap->equal(entry->key, key) && !entry->isDelete) { + return entry->value; + } + } + } + return NULL; } -let defaultRemove(HashMap hashMap, let key) { - int index = hashMap->hashCode(hashMap, key); - Entry entry = &hashMap->list[index]; - if (entry->key == NULL) { - return NULL; - } - let entryKey = entry->key; - Boolean result = False; - if (hashMap->equal(entry->key, key)) { - hashMap->size--; - if (entry->next != NULL) { - Entry temp = entry->next; - entry->key = temp->key; - entry->value = temp->value; - entry->next = temp->next; - free(temp); - } - else { - entry->key = NULL; - entry->value = NULL; +Boolean defaultRemove(HashMap hashMap, let key) { + int index = hashMap->hashCode(hashMap, key); + Boolean result = False; + for (int indexNext = index; indexNext < hashMap->listSize; ++indexNext) { + Entry entry = &hashMap->list[indexNext]; + if (entry->key == NULL) { + return result; } - result = True; - } - else { - Entry p = entry; - entry = entry->next; - while (entry != NULL) { - if (hashMap->equal(entry->key, key)) { - hashMap->size--; - p->next = entry->next; - free(entry); - result = True; - break; - } - p = entry; - entry = entry->next; - }; - } - - // 如果空间占用不足一半,则释放多余内存 - if (result && hashMap->autoAssign && hashMap->size < hashMap->listSize / 2) { - resetHashMap(hashMap, hashMap->listSize / 2); - } - return entryKey; + if (hashMap->equal(entry->key, key)) { + hashMap->size--; + if (entry->next != NULL) { + Entry temp = entry->next; + entry->key = temp->key; + entry->value = temp->value; + entry->next = temp->next; + free(temp); + } else { + entry->isDelete = True; + } + result = True; + break; + } + } + // 如果空间占用不足一半,则释放多余内存 + if (result && hashMap->autoAssign && hashMap->size < hashMap->listSize / 2) { + resetHashMap(hashMap, hashMap->listSize / 2); + } + return result; } Boolean defaultExists(HashMap hashMap, let key) { - int index = hashMap->hashCode(hashMap, key); - Entry entry = &hashMap->list[index]; - if (entry->key == NULL) { - return False; - } - else { - while (entry != NULL) { - if (hashMap->equal(entry->key, key)) { - return True; - } - entry = entry->next; - } - return False; - } + int index = hashMap->hashCode(hashMap, key); + for (int indexNext = index; indexNext < hashMap->listSize; ++indexNext) { + Entry entry = &hashMap->list[indexNext]; + // 判断是否存在关键字并且没有被逻辑删除 + if (entry->key == NULL) { + return False; + } else if (hashMap->equal(entry->key, key) && !entry->isDelete) { + return True; + } + } + return False; } void defaultClear(HashMap hashMap) { - for (int i = 0; i < hashMap->listSize; i++) { - // 释放冲突值内存 - Entry entry = hashMap->list[i].next; - while (entry != NULL) { - Entry next = entry->next; - free(entry); - entry = next; - } - hashMap->list[i].next = NULL; - } - // 释放存储空间 - free(hashMap->list); - hashMap->list = NULL; - hashMap->size = -1; - hashMap->listSize = 0; + for (int i = 0; i < hashMap->listSize; i++) { + // 释放冲突值内存 + Entry entry = hashMap->list[i].next; + while (entry != NULL) { + Entry next = entry->next; + free(entry); + entry = next; + } + hashMap->list[i].next = NULL; + } + // 释放存储空间 + free(hashMap->list); + hashMap->list = NULL; + hashMap->size = -1; + hashMap->listSize = 0; } HashMap createHashMap(HashCode hashCode, Equal equal) { - HashMap hashMap = newHashMap(); - if (hashMap == NULL) { - return NULL; - } - hashMap->size = 0; - hashMap->listSize = 8; - hashMap->hashCode = hashCode == NULL ? defaultHashCode : hashCode; - hashMap->equal = equal == NULL ? defaultEqual : equal; - hashMap->exists = defaultExists; - hashMap->get = defaultGet; - hashMap->put = defaultPut; - hashMap->remove = defaultRemove; - hashMap->clear = defaultClear; - hashMap->autoAssign = True; - - // 起始分配8个内存空间,溢出时会自动扩充 - hashMap->list = newEntryList(hashMap->listSize); - if (hashMap->list == NULL) { - return NULL; - } - Entry p = hashMap->list; - for (int i = 0; i < hashMap->listSize; i++) { - p[i].key = p[i].value = p[i].next = NULL; - } - return hashMap; + HashMap hashMap = newHashMap(); + if (hashMap == NULL) { + return NULL; + } + hashMap->size = 0; + hashMap->listSize = 8; + hashMap->hashCode = hashCode == NULL ? defaultHashCode : hashCode; + hashMap->equal = equal == NULL ? defaultEqual : equal; + hashMap->exists = defaultExists; + hashMap->get = defaultGet; + hashMap->put = defaultPut; + hashMap->remove = defaultRemove; + hashMap->clear = defaultClear; + hashMap->autoAssign = True; + + // 起始分配8个内存空间,溢出时会自动扩充 + hashMap->list = newEntryList(hashMap->listSize); + if (hashMap->list == NULL) { + return NULL; + } + Entry p = hashMap->list; + for (int i = 0; i < hashMap->listSize; i++) { + p[i].key = p[i].value = p[i].next = NULL; + p[i].isDelete = False; + } + return hashMap; } HashMapIterator createHashMapIterator(HashMap hashMap) { - HashMapIterator iterator = newHashMapIterator(); - if (iterator == NULL) { - return NULL; - } - iterator->hashMap = hashMap; - iterator->count = 0; - iterator->hashCode = -1; - iterator->entry = NULL; - return iterator; + HashMapIterator iterator = newHashMapIterator(); + if (iterator == NULL) { + return NULL; + } + iterator->hashMap = hashMap; + iterator->count = 0; + iterator->hashCode = -1; + iterator->entry = NULL; + return iterator; } Boolean hasNextHashMapIterator(HashMapIterator iterator) { - return iterator->count < iterator->hashMap->size ? True : False; + return iterator->count < iterator->hashMap->size ? True : False; } HashMapIterator nextHashMapIterator(HashMapIterator iterator) { - if (hasNextHashMapIterator(iterator)) { - if (iterator->entry != NULL && iterator->entry->next != NULL) { - iterator->count++; - iterator->entry = iterator->entry->next; - return iterator; - } - while (++iterator->hashCode < iterator->hashMap->listSize) { - Entry entry = &iterator->hashMap->list[iterator->hashCode]; - if (entry->key != NULL) { - iterator->count++; - iterator->entry = entry; - break; - } - } - } - return iterator; + if (hasNextHashMapIterator(iterator)) { + if (iterator->entry != NULL && iterator->entry->next != NULL) { + iterator->count++; + iterator->entry = iterator->entry->next; + return iterator; + } + while (++iterator->hashCode < iterator->hashMap->listSize) { + Entry entry = &iterator->hashMap->list[iterator->hashCode]; + if (entry->key != NULL) { + iterator->count++; + iterator->entry = entry; + break; + } + } + } + return iterator; } -void freeHashMapIterator(HashMapIterator * iterator) { - free(*iterator); - *iterator = NULL; +void freeHashMapIterator(HashMapIterator *iterator) { + free(*iterator); + *iterator = NULL; } #define Put(map, key, value) map->put(map, (void *)key, (void *)value); @@ -308,8 +291,9 @@ void TestHash() { HashMapIterator iterator = createHashMapIterator(map); while (hasNextHashMapIterator(iterator)) { iterator = nextHashMapIterator(iterator); - printf("{ key: %s, key: %s, hashcode: %d }\n", - (char *)iterator->entry->key, (char *)iterator->entry->value, iterator->hashCode); + printf("{ key: %s, key: %s, oldHashcode: %d, newHashcode: %d }\n", + (char *) iterator->entry->key, (char *) iterator->entry->value, + iterator->hashMap->hashCode(iterator->hashMap, iterator->entry->key), iterator->hashCode); } printf("key: 000852, exists: %s\n", Existe(map, "000852") ? "true" : "false"); printf("000852: %s\n", Get(map, "000852")); diff --git a/APP_Framework/Applications/app_test/test_hash/test_hash.h b/APP_Framework/Applications/app_test/test_hash/test_hash.h index 3f7358411..2bb0ce16f 100644 --- a/APP_Framework/Applications/app_test/test_hash/test_hash.h +++ b/APP_Framework/Applications/app_test/test_hash/test_hash.h @@ -23,16 +23,19 @@ #define NEW(type) (type *)malloc(sizeof(type)) // 布尔类型 -enum _Boolean { True = 1, False = 0 }; -typedef enum _Boolean Boolean; +enum _Boolean { + True = 1, False = 0 +}; +typedef enum _Boolean Boolean; #define let void * typedef struct entry { - let key; // 键 - let value; // 值 - struct entry * next; // 冲突链表 -}*Entry; + let key; // 键 + let value; // 值 + Boolean isDelete; // 逻辑删除 + struct entry *next; // 冲突链表 +} *Entry; #define newEntry() NEW(struct entry) #define newEntryList(length) (Entry)malloc(length * sizeof(struct entry)) @@ -48,14 +51,14 @@ typedef int(*HashCode)(HashMap, let key); // 判等函数类型 typedef Boolean(*Equal)(let key1, let key2); -// 添加键函数类型 -typedef void(*Put)(HashMap hashMap, let key, let value); +// 添加键函数类型 返回是否成功 +typedef Boolean(*Put)(HashMap hashMap, let key, let value); // 获取键对应值的函数类型 typedef let(*Get)(HashMap hashMap, let key); // 删除键的函数类型 -typedef let(*Remove)(HashMap hashMap, let key); +typedef Boolean(*Remove)(HashMap hashMap, let key); // 清空Map的函数类型 typedef void(*Clear)(HashMap hashMap); @@ -64,26 +67,26 @@ typedef void(*Clear)(HashMap hashMap); typedef Boolean(*Exists)(HashMap hashMap, let key); typedef struct hashMap { - int size; // 当前大小 - int listSize; // 有效空间大小 - HashCode hashCode; // 哈希函数 - Equal equal; // 判等函数 - Entry list; // 存储区域 - Put put; // 添加键的函数 - Get get; // 获取键对应值的函数 - Remove remove; // 删除键 - Clear clear; // 清空Map - Exists exists; // 判断键是否存在 - Boolean autoAssign; // 设定是否根据当前数据量动态调整内存大小,默认开启 -}*HashMap; + int size; // 当前大小 + int listSize; // 有效空间大小 + HashCode hashCode; // 哈希函数 + Equal equal; // 判等函数 + Entry list; // 存储区域 + Put put; // 添加键的函数 + Get get; // 获取键对应值的函数 + Remove remove; // 删除键 + Clear clear; // 清空Map + Exists exists; // 判断键是否存在 + Boolean autoAssign; // 设定是否根据当前数据量动态调整内存大小,默认开启 +} *HashMap; // 迭代器结构 typedef struct hashMapIterator { - Entry entry; // 迭代器当前指向 - int count; // 迭代次数 - int hashCode; // 键值对的哈希值 - HashMap hashMap; -}*HashMapIterator; + Entry entry; // 迭代器当前指向 + int count; // 迭代次数 + int hashCode; // 键值对的哈希值 + HashMap hashMap; +} *HashMapIterator; #define newHashMapIterator() NEW(struct hashMapIterator) @@ -93,14 +96,14 @@ static int defaultHashCode(HashMap hashMap, let key); // 默认判断键值是否相等 static Boolean defaultEqual(let key1, let key2); -// 默认添加键值对 -static void defaultPut(HashMap hashMap, let key, let value); +// 默认添加键值对 返回是否插入成功 +static Boolean defaultPut(HashMap hashMap, let key, let value); // 默认获取键对应值 static let defaultGet(HashMap hashMap, let key); // 默认删除键 -static let defaultRemove(HashMap hashMap, let key); +static Boolean defaultRemove(HashMap hashMap, let key); // 默认判断键是否存在 static Boolean defaultExists(HashMap hashMap, let key); @@ -124,6 +127,6 @@ Boolean hasNextHashMapIterator(HashMapIterator iterator); HashMapIterator nextHashMapIterator(HashMapIterator iterator); // 释放迭代器内存 -void freeHashMapIterator(HashMapIterator * iterator); +void freeHashMapIterator(HashMapIterator *iterator); #endif // !__HASHMAP_H__ \ No newline at end of file