All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.ethz.globis.phtree.v11.nt.NtNode Maven / Gradle / Ivy

There is a newer version: 2.8.1
Show newest version
/*
 * Copyright 2011-2016 ETH Zurich. All Rights Reserved.
 *
 * This software is the proprietary information of ETH Zurich.
 * Use is subject to license terms.
 */
package ch.ethz.globis.phtree.v11.nt;

import ch.ethz.globis.phtree.util.Refs;
import ch.ethz.globis.phtree.util.RefsLong;


/**
 * Node of the nested PH-tree.
 * 
 * @author ztilmann
 *
 * @param  value type
 */
public class NtNode {
	
	/**
	 * All nodes have MAX_DIM as DIM. 
	 * This may not be correct for all sub-nodes, but it does no harm and simplifies
	 * algorithms that iterator over the tree (avoid switching dimensions, etc).
	 * 
	 * WARNING using MAX_DIM bigger than 15 will fail because entryCnt is of type 'short'! 
	 */
	public static final int MAX_DIM = 6;
	private static final long MAX_DIM_MASK = ~((-1L) << MAX_DIM);
	
	//size of references in bytes
	private static final int REF_BITS = 4*8;

	static final int HC_BITS = 0;  //number of bits required for storing current (HC)-representation
	static final int INN_HC_WIDTH = 0; //Index-NotNull: width of not-null flag for post/infix-hc

	private Object[] values;
	private short entryCnt = 0;
	long[] ba = null;
	private long[] kdKeys = null;
	private boolean isAHC = false;
	private byte postLen = 0;

	static final int IK_WIDTH(int dims) { return dims; } //post index key width 
	static final int KD_WIDTH(int kdDims) { return kdDims * 64; } //post index key width 

	protected NtNode(NtNode original) {
        this.values = Refs.arrayClone(original.values);
        this.entryCnt = original.entryCnt;
        this.isAHC = original.isAHC;
        this.postLen = original.postLen;
        this.ba = Bits.arrayClone(original.ba);
        this.kdKeys = RefsLong.arrayClone(original.kdKeys);
    }

	private NtNode() {
		//nothing
	}

	static  NtNode createNode(NtNode original) {
		return new NtNode<>(original);
	}

	@SuppressWarnings("unchecked")
	public static  NtNode createRoot(int keyBitWidth) {
		NtNode n = (NtNode) NtNodePool.getNode();
		n.initNode(calcTreeHeight(keyBitWidth) - 1, keyBitWidth);
		return n;
	}
	
	@SuppressWarnings("unchecked")
	static  NtNode createNode(int postLen, int keyBitWidth) {
		NtNode n = (NtNode) NtNodePool.getNode();
		n.initNode(postLen, keyBitWidth);
		return n;
	}
	
	/**
	 * To be used by the NtNodePool only.
	 * @return returns an empty node.
	 */
	static  NtNode createEmptyNode() {
		return new NtNode<>();
	}
	
	void initNode(int postLen, int keyBitWidth) {
		this.postLen = (byte) postLen;
		this.entryCnt = 0;
		this.isAHC = false;
		int size = calcArraySizeTotalBits(2, MAX_DIM);
		this.ba = Bits.arrayCreate(size);
		this.kdKeys = RefsLong.arrayCreate(calcArraySizeTotalLongs(2, MAX_DIM, keyBitWidth));
		this.values = Refs.arrayCreate(2);
	}
	
	void discardNode() {
		Bits.arrayReplace(ba, null);
		RefsLong.arrayReplace(kdKeys, null);
		Refs.arrayReplace(values, null);
		NtNodePool.offer(this);
	}
	
	int calcArraySizeTotalBits(int entryCount, final int dims) {
		int nBits = getBitPosIndex();
		//post-fixes
		if (isAHC()) {
			//hyper-cube
			nBits += (INN_HC_WIDTH + dims * postLen) * (1 << dims);
		} else {
			//hc-pos index
			nBits += entryCount * (IK_WIDTH(dims) + dims * postLen);
		}
		return nBits;
	}

	int calcArraySizeTotalLongs(int entryCount, final int dims, final int kdDims) {
		return isAHC() ? 
				//hyper-cube
				kdDims * (1 << dims)
				:
				//LHC index
				kdDims * entryCount;
	}

	//
	//  NtNode stuff
	//
	
	static int calcTreeHeight(int k) {
		return (k-1) / MAX_DIM + 1;
	}
	
