/*
 * Copyright (c) 2012-2013, Peter Abeles. All Rights Reserved.
 *
 * This file is part of DDogleg (http://ddogleg.org).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ddogleg.nn.alg;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

import static org.junit.Assert.assertTrue;

/**
 * @author Peter Abeles
 */
public class TestKdTreeConstructor {

	/**
	 * Makes sure a branch is created correctly given the results from the splitter
	 */
	@Test
	public void computeBranch() {
		DummySplitter splitter = createSplitter(1,1,false);
		splitter.splitAxis = 1;
		splitter.splitPoint = new double[]{2,3};

		KdTreeConstructor alg = new KdTreeConstructor(new KdTreeMemory(),2,splitter);

		KdTree.Node n = alg.computeBranch(new ArrayList(),null);

		assertTrue(n.point == splitter.splitPoint);
		assertTrue(n.split == splitter.splitAxis);
		assertTrue(n.data == null);
		assertTrue(n.left.isLeaf());
		assertTrue(n.right.isLeaf());
		assertTrue(n.left.point == splitter.left.get(0));
		assertTrue(n.left.data == null);
		assertTrue(n.right.point == splitter.right.get(0));
		assertTrue(n.right.data == null);
	}

	/**
	 * Same test as above but with associated data
	 */
	@Test
	public void computeBranch_widthData() {
		DummySplitter splitter = createSplitter(1,1,true);
		splitter.splitAxis = 1;
		splitter.splitPoint = new double[]{2,3};
		splitter.splitData = 2;

		KdTreeConstructor alg = new KdTreeConstructor(new KdTreeMemory(),2,splitter);

		KdTree.Node n = alg.computeBranch(new ArrayList(),new ArrayList());

		assertTrue(n.point == splitter.splitPoint);
		assertTrue(n.split == splitter.splitAxis);
		assertTrue(n.data == splitter.splitData);
		assertTrue(n.left.isLeaf());
		assertTrue(n.right.isLeaf());
		assertTrue(n.left.point == splitter.left.get(0));
		assertTrue(n.left.data == splitter.leftData.get(0));
		assertTrue(n.right.point == splitter.right.get(0));
		assertTrue(n.right.data == splitter.rightData.get(0));
	}

	@Test
	public void computeChild() {
		KdTreeConstructor alg = new KdTreeConstructor(2);

		List points = new ArrayList();
		List data = new ArrayList();

		// empty lists should be null
		KdTree.Node n = new KdTree.Node();
		n.point = new double[2];
		n.data = 1;
		KdTree.Node found = alg.computeChild(points,data);
		assertTrue(found == null);

		// add a point
		points.add( new double[2] );
		data.add(2);
		found = alg.computeChild(points,data);
		assertTrue(found.isLeaf());
		assertTrue(found.point == points.get(0));
		assertTrue(found.data == data.get(0));

		// for all the other cases it will create a branch.  testing that will require a bit more work...
	}

	/**
	 * Basic tests to see if it can handle different sized input lists.
	 */
	@Test
	public void construct() {
		KdTreeConstructor alg = new KdTreeConstructor(new KdTreeMemory(),2,createSplitter(1,1,false));

		// test an empty list
		List points = new ArrayList();
		KdTree tree = alg.construct(points,null);
		assertTrue(tree.N == 2);
		assertTrue(tree.root == null);

		// add a point
		points.add( new double[]{1,2});
		tree = alg.construct(points,null);
		assertTrue(tree.N == 2);
		assertTrue(tree.root.point == points.get(0));
		assertTrue(tree.root.isLeaf());

		// add another point.  These input points are ignored by the dummy splitter
		points.add( new double[]{1,2,4,5});
		tree = alg.construct(points,null);
		assertTrue(tree.N == 2);
		assertTrue(tree.root.left.isLeaf());
		assertTrue(tree.root.right.isLeaf());
	}



	public static List createPoints( int dimen , double ...v ) {

		List ret = new ArrayList();

		for( int i = 0; i < v.length; i += dimen ) {
			double p[] = new double[dimen];
			for( int j = 0; j < dimen; j++ ) {
				p[j] = v[i+j];
			}
			ret.add(p);
		}

		return ret;
	}

	private DummySplitter createSplitter( int numLeft , int numRight , boolean withData) {
		List left = new ArrayList();
		List right = new ArrayList();
		List leftData = null;
		List rightData = null;

		for( int i = 0; i < numLeft; i++ ) {
			left.add( new double[2] );
		}
		for( int i = 0; i < numRight; i++ ) {
			right.add( new double[2] );
		}

		if( withData ) {
			leftData = new ArrayList();
			rightData = new ArrayList();

			for( int i = 0; i < numLeft; i++ ) {
				leftData.add( i );
			}
			for( int i = 0; i < numRight; i++ ) {
				rightData.add( i );
			}
		}


		return new DummySplitter(null,null,1,left,leftData,right,rightData);
	}

	public static class DummySplitter implements AxisSplitter {

		boolean calledSetDimension = false;
		Integer splitData;
		double[] splitPoint;
		int splitAxis;
		List left;
		List leftData;
		List right;
		List rightData;

		public DummySplitter(Integer splitData, double[] splitPoint, int splitAxis,
							 List left, List leftData,
							 List right, List rightData)
		{
			this.splitData = splitData;
			this.splitPoint = splitPoint;
			this.splitAxis = splitAxis;
			this.left = left;
			this.leftData = leftData;
			this.right = right;
			this.rightData = rightData;
		}

		@Override
		public void setDimension(int N) {
			calledSetDimension = true;
		}

		@Override
		public void splitData(List points, List data,
							  List left, List leftData,
							  List right, List rightData)
		{
			left.addAll(this.left);
			right.addAll(this.right);
			if( leftData != null )
				leftData.addAll(this.leftData);
			if( rightData != null )
				rightData.addAll(this.rightData);
		}

		@Override
		public double[] getSplitPoint() {
			return splitPoint;
		}

		@Override
		public Integer getSplitData() {
			return splitData;
		}

		@Override
		public int getSplitAxis() {
			return splitAxis;
		}
	}

}

 

Ads help maintain this website.