/* 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 TupleIndex implements Serializable { private static final long serialVersionUID=-4284072092430590904L; protected static final float LOAD_FACTOR=0.7f; protected static final int BUCKET_OFFSET=1; protected final int[] m_indexingSequence; protected final TrieNodeManager m_trieNodeManager; protected int m_root; protected int[] m_buckets; protected int m_bucketsLengthMinusOne; // must be all ones in binary! protected int m_resizeThreshold; protected int m_numberOfNodes; public TupleIndex(int[] indexingSequence) { m_indexingSequence=indexingSequence; m_trieNodeManager=new TrieNodeManager(); clear(); } public int sizeInMemoy() { return m_buckets.length*4+m_trieNodeManager.size(); } public int[] getIndexingSequence() { return m_indexingSequence; } public void clear() { m_trieNodeManager.clear(); m_root=m_trieNodeManager.newTrieNode(); m_trieNodeManager.initializeTrieNode(m_root,-1,-1,-1,-1,-1,null); m_buckets=new int[16]; m_bucketsLengthMinusOne=m_buckets.length-1; m_resizeThreshold=(int)(m_buckets.length*LOAD_FACTOR); m_numberOfNodes=0; } public int addTuple(Object[] tuple,int potentialTupleIndex) { int trieNode=m_root; for (int position=0;position<m_indexingSequence.length;position++) { Object object=tuple[m_indexingSequence[position]]; trieNode=getChildNodeAddIfNecessary(trieNode,object); } if (m_trieNodeManager.getTrieNodeComponent(trieNode,TRIE_NODE_TUPLE_INDEX)==-1) { m_trieNodeManager.setTrieNodeComponent(trieNode,TRIE_NODE_TUPLE_INDEX,potentialTupleIndex); return potentialTupleIndex; } else return m_trieNodeManager.getTrieNodeComponent(trieNode,TRIE_NODE_TUPLE_INDEX); } public int getTupleIndex(Object[] tuple) { int trieNode=m_root; for (int position=0;position<m_indexingSequence.length;position++) { Object object=tuple[m_indexingSequence[position]]; trieNode=getChildNode(trieNode,object); if (trieNode==-1) return -1; } return m_trieNodeManager.getTrieNodeComponent(trieNode,TRIE_NODE_TUPLE_INDEX); } public int removeTuple(Object[] tuple) { int leafTrieNode=m_root; for (int position=0;position<m_indexingSequence.length;position++) { Object object=tuple[m_indexingSequence[position]]; leafTrieNode=getChildNode(leafTrieNode,object); if (leafTrieNode==-1) return -1; } int tupleIndex=m_trieNodeManager.getTrieNodeComponent(leafTrieNode,TRIE_NODE_TUPLE_INDEX); int trieNode=m_trieNodeManager.getTrieNodeComponent(leafTrieNode,TRIE_NODE_PARENT); removeTrieNode(leafTrieNode); while (trieNode!=m_root && m_trieNodeManager.getTrieNodeComponent(trieNode,TRIE_NODE_FIRST_CHILD)==-1) { int parentTrieNode=m_trieNodeManager.getTrieNodeComponent(trieNode,TRIE_NODE_PARENT); removeTrieNode(trieNode); trieNode=parentTrieNode; } return tupleIndex; } protected void removeTrieNode(int trieNode) { Object object=m_trieNodeManager.getTrieNodeObject(trieNode); int parent=m_trieNodeManager.getTrieNodeComponent(trieNode,TRIE_NODE_PARENT); int bucketIndex=getIndexFor(object.hashCode()+parent,m_bucketsLengthMinusOne); int child=m_buckets[bucketIndex]-BUCKET_OFFSET; int previousChild=-1; while (child!=-1) { int nextChild=m_trieNodeManager.getTrieNodeComponent(child,TRIE_NODE_NEXT_ENTRY); if (child==trieNode) { m_numberOfNodes--; int previousSibling=m_trieNodeManager.getTrieNodeComponent(trieNode,TRIE_NODE_PREVIOUS_SIBLING); int nextSibling=m_trieNodeManager.getTrieNodeComponent(trieNode,TRIE_NODE_NEXT_SIBLING); if (previousSibling==-1) m_trieNodeManager.setTrieNodeComponent(parent,TRIE_NODE_FIRST_CHILD,nextSibling); else m_trieNodeManager.setTrieNodeComponent(previousSibling,TRIE_NODE_NEXT_SIBLING,nextSibling); if (nextSibling!=-1) m_trieNodeManager.setTrieNodeComponent(nextSibling,TRIE_NODE_PREVIOUS_SIBLING,previousSibling); if (previousChild==-1) m_buckets[bucketIndex]=nextChild+BUCKET_OFFSET; else m_trieNodeManager.setTrieNodeComponent(previousChild,TRIE_NODE_NEXT_ENTRY,nextChild); m_trieNodeManager.deleteTrieNode(trieNode); return; } previousChild=child; child=nextChild; } throw new IllegalStateException("Internal error: should be able to remove the child node."); } protected int getChildNode(int parent,Object object) { int bucketIndex=getIndexFor(object.hashCode()+parent,m_bucketsLengthMinusOne); int child=m_buckets[bucketIndex]-BUCKET_OFFSET; while (child!=-1) { if (parent==m_trieNodeManager.getTrieNodeComponent(child,TRIE_NODE_PARENT) && object.equals(m_trieNodeManager.getTrieNodeObject(child))) return child; child=m_trieNodeManager.getTrieNodeComponent(child,TRIE_NODE_NEXT_ENTRY); } return -1; } protected int getChildNodeAddIfNecessary(int parent,Object object) { int hashCode=object.hashCode()+parent; int bucketIndex=getIndexFor(hashCode,m_bucketsLengthMinusOne); int child=m_buckets[bucketIndex]-BUCKET_OFFSET; while (child!=-1) { if (parent==m_trieNodeManager.getTrieNodeComponent(child,TRIE_NODE_PARENT) && object.equals(m_trieNodeManager.getTrieNodeObject(child))) return child; child=m_trieNodeManager.getTrieNodeComponent(child,TRIE_NODE_NEXT_ENTRY); } if (m_numberOfNodes>=m_resizeThreshold) { resizeBuckets(); bucketIndex=getIndexFor(hashCode,m_bucketsLengthMinusOne); } child=m_trieNodeManager.newTrieNode(); int nextSibling=m_trieNodeManager.getTrieNodeComponent(parent,TRIE_NODE_FIRST_CHILD); if (nextSibling!=-1) m_trieNodeManager.setTrieNodeComponent(nextSibling,TRIE_NODE_PREVIOUS_SIBLING,child); m_trieNodeManager.setTrieNodeComponent(parent,TRIE_NODE_FIRST_CHILD,child); m_trieNodeManager.initializeTrieNode(child,parent,-1,-1,nextSibling,m_buckets[bucketIndex]-BUCKET_OFFSET,object); m_buckets[bucketIndex]=child+BUCKET_OFFSET; m_numberOfNodes++; return child; } protected void resizeBuckets() { if (m_buckets.length==0x40000000) m_resizeThreshold=Integer.MAX_VALUE; else { int[] newBuckets=new int[m_buckets.length*2]; int newBucketsLengthMinusOne=newBuckets.length-1; for (int bucketIndex=m_bucketsLengthMinusOne;bucketIndex>=0;--bucketIndex) { int trieNode=m_buckets[bucketIndex]-BUCKET_OFFSET; while (trieNode!=-1) { int nextTrieNode=m_trieNodeManager.getTrieNodeComponent(trieNode,TRIE_NODE_NEXT_ENTRY); int hashCode=m_trieNodeManager.getTrieNodeObject(trieNode).hashCode()+m_trieNodeManager.getTrieNodeComponent(trieNode,TRIE_NODE_PARENT); int newBucketIndex=getIndexFor(hashCode,newBucketsLengthMinusOne); m_trieNodeManager.setTrieNodeComponent(trieNode,TRIE_NODE_NEXT_ENTRY,newBuckets[newBucketIndex]-BUCKET_OFFSET); newBuckets[newBucketIndex]=trieNode+BUCKET_OFFSET; trieNode=nextTrieNode; } } m_buckets=newBuckets; m_bucketsLengthMinusOne=newBucketsLengthMinusOne; m_resizeThreshold=(int)(m_buckets.length*LOAD_FACTOR); } } protected static int getIndexFor(int hashCode,int tableLengthMinusOne) { hashCode+=~(hashCode << 9); hashCode^=(hashCode >>> 14); hashCode+=(hashCode << 4); hashCode^=(hashCode >>> 10); return hashCode & tableLengthMinusOne; } protected static final int TRIE_NODE_PARENT=0; protected static final int TRIE_NODE_FIRST_CHILD=1; protected static final int TRIE_NODE_TUPLE_INDEX=1; protected static final int TRIE_NODE_PREVIOUS_SIBLING=2; protected static final int TRIE_NODE_NEXT_SIBLING=3; protected static final int TRIE_NODE_NEXT_ENTRY=4; protected static final int TRIE_NODE_SIZE=5; protected static final int TRIE_NODE_PAGE_SIZE=1024; protected static final class TrieNodeManager implements Serializable { private static final long serialVersionUID=-1978070096232682717L; protected int[][] m_indexPages; protected Object[][] m_objectPages; protected int m_firstFreeTrieNode; protected int m_numberOfPages; public TrieNodeManager() { clear(); } public int size() { int size=m_indexPages.length*4+m_objectPages.length*4; for (int i=m_indexPages.length-1;i>=0;--i) if (m_indexPages[i]!=null) size+=m_indexPages[i].length*4; for (int i=m_objectPages.length-1;i>=0;--i) if (m_objectPages[i]!=null) size+=m_objectPages[i].length*4; return size; } public void clear() { m_indexPages=new int[10][]; m_indexPages[0]=new int[TRIE_NODE_SIZE*TRIE_NODE_PAGE_SIZE]; m_objectPages=new Object[10][]; m_objectPages[0]=new Object[TRIE_NODE_PAGE_SIZE]; m_numberOfPages=1; m_firstFreeTrieNode=0; setTrieNodeComponent(m_firstFreeTrieNode,TRIE_NODE_NEXT_SIBLING,-1); } public int getTrieNodeComponent(int trieNode,int component) { return m_indexPages[trieNode / TRIE_NODE_PAGE_SIZE][(trieNode % TRIE_NODE_PAGE_SIZE)*TRIE_NODE_SIZE+component]; } public void setTrieNodeComponent(int trieNode,int component,int value) { m_indexPages[trieNode / TRIE_NODE_PAGE_SIZE][(trieNode % TRIE_NODE_PAGE_SIZE)*TRIE_NODE_SIZE+component]=value; } public Object getTrieNodeObject(int trieNode) { return m_objectPages[trieNode / TRIE_NODE_PAGE_SIZE][trieNode % TRIE_NODE_PAGE_SIZE]; } public void setTrieNodeObject(int trieNode,Object object) { m_objectPages[trieNode / TRIE_NODE_PAGE_SIZE][trieNode % TRIE_NODE_PAGE_SIZE]=object; } public void initializeTrieNode(int trieNode,int parent,int firstChild,int previousSibling,int nextSibling,int nextEntry,Object object) { int pageIndex=trieNode / TRIE_NODE_PAGE_SIZE; int indexInPage=trieNode % TRIE_NODE_PAGE_SIZE; int[] indexPage=m_indexPages[pageIndex]; int start=indexInPage*TRIE_NODE_SIZE; indexPage[start+TRIE_NODE_PARENT]=parent; indexPage[start+TRIE_NODE_FIRST_CHILD]=firstChild; indexPage[start+TRIE_NODE_PREVIOUS_SIBLING]=previousSibling; indexPage[start+TRIE_NODE_NEXT_SIBLING]=nextSibling; indexPage[start+TRIE_NODE_NEXT_ENTRY]=nextEntry; m_objectPages[pageIndex][indexInPage]=object; } public int newTrieNode() { int newTrieNode=m_firstFreeTrieNode; int nextFreeTrieNode=getTrieNodeComponent(m_firstFreeTrieNode,TRIE_NODE_NEXT_SIBLING); if (nextFreeTrieNode!=-1) m_firstFreeTrieNode=nextFreeTrieNode; else { m_firstFreeTrieNode++; if (m_firstFreeTrieNode<0) throw new OutOfMemoryError("The space of nodes in TupleIndex was exhausted: the ontology is just too large."); int pageIndex=m_firstFreeTrieNode / TRIE_NODE_PAGE_SIZE; if (pageIndex>=m_numberOfPages) { if (pageIndex>=m_indexPages.length) { int[][] newIndexPages=new int[m_indexPages.length*3/2][]; System.arraycopy(m_indexPages,0,newIndexPages,0,m_indexPages.length); m_indexPages=newIndexPages; Object[][] newObjectPages=new Object[m_objectPages.length*3/2][]; System.arraycopy(m_objectPages,0,newObjectPages,0,m_objectPages.length); m_objectPages=newObjectPages; } m_indexPages[pageIndex]=new int[TRIE_NODE_SIZE*TRIE_NODE_PAGE_SIZE]; m_objectPages[pageIndex]=new Object[TRIE_NODE_PAGE_SIZE]; m_numberOfPages++; } setTrieNodeComponent(m_firstFreeTrieNode,TRIE_NODE_NEXT_SIBLING,-1); } return newTrieNode; } public void deleteTrieNode(int trieNode) { setTrieNodeComponent(trieNode,TRIE_NODE_NEXT_SIBLING,m_firstFreeTrieNode); setTrieNodeObject(trieNode,null); m_firstFreeTrieNode=trieNode; } } public static class TupleIndexRetrieval implements Serializable { private static final long serialVersionUID=3052986474027614595L; protected final TupleIndex m_tupleIndex; protected final Object[] m_bindingsBuffer; protected final int[] m_selectionIndices; protected final int m_selectionIndicesLength; protected final int m_indexingSequenceLength; protected int m_currentTrieNode; public TupleIndexRetrieval(TupleIndex tupleIndex,Object[] bindingsBuffer,int[] selectionIndices) { m_tupleIndex=tupleIndex; m_bindingsBuffer=bindingsBuffer; m_selectionIndices=selectionIndices; m_selectionIndicesLength=m_selectionIndices.length; m_indexingSequenceLength=tupleIndex.m_indexingSequence.length; } public void open() { m_currentTrieNode=m_tupleIndex.m_root; for (int position=0;position<m_selectionIndicesLength;position++) { Object object=m_bindingsBuffer[m_selectionIndices[position]]; m_currentTrieNode=m_tupleIndex.getChildNode(m_currentTrieNode,object); if (m_currentTrieNode==-1) return; } if (m_selectionIndicesLength==0 && m_tupleIndex.m_trieNodeManager.getTrieNodeComponent(m_tupleIndex.m_root,TRIE_NODE_FIRST_CHILD)==-1) m_currentTrieNode=-1; else { for (int index=m_selectionIndicesLength;index<m_indexingSequenceLength;index++) m_currentTrieNode=m_tupleIndex.m_trieNodeManager.getTrieNodeComponent(m_currentTrieNode,TRIE_NODE_FIRST_CHILD); } } public boolean afterLast() { return m_currentTrieNode==-1; } public int getCurrentTupleIndex() { return m_tupleIndex.m_trieNodeManager.getTrieNodeComponent(m_currentTrieNode,TRIE_NODE_TUPLE_INDEX); } public void next() { int trieNodeDepth=m_indexingSequenceLength; while (trieNodeDepth!=m_selectionIndicesLength && m_tupleIndex.m_trieNodeManager.getTrieNodeComponent(m_currentTrieNode,TRIE_NODE_NEXT_SIBLING)==-1) { m_currentTrieNode=m_tupleIndex.m_trieNodeManager.getTrieNodeComponent(m_currentTrieNode,TRIE_NODE_PARENT); trieNodeDepth--; } if (trieNodeDepth==m_selectionIndicesLength) m_currentTrieNode=-1; else { m_currentTrieNode=m_tupleIndex.m_trieNodeManager.getTrieNodeComponent(m_currentTrieNode,TRIE_NODE_NEXT_SIBLING); for (int index=trieNodeDepth;index<m_indexingSequenceLength;index++) m_currentTrieNode=m_tupleIndex.m_trieNodeManager.getTrieNodeComponent(m_currentTrieNode,TRIE_NODE_FIRST_CHILD); } } } }