	/**
	 * This conversion has to be made such that the z-order of all entries is
	 * preserved.
	 * Therefore, we have to keep higher-order bits in the top-level nodes of the
	 * tree. As a downside, it requires additional effort to have 2^MAX_DIM
	 * children in the root node if the dimension is not a multiple of MAX_DIM.
	 * 
	 * @param hcPos
	 * @param postLen
	 * @return The local hcPos / z-value.
	 */
	static long pos2LocalPos(long hcPos, int postLen) {
		return (hcPos >>> (postLen*MAX_DIM)) & MAX_DIM_MASK;
	}

	static long pos2LocalPosNegative(long hcPos, int postLen) {
		return hcPos >> (postLen*MAX_DIM);
	}
	
    long localReadInfix(int pin, long localHcPos) {
		int infixBits = getPostLen() * MAX_DIM;
		int infixPos = pinToOffsBitsData(pin, localHcPos, MAX_DIM); 
		return Bits.readArray(ba, infixPos, infixBits);
	}

    long localReadPostfix(int pin, long localHcPos) {
		int postBits = getPostLen() * MAX_DIM;
		int postPos = pinToOffsBitsData(pin, localHcPos, MAX_DIM); 
		return Bits.readArray(ba, postPos, postBits);
	}

    long localReadAndApplyReadPostfixAndHc(int pin, long localHcPos, long prefix) {
		int postBits = getPostLen() * MAX_DIM;
		int postPos = pinToOffsBitsData(pin, localHcPos, MAX_DIM); 
		long postFix = Bits.readArray(ba, postPos, postBits);
 		long mask = (-1L) << postBits; //  = 111100000000
 		mask <<= MAX_DIM;
     	return (prefix & mask) | (localHcPos << postBits) | postFix;
     }

    long localReadKey(int pin) {
    	if (isAHC()) {
    		return pin;
    	}
		int keyPos = pinToOffsBitsLHC(pin, getBitPosIndex(), MAX_DIM); 
		return Bits.readArray(ba, keyPos, MAX_DIM);
	}

    void localAddEntry(long localHcPos, long postFix, long[] kdKey, Object value) {
    	int pin = getPosition(localHcPos, MAX_DIM);
    	localAddEntryPIN(pin, localHcPos, postFix, kdKey, value);
    }
    
    void localAddEntryPIN(int pin, long localHcPos, long postFix, long[] key, Object value) {
    	addEntryPIN(localHcPos, pin, postFix, key, value, MAX_DIM);
    }
    
    void localReplaceEntryWithSub(int pin, long localHcPos, long hcPos, NtNode newSub) {
    	//let's write the whole postfix...
		int totalInfixLen = getPostLen() * MAX_DIM;
		int infixPos = pinToOffsBitsData(pin, localHcPos, MAX_DIM);
		Bits.writeArray(ba, infixPos, totalInfixLen, hcPos);
		
    	replaceValueSub(pin, newSub);
    }
    
    Object localReplaceEntry(int pin, long[] kdKey, Object newValue) {
    	return replaceEntry(pin, kdKey, newValue);
    }

	/**
	 * Replace an value in a node without modifying the postfix or infix.
	 */
    Object localReplaceValue(int pin, Object newValue) {
		Object ret = values[pin];
		values[pin] = newValue;
		return ret;
    }

	
	static int getConflictingLevels(long key, long infix, int postLen) {
		if (postLen == 0) {
			return 0;
		}
		int endInclusive = postLen * MAX_DIM - 1;
		int confBits = Bits.getMaxConflictingBits(key, infix, endInclusive);
		return (confBits + MAX_DIM - 1) / MAX_DIM;
	}

	static boolean hasConflictingLevels(long key, long infix, int postLen) {
		if (postLen == 0) {
			return false;
		}
		int endInclusive = postLen * MAX_DIM - 1;
		return Bits.hasConflictingBits(key, infix, endInclusive);
	}

    /**
     * Calculates the number of conflicting tree-levels, consisting of the most significant level
     * and all levels 'below' it (all less significant levels).
     * @param key value 1
     * @param infix value 2
     * @param parentPostLen bits to check (inclusive). Counting from right (least significant bit).
     * @param subPostLen bits to ignore (exclusive; less than parentPostLen)
     * @return Number of conflicting bits or 0 if none.
     */
	static int getConflictingLevels(long key, long infix, int parentPostLen, int subPostLen) {
		int subInfixLen = parentPostLen-subPostLen-1;
		if (subInfixLen == 0) {
			return 0;
		}
		int startExclusive = (subPostLen+1) * MAX_DIM;
		int endInclusive = parentPostLen * MAX_DIM - 1;
		int confBits = Bits.getMaxConflictingBits(key, infix, startExclusive, endInclusive);
		return (confBits + MAX_DIM - 1) / MAX_DIM;
	}

