/* Copyright 2008, 2009, 2010 by the Oxford University Computing Laboratory This file is part of HermiT. HermiT is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. HermiT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with HermiT. If not, see <http://www.gnu.org/licenses/>. */ package org.semanticweb.HermiT.tableau; import java.io.Serializable; public final class TupleTableFullIndex implements Serializable { private static final long serialVersionUID=5006873858554891684L; protected static final int BUCKET_OFFSET=1; protected static final float LOAD_FACTOR=0.75f; protected final TupleTable m_tupleTable; protected final int m_indexedArity; protected final EntryManager m_entryManager; protected int[] m_buckets; protected int m_resizeThreshold; protected int m_numberOfTuples; public TupleTableFullIndex(TupleTable tupleTable,int indexedArity) { m_tupleTable=tupleTable; m_indexedArity=indexedArity; m_entryManager=new EntryManager(); clear(); } public int sizeInMemory() { return m_buckets.length*4+m_entryManager.size(); } public void clear() { m_buckets=new int[16]; m_resizeThreshold=(int)(m_buckets.length*LOAD_FACTOR); m_entryManager.clear(); } public int addTuple(Object[] tuple,int tentativeTupleIndex) { int hashCode=getTupleHashCode(tuple); int entryIndex=getBucketIndex(hashCode,m_buckets.length); int entry=m_buckets[entryIndex]-BUCKET_OFFSET; while (entry!=-1) { if (hashCode==m_entryManager.getEntryComponent(entry,ENTRY_HASH_CODE)) { int tupleIndex=m_entryManager.getEntryComponent(entry,ENTRY_TUPLE_INDEX); if (m_tupleTable.tupleEquals(tuple,tupleIndex,m_indexedArity)) return tupleIndex; } entry=m_entryManager.getEntryComponent(entry,ENTRY_NEXT); } entry=m_entryManager.newEntry(); m_entryManager.setEntryComponent(entry,ENTRY_NEXT,m_buckets[entryIndex]-BUCKET_OFFSET); m_entryManager.setEntryComponent(entry,ENTRY_HASH_CODE,hashCode); m_entryManager.setEntryComponent(entry,ENTRY_TUPLE_INDEX,tentativeTupleIndex); m_buckets[entryIndex]=entry+BUCKET_OFFSET; m_numberOfTuples++; if (m_numberOfTuples>=m_resizeThreshold) resizeBuckets(); return tentativeTupleIndex; } protected void resizeBuckets() { int[] newBuckets=new int[m_buckets.length*2]; for (int bucketIndex=m_buckets.length-1;bucketIndex>=0;--bucketIndex) { int entry=m_buckets[bucketIndex]-BUCKET_OFFSET; while (entry!=-1) { int nextEntry=m_entryManager.getEntryComponent(entry,ENTRY_NEXT); int newBucketIndex=getBucketIndex(m_entryManager.getEntryComponent(entry,ENTRY_HASH_CODE),newBuckets.length); m_entryManager.setEntryComponent(entry,ENTRY_NEXT,newBuckets[newBucketIndex]-BUCKET_OFFSET); newBuckets[newBucketIndex]=entry+BUCKET_OFFSET; entry=nextEntry; } } m_buckets=newBuckets; m_resizeThreshold=(int)(newBuckets.length*LOAD_FACTOR); } public int getTupleIndex(Object[] tuple) { int hashCode=getTupleHashCode(tuple); int entryIndex=getBucketIndex(hashCode,m_buckets.length); int entry=m_buckets[entryIndex]-BUCKET_OFFSET; while (entry!=-1) { if (hashCode==m_entryManager.getEntryComponent(entry,ENTRY_HASH_CODE)) { int tupleIndex=m_entryManager.getEntryComponent(entry,ENTRY_TUPLE_INDEX); if (m_tupleTable.tupleEquals(tuple,tupleIndex,m_indexedArity)) return tupleIndex; } entry=m_entryManager.getEntryComponent(entry,ENTRY_NEXT); } return -1; } public int getTupleIndex(Object[] tupleBuffer,int[] positionIndexes) { int hashCode=getTupleHashCode(tupleBuffer,positionIndexes); int entryIndex=getBucketIndex(hashCode,m_buckets.length); int entry=m_buckets[entryIndex]-BUCKET_OFFSET; while (entry!=-1) { if (hashCode==m_entryManager.getEntryComponent(entry,ENTRY_HASH_CODE)) { int tupleIndex=m_entryManager.getEntryComponent(entry,ENTRY_TUPLE_INDEX); if (m_tupleTable.tupleEquals(tupleBuffer,positionIndexes,tupleIndex,m_indexedArity)) return tupleIndex; } entry=m_entryManager.getEntryComponent(entry,ENTRY_NEXT); } return -1; } public boolean removeTuple(int tupleIndex) { int hashCode=0; for (int i = 0; i < m_indexedArity; ++i) { hashCode += m_tupleTable.getTupleObject(tupleIndex, i).hashCode(); } int lastEntry=-1; int entryIndex=getBucketIndex(hashCode,m_buckets.length); int entry=m_buckets[entryIndex]-BUCKET_OFFSET; while (entry!=-1) { int nextEntry=m_entryManager.getEntryComponent(entry,ENTRY_NEXT); if (hashCode==m_entryManager.getEntryComponent(entry,ENTRY_HASH_CODE) && tupleIndex==m_entryManager.getEntryComponent(entry,ENTRY_TUPLE_INDEX)) { if (lastEntry==-1) m_buckets[entryIndex]=nextEntry+BUCKET_OFFSET; else m_entryManager.setEntryComponent(lastEntry,ENTRY_NEXT,nextEntry); return true; } lastEntry=entry; entry=nextEntry; } return false; } protected int getTupleHashCode(Object[] tuple) { int hashCode=0; for (int index=0;index<m_indexedArity;index++) hashCode+=tuple[index].hashCode(); return hashCode; } protected int getTupleHashCode(Object[] tupleBuffer,int[] positionIndexes) { int hashCode=0; for (int index=0;index<m_indexedArity;index++) hashCode+=tupleBuffer[positionIndexes[index]].hashCode(); return hashCode; } protected static int getBucketIndex(int hashCode,int bucketsLength) { return hashCode & (bucketsLength-1); } protected static final int ENTRY_SIZE=3; protected static final int ENTRY_NEXT=0; protected static final int ENTRY_HASH_CODE=1; protected static final int ENTRY_TUPLE_INDEX=2; protected static final int ENTRY_PAGE_SIZE=512; protected static final class EntryManager implements Serializable { private static final long serialVersionUID=-7562640774004213308L; protected int[] m_entries; protected int m_firstFreeEntry; public EntryManager() { clear(); } public int size() { return m_entries.length*4; } public void clear() { m_entries=new int[ENTRY_SIZE*ENTRY_PAGE_SIZE]; m_firstFreeEntry=0; m_entries[m_firstFreeEntry+ENTRY_NEXT]=-1; } public int getEntryComponent(int entry,int component) { return m_entries[entry+component]; } public void setEntryComponent(int entry,int component,int value) { m_entries[entry+component]=value; } public int newEntry() { int result=m_firstFreeEntry; int nextFreeEntry=m_entries[m_firstFreeEntry+ENTRY_NEXT]; if (nextFreeEntry==-1) { m_firstFreeEntry+=ENTRY_SIZE; if (m_firstFreeEntry>=m_entries.length) { int[] newEntries=new int[m_entries.length+ENTRY_SIZE*ENTRY_PAGE_SIZE]; System.arraycopy(m_entries,0,newEntries,0,m_entries.length); m_entries=newEntries; } m_entries[m_firstFreeEntry+ENTRY_NEXT]=-1; } else m_firstFreeEntry=nextFreeEntry; return result; } public void deleteEntry(int entry) { m_entries[entry+ENTRY_NEXT]=m_firstFreeEntry; m_firstFreeEntry=entry; } } }