	static boolean hasConflictingLevels(long key, long infix, int parentPostLen, int subPostLen) {
		int subInfixLen = parentPostLen-subPostLen-1;
		if (subInfixLen == 0) {
			return false;
		}
		int startExclusive = (subPostLen+1) * MAX_DIM;
		int endInclusive = parentPostLen * MAX_DIM - 1;
		return Bits.hasConflictingBits(key, infix, startExclusive, endInclusive);
	}

    static long applyHcPos(long localHcPos, int postLen, long prefix) {
		long mask = ~((-1L) << MAX_DIM); // =000000001111
		int postLen2 = postLen*MAX_DIM;
		mask = ~(mask << postLen2); //  = 111100001111
    	return (prefix & mask) | (localHcPos << postLen2);
    }
    
    long applyHcPos(long localHcPos, long prefix) {
		long mask = ~((-1L) << MAX_DIM); // =000000001111
		int postLen2 = postLen*MAX_DIM;
		mask = ~(mask << postLen2); //  = 111100001111
    	return (prefix & mask) | (localHcPos << postLen2);
    }
    
    static long applyPostFix(long postFix, int postLen, long prefix) {
		int postLen2 = postLen*MAX_DIM;
		long mask = ~((-1L) << postLen2); //  = 111100000000
    	return (prefix & mask) | postFix;
    }
    
    static long applyHcAndPostFix(long localHcPos, long postFix, int postLen, long prefix) {
 		int postLen2 = postLen*MAX_DIM;
 		long mask = ~((-1L) << postLen2); //  = 111100000000
 		mask <<= MAX_DIM;
     	return (prefix & mask) | (localHcPos << postLen2) | postFix;
     }
	
	
	//
	// Normal Node stuff
	//
	

	/**
	 * @param posInNode
	 * @return The sub node or null.
	 */
	Object getValueByPIN(int posInNode) {
		return values[posInNode];
	}


	/**
	 * @param posInNode
	 * @param outKey Result container.
	 * @return The sub node or null.
	 */
	Object getEntryByPIN(int posInNode, long[] outKey) {
		RefsLong.readArray(kdKeys, pinToKdPos(posInNode, outKey.length), outKey);
		return values[posInNode];
	}


	/**
	 * @param posInNode
	 * @param outKey Result container.
	 */
	void getKdKeyByPIN(int posInNode, long[] outKey) {
		RefsLong.readArray(kdKeys, pinToKdPos(posInNode, outKey.length), outKey);
	}


	private boolean shouldSwitchToAHC(int entryCount, int dims, int kdDims) {
		return useAHC(entryCount, dims, kdDims);
	}
	
	private boolean shouldSwitchToLHC(int entryCount, int dims, int kdDims) {
		return !useAHC(entryCount+2, dims, kdDims);
	}
	
	private boolean useAHC(int entryCount, int dims, int kdDims) {
		//calc post mode.
		//+1 bit for null/not-null flag
		long sizeAHC = (dims * postLen + INN_HC_WIDTH + REF_BITS + KD_WIDTH(kdDims)) * (1L << dims); 
		//+DIM because every index entry needs DIM bits
		long sizeLHC = (dims * postLen + IK_WIDTH(dims) + REF_BITS + KD_WIDTH(kdDims)) 
				* (long)entryCount;
		return NodeTreeV11.AHC_ENABLED && (dims<=31) && (sizeLHC*1.5 >= sizeAHC);
	}

	void replacePost(int pin, long hcPos, long newKey, int dims) {
		int offs = pinToOffsBitsData(pin, hcPos, dims);
		Bits.writeArray(ba, offs, postLen*dims, newKey);
	}

	/**
	 * Replace an value in a node without modifying the postfix or infix.
	 */
	void replaceValueSub(int pin, NtNode sub) {
		values[pin] = sub;
	}
	
	/**
	 * Replace an value in a node without modifying the postfix or infix.
	 */
	Object replaceEntry(int pin, long[] kdKey, Object val) {
		Object ret = values[pin];
		RefsLong.writeArray(kdKey, kdKeys, pinToKdPos(pin, kdKey.length));
		values[pin] = val;
		return ret;
	}

	void readKdKeyPIN(int pin, long[] outKdKey) {
		RefsLong.readArray(kdKeys, pin*outKdKey.length, outKdKey);
	}
	
	boolean readKdKeyAndCheck(int pin, long[] keyToMatch, long mask) {
		int pos = pin*keyToMatch.length;
		for (int i = 0; i < keyToMatch.length; i++) {
			if (((kdKeys[pos++] ^ keyToMatch[i]) & mask) != 0) {
				return false;
			}
		}
		return true;
	}
	
	private void switchLhcToAhcAndGrow(int oldEntryCount, int dims, int kdDims) {
		int posOfIndex = getBitPosIndex();
		int posOfData = posToOffsBitsDataAHC(0, posOfIndex, dims);
		setAHC( true );
		long[] bia2 = Bits.arrayCreate(calcArraySizeTotalBits(oldEntryCount+1, dims));
		long[] kdKeys2 = 
				RefsLong.arrayCreate(calcArraySizeTotalLongs(oldEntryCount+1, dims, kdDims));
		Object [] v2 = Refs.arrayCreate(1< Initially, the linear version is always smaller, because the cube has at least
		//    two entries, even for a single dimension. (unless DIM > 2*REF=2*32 bit 
		//    For one dimension, both need one additional bit to indicate either
		//    null/not-null (hypercube, actually two bit) or to indicate the index. 

		//switch representation (HC <-> Linear)?
		if (!isAHC() && shouldSwitchToAHC(bufEntryCnt + 1, dims, kdKey.length)) {
			switchLhcToAhcAndGrow(bufEntryCnt, dims, kdKey.length);
		}

		incEntryCount();

		int offsIndex = getBitPosIndex();
		if (isAHC()) {
			//hyper-cube
			int offsPostKey = posToOffsBitsDataAHC(hcPos, offsIndex, dims);
			Bits.writeArray(ba, offsPostKey, postLen*dims, key);
			int kdPos = posToKdPosAHC(hcPos, kdDims);
			RefsLong.writeArray(kdKey, kdKeys, kdPos);
			values[(int) hcPos] = value;
		} else {
			//get position
			int pin = -(negPin+1);

			//resize array
			ba = Bits.arrayEnsureSize(ba, calcArraySizeTotalBits(bufEntryCnt+1, dims));
			long[] ia = ba;
			int offs = pinToOffsBitsLHC(pin, offsIndex, dims);
			Bits.insertBits(ia, offs, IK_WIDTH(dims) + dims*postLen);
			//insert key
			Bits.writeArray(ia, offs, IK_WIDTH(dims), hcPos);
			//insert kdKey
			int kdPos = posToKdPosLHC(pin, kdDims);
			kdKeys = RefsLong.insertArray(kdKeys, kdKey, kdPos);
			//insert value:
			offs += IK_WIDTH(dims);
			Bits.writeArray(ia, offs, postLen*dims, key);
			values = Refs.insertSpaceAtPos(values, pin, bufEntryCnt+1);
			values[pin] = value;
		}
	}

	void getPostPIN(int posInNode, long hcPos, long[] key) {
		long[] ia = ba;
		int offs = pinToOffsBitsData(posInNode, hcPos, key.length);
		final long mask = (~0L)< Linear)?
		if (isAHC() && shouldSwitchToLHC(bufEntryCnt, dims, kdDims)) {
			//revert to linearized representation, if applicable
			Object oldVal = switchAhcToLhcAndShrink(bufEntryCnt, dims, kdDims, hcPos);
			decEntryCount();
			return oldVal;
		}			

		Object oldVal;
		int offsIndex = getBitPosIndex();
		if (isAHC()) {
			//hyper-cube
			oldVal = values[(int) hcPos]; 
			values[(int) hcPos] = null;
			//Nothing else to do, values can just stay where they are
		} else {
			//linearized cube:
			//remove key and value
			int posBit = pinToOffsBitsLHC(posInNode, offsIndex, dims);
			Bits.removeBits(ba, posBit, IK_WIDTH(dims) + dims*postLen);
			//shrink array
			ba = Bits.arrayTrim(ba, calcArraySizeTotalBits(bufEntryCnt-1, dims));
			//values:
			oldVal = values[posInNode]; 
			values = Refs.removeSpaceAtPos(values, posInNode, bufEntryCnt-1);
			//kdKey
			int kdPos = posToKdPosLHC(posInNode, kdDims);
			kdKeys = RefsLong.arrayRemove(kdKeys, kdPos, kdDims);
		}

		decEntryCount();

		return oldVal;
	}


	/**
	 * @return True if the post-fixes are stored as array hyper-cube
	 */
	boolean isAHC() {
		return isAHC;
	}


	/**
	 * Set whether the post-fixes are stored as array hyper-cube.
	 */
	void setAHC(boolean b) {
		isAHC = b;
	}

	/**
	 * @return entry counter
	 */
	int getEntryCount() {
		return entryCnt;
	}


	private void decEntryCount() {
		--entryCnt;
	}


	private void incEntryCount() {
		entryCnt++;
	}


	int getBitPosIndex() {
		return getBitPosInfix();
	}

	int getBitPosInfix() {
		return HC_BITS;
	}


	private int posToOffsBitsDataAHC(long hcPos, int offsIndex, int dims) {
		return offsIndex + INN_HC_WIDTH * (1<




© 2015 - 2024 Weber Informatics LLC | Privacy Policy