<commit_before>/*******************************************************************************
 *
 *    Copyright (C) 2015-2017 the BBoxDB project
 *  
 *    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.bboxdb.storage.entity;

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

import org.bboxdb.util.DataEncoderHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BoundingBox implements Comparable<BoundingBox> {
	
	public final static BoundingBox EMPTY_BOX = new BoundingBox();
	
	/**
	 * The boundingBox contains a interval for each dimension
	 */
	protected final List<DoubleInterval> boundingBox;
	
	/**
	 * The return value of an invalid dimension
	 */
	public final static int INVALID_DIMENSION = -1;
	
	/**
	 * The min value
	 */
	public final static double MIN_VALUE = -Float.MAX_VALUE;
	
	/**
	 * The max value
	 */
	public final static double MAX_VALUE = Float.MAX_VALUE;
	
	/**
	 * The Logger
	 */
	protected static final Logger logger = LoggerFactory.getLogger(BoundingBox.class);
	
	/**
	 * Create from Double
	 * @param args
	 */
	public BoundingBox(final Double... args) {
		
		if(args.length % 2 != 0) {
			throw new IllegalArgumentException("Even number of arguments expected");
		}
		
		boundingBox = new ArrayList<DoubleInterval>(args.length / 2);
				
		for(int i = 0; i < args.length; i = i + 2) {
			final DoubleInterval interval = new DoubleInterval(args[i], args[i+1]);
			boundingBox.add(interval);
		}				
	}
	
	/**
	 * Create from double[]
	 * @param args
	 */
	public BoundingBox(final double[] values) {
		
		if(values.length % 2 != 0) {
			throw new IllegalArgumentException("Even number of arguments expected");
		}
		
		boundingBox = new ArrayList<DoubleInterval>(values.length / 2);
		
		for(int i = 0; i < values.length; i = i + 2) {
			final DoubleInterval interval = new DoubleInterval(values[i], values[i+1]);
			boundingBox.add(interval);
		}				
	}
	
	/**
	 * Create from List<DoubleInterval>
	 * @param args
	 */
	public BoundingBox(final List<DoubleInterval> values) {
		boundingBox = new ArrayList<DoubleInterval>(values);
	}

	/**
	 * Returns the size of the bounding box in bytes
	 * 
	 * @return
	 */
	public int getSize() {
		return boundingBox.size();
	}
	
	/**
	 * Convert the bounding box into a byte array
	 * 
	 * @return
	 */
	public byte[] toByteArray() {
		final double[] values = toDoubleArray();
		
		return DataEncoderHelper.doubleArrayToByteBuffer(values).array();
	}

	/**
	 * Convert the bounding box into a double array
	 * 
	 * @return
	 */
	public double[] toDoubleArray() {
		final double[] values = new double[boundingBox.size() * 2];
		for(int i = 0; i < boundingBox.size(); i++) {
			values[2*i] = boundingBox.get(i).getBegin();
			values[(2*i)+1] = boundingBox.get(i).getEnd();
		}
		return values;
	}
	
	/**
	 * Read the bounding box from a byte array
	 * @param boxBytes
	 * @return
	 */
	public static BoundingBox fromByteArray(final byte[] boxBytes) {
		final double[] doubleArray = DataEncoderHelper.readDoubleArrayFromByte(boxBytes);
		return new BoundingBox(doubleArray);
	}
	
	/**
	 * Create a bounding box the covers the full space for n dimensions
	 * @param dimension
	 * @return
	 */
	public static BoundingBox createFullCoveringDimensionBoundingBox(final int dimension) {
		if(dimension <= 0) {
			throw new IllegalArgumentException("Unable to create full covering bounding box for dimension: " + dimension);
		}
		
		final List<DoubleInterval> dimensions = new ArrayList<DoubleInterval>();
		
		for(int i = 0; i < dimension; i++) {
			dimensions.add(new DoubleInterval(MIN_VALUE, MAX_VALUE));
		}
		
		return new BoundingBox(dimensions);
	}
	
	/**
	 * Tests if two bounding boxes share some space
	 * @param otherBoundingBox
	 * @return
	 */
	public boolean overlaps(final BoundingBox otherBoundingBox) {
		
		// Null does overlap with nothing
		if(otherBoundingBox == null) {
			return false;
		}
		
		// The empty bounding box overlaps everything
		if(this == BoundingBox.EMPTY_BOX) {
			return true;
		}
		
		// The empty bounding box overlaps everything
		if(otherBoundingBox == BoundingBox.EMPTY_BOX) {
			return true;
		}
		
		// An other instance of the bounding box
		if(otherBoundingBox.getDimension() == 0) {
			return true;
		}
		
		// Both boxes are equal (Case 5)
		if(equals(otherBoundingBox)) {
			return true;
		}
		
		// Dimensions are not equal
		if(otherBoundingBox.getDimension() != getDimension()) {
			return false;
		}
		
		// Check the overlapping in each dimension d
		for(int d = 0; d < getDimension(); d++) {
			
			final DoubleInterval ourInterval = boundingBox.get(d);
			final DoubleInterval otherInterval = otherBoundingBox.getIntervalForDimension(d);
			
			if(! ourInterval.isOverlappingWith(otherInterval)) {
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * Does the bounding box covers the point in the dimension?
	 * @param point
	 * @param dimension
	 * @return
	 */
	public boolean isCoveringPointInDimension(final double point, final int dimension) {
		
		if(dimension >= boundingBox.size()) {
			throw new IllegalArgumentException("Wrong dimension : " + dimension + " we have only " + boundingBox.size() + " dimensions");
		}
		
		final DoubleInterval dimensionInterval = boundingBox.get(dimension);
		return dimensionInterval.isNumberIncluded(point);
	}
	
	/**
	 * Get the extent for the dimension
	 * @param dimension
	 * @return
	 */
	public double getExtent(final int dimension) {
		return boundingBox.get(dimension).getEnd() - boundingBox.get(dimension).getBegin();
	}
	
	/**
	 * Get the double interval for the given dimension
	 * @param dimension
	 * @return
	 */
	public DoubleInterval getIntervalForDimension(final int dimension) {
		return boundingBox.get(dimension);
	}
	
	/**
	 * The the lowest coordinate for the dimension
	 * @param dimension
	 * @return
	 */
	public double getCoordinateLow(final int dimension) {
		return boundingBox.get(dimension).getBegin();
	}
	
	/**
	 * The the highest coordinate for the dimension
	 * @param dimension
	 * @return
	 */
	public double getCoordinateHigh(final int dimension) {
		return boundingBox.get(dimension).getEnd();
	}
	
	/**
	 * Return the dimension of the bounding box
	 * @return
	 */
	public int getDimension() {
		return boundingBox.size();
	}
	
	/**
	 * Split the bounding box at the given position and get the left part
	 * @param splitPosition
	 * @param splitPositionIncluded
	 * @return
	 */
	public BoundingBox splitAndGetLeft(final double splitPosition, 
			final int splitDimension, final boolean splitPositionIncluded) {
		
		if(splitDimension > getDimension() - 1) {
			throw new IllegalArgumentException("Unable to split a bounding box with " + getDimension() + " dimensions in dimension" + splitDimension);
		}
		
		if(! isCoveringPointInDimension(splitPosition, splitDimension)) {
			throw new IllegalArgumentException("Unable to split, point " + splitPosition + " is not covered in dimension " + splitDimension + " " + boundingBox.get(splitDimension));
		}
		
		final List<DoubleInterval> intervals = new ArrayList<DoubleInterval>(boundingBox);
		final DoubleInterval splitInterval = intervals.get(splitDimension);
		final DoubleInterval newInterval = splitInterval.splitAndGetLeftPart(splitPosition, splitPositionIncluded);
		intervals.set(splitDimension, newInterval);
		return new BoundingBox(intervals);
	}
	
	/**
	 * Split the bounding box at the given position and get the right part
	 * @param splitPosition
	 * @param splitPositionIncluded
	 * @return
	 */
	public BoundingBox splitAndGetRight(final double splitPosition, final int splitDimension, final boolean splitPositionIncluded) {
		if(splitDimension > getDimension()) {
			throw new IllegalArgumentException("Unable to split a bounding box with " + getDimension() + " dimensions in dimension" + splitDimension);
		}
		
		if(! isCoveringPointInDimension(splitPosition, splitDimension)) {
			throw new IllegalArgumentException("Unable to split, point " + splitDimension + " is not covered in dimension " + splitDimension);
		}
		
		final List<DoubleInterval> intervals = new ArrayList<DoubleInterval>(boundingBox);
		final DoubleInterval splitInterval = intervals.get(splitDimension);
		final DoubleInterval newInterval = splitInterval.splitAndGetRightPart(splitPosition, splitPositionIncluded);
		intervals.set(splitDimension, newInterval);
		return new BoundingBox(intervals);
	}

	/**
	 * Convert to a readable string
	 * 
	 */
	@Override
	public String toString() {
		final StringBuilder sb = new StringBuilder("BoundingBox [dimensions=");
		sb.append(getDimension());
		
		for(int d = 0; d < getDimension(); d++) {
			sb.append(", dimension ");
			sb.append(d);
			sb.append(" low: ");
			sb.append(getCoordinateLow(d));
			sb.append(" high: ");
			sb.append(getCoordinateHigh(d));
		}
				
		sb.append("]");
		return sb.toString();
	}

	/**
	 * Convert into a hashcode
	 * 
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((boundingBox == null) ? 0 : boundingBox.hashCode());
		return result;
	}

	/**
	 * Is equals with an other object
	 * 
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		BoundingBox other = (BoundingBox) obj;
		if (boundingBox == null) {
			if (other.boundingBox != null)
				return false;
		} else if (!boundingBox.equals(other.boundingBox))
			return false;
		return true;
	}

	/**
	 * Compare to an other boudning box
	 */
	@Override
	public int compareTo(final BoundingBox otherBox) {
		
		// Check number od dimensions
		if(getDimension() != otherBox.getDimension()) {
			return getDimension() - otherBox.getDimension(); 
		}
		
		// Check start point of each dimension
		for(int d = 0; d < getDimension(); d++) {
			if(getCoordinateLow(d) != otherBox.getCoordinateLow(d)) {
				if(getCoordinateLow(d) > otherBox.getCoordinateLow(d)) {
					return 1;
				} else {
					return -1;
				}
			}
		}
		
		// Objects are equal
		return 0;
	}
	
	/**
	 * Get the bounding box of two bounding boxes
	 * @param boundingBox1
	 * @param boundingBox2
	 * @return
	 */
	public static BoundingBox getCoveringBox(final BoundingBox... boundingBoxes) {
		
		// No argument
		if(boundingBoxes.length == 0) {
			return BoundingBox.EMPTY_BOX;
		}
		
		// Only 1 argument
		if(boundingBoxes.length == 1) {
			return boundingBoxes[0];
		}
		
		int dimensions = boundingBoxes[0].getDimension();
		
		// All bounding boxes need the same dimension
		for(int i = 1 ; i < boundingBoxes.length; i++) {
			
			final BoundingBox currentBox = boundingBoxes[i];
			
			// Bounding box could be null, e.g. for DeletedTuple instances
			if(currentBox == null) {
				continue;
			}
			
			// Don't merge the empty box
			if(currentBox == EMPTY_BOX) {
				continue;
			}
			
			if(dimensions != currentBox.getDimension()) {
				logger.error("Merging bounding boxes with different dimensions: " + dimensions + "/" + currentBox.getDimension());
				logger.error("Box 0: " + boundingBoxes[0]);
				logger.error("Other box: " + currentBox);
				return null;
			}
		}
		
		// Array with data for the result box<commit_msg>Replaced null return with exception (found by coverity)<commit_after>/*******************************************************************************
 *
 *    Copyright (C) 2015-2017 the BBoxDB project
 *  
 *    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.bboxdb.storage.entity;

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

import org.bboxdb.util.DataEncoderHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BoundingBox implements Comparable<BoundingBox> {
	
	public final static BoundingBox EMPTY_BOX = new BoundingBox();
	
	/**
	 * The boundingBox contains a interval for each dimension
	 */
	protected final List<DoubleInterval> boundingBox;
	
	/**
	 * The return value of an invalid dimension
	 */
	public final static int INVALID_DIMENSION = -1;
	
	/**
	 * The min value
	 */
	public final static double MIN_VALUE = -Float.MAX_VALUE;
	
	/**
	 * The max value
	 */
	public final static double MAX_VALUE = Float.MAX_VALUE;
	
	/**
	 * The Logger
	 */
	protected static final Logger logger = LoggerFactory.getLogger(BoundingBox.class);
	
	/**
	 * Create from Double
	 * @param args
	 */
	public BoundingBox(final Double... args) {
		
		if(args.length % 2 != 0) {
			throw new IllegalArgumentException("Even number of arguments expected");
		}
		
		boundingBox = new ArrayList<DoubleInterval>(args.length / 2);
				
		for(int i = 0; i < args.length; i = i + 2) {
			final DoubleInterval interval = new DoubleInterval(args[i], args[i+1]);
			boundingBox.add(interval);
		}				
	}
	
	/**
	 * Create from double[]
	 * @param args
	 */
	public BoundingBox(final double[] values) {
		
		if(values.length % 2 != 0) {
			throw new IllegalArgumentException("Even number of arguments expected");
		}
		
		boundingBox = new ArrayList<DoubleInterval>(values.length / 2);
		
		for(int i = 0; i < values.length; i = i + 2) {
			final DoubleInterval interval = new DoubleInterval(values[i], values[i+1]);
			boundingBox.add(interval);
		}				
	}
	
	/**
	 * Create from List<DoubleInterval>
	 * @param args
	 */
	public BoundingBox(final List<DoubleInterval> values) {
		boundingBox = new ArrayList<DoubleInterval>(values);
	}

	/**
	 * Returns the size of the bounding box in bytes
	 * 
	 * @return
	 */
	public int getSize() {
		return boundingBox.size();
	}
	
	/**
	 * Convert the bounding box into a byte array
	 * 
	 * @return
	 */
	public byte[] toByteArray() {
		final double[] values = toDoubleArray();
		
		return DataEncoderHelper.doubleArrayToByteBuffer(values).array();
	}

	/**
	 * Convert the bounding box into a double array
	 * 
	 * @return
	 */
	public double[] toDoubleArray() {
		final double[] values = new double[boundingBox.size() * 2];
		for(int i = 0; i < boundingBox.size(); i++) {
			values[2*i] = boundingBox.get(i).getBegin();
			values[(2*i)+1] = boundingBox.get(i).getEnd();
		}
		return values;
	}
	
	/**
	 * Read the bounding box from a byte array
	 * @param boxBytes
	 * @return
	 */
	public static BoundingBox fromByteArray(final byte[] boxBytes) {
		final double[] doubleArray = DataEncoderHelper.readDoubleArrayFromByte(boxBytes);
		return new BoundingBox(doubleArray);
	}
	
	/**
	 * Create a bounding box the covers the full space for n dimensions
	 * @param dimension
	 * @return
	 */
	public static BoundingBox createFullCoveringDimensionBoundingBox(final int dimension) {
		if(dimension <= 0) {
			throw new IllegalArgumentException("Unable to create full covering bounding box for dimension: " + dimension);
		}
		
		final List<DoubleInterval> dimensions = new ArrayList<DoubleInterval>();
		
		for(int i = 0; i < dimension; i++) {
			dimensions.add(new DoubleInterval(MIN_VALUE, MAX_VALUE));
		}
		
		return new BoundingBox(dimensions);
	}
	
	/**
	 * Tests if two bounding boxes share some space
	 * @param otherBoundingBox
	 * @return
	 */
	public boolean overlaps(final BoundingBox otherBoundingBox) {
		
		// Null does overlap with nothing
		if(otherBoundingBox == null) {
			return false;
		}
		
		// The empty bounding box overlaps everything
		if(this == BoundingBox.EMPTY_BOX) {
			return true;
		}
		
		// The empty bounding box overlaps everything
		if(otherBoundingBox == BoundingBox.EMPTY_BOX) {
			return true;
		}
		
		// An other instance of the bounding box
		if(otherBoundingBox.getDimension() == 0) {
			return true;
		}
		
		// Both boxes are equal (Case 5)
		if(equals(otherBoundingBox)) {
			return true;
		}
		
		// Dimensions are not equal
		if(otherBoundingBox.getDimension() != getDimension()) {
			return false;
		}
		
		// Check the overlapping in each dimension d
		for(int d = 0; d < getDimension(); d++) {
			
			final DoubleInterval ourInterval = boundingBox.get(d);
			final DoubleInterval otherInterval = otherBoundingBox.getIntervalForDimension(d);
			
			if(! ourInterval.isOverlappingWith(otherInterval)) {
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * Does the bounding box covers the point in the dimension?
	 * @param point
	 * @param dimension
	 * @return
	 */
	public boolean isCoveringPointInDimension(final double point, final int dimension) {
		
		if(dimension >= boundingBox.size()) {
			throw new IllegalArgumentException("Wrong dimension : " + dimension + " we have only " + boundingBox.size() + " dimensions");
		}
		
		final DoubleInterval dimensionInterval = boundingBox.get(dimension);
		return dimensionInterval.isNumberIncluded(point);
	}
	
	/**
	 * Get the extent for the dimension
	 * @param dimension
	 * @return
	 */
	public double getExtent(final int dimension) {
		return boundingBox.get(dimension).getEnd() - boundingBox.get(dimension).getBegin();
	}
	
	/**
	 * Get the double interval for the given dimension
	 * @param dimension
	 * @return
	 */
	public DoubleInterval getIntervalForDimension(final int dimension) {
		return boundingBox.get(dimension);
	}
	
	/**
	 * The the lowest coordinate for the dimension
	 * @param dimension
	 * @return
	 */
	public double getCoordinateLow(final int dimension) {
		return boundingBox.get(dimension).getBegin();
	}
	
	/**
	 * The the highest coordinate for the dimension
	 * @param dimension
	 * @return
	 */
	public double getCoordinateHigh(final int dimension) {
		return boundingBox.get(dimension).getEnd();
	}
	
	/**
	 * Return the dimension of the bounding box
	 * @return
	 */
	public int getDimension() {
		return boundingBox.size();
	}
	
	/**
	 * Split the bounding box at the given position and get the left part
	 * @param splitPosition
	 * @param splitPositionIncluded
	 * @return
	 */
	public BoundingBox splitAndGetLeft(final double splitPosition, 
			final int splitDimension, final boolean splitPositionIncluded) {
		
		if(splitDimension > getDimension() - 1) {
			throw new IllegalArgumentException("Unable to split a bounding box with " + getDimension() + " dimensions in dimension" + splitDimension);
		}
		
		if(! isCoveringPointInDimension(splitPosition, splitDimension)) {
			throw new IllegalArgumentException("Unable to split, point " + splitPosition + " is not covered in dimension " + splitDimension + " " + boundingBox.get(splitDimension));
		}
		
		final List<DoubleInterval> intervals = new ArrayList<DoubleInterval>(boundingBox);
		final DoubleInterval splitInterval = intervals.get(splitDimension);
		final DoubleInterval newInterval = splitInterval.splitAndGetLeftPart(splitPosition, splitPositionIncluded);
		intervals.set(splitDimension, newInterval);
		return new BoundingBox(intervals);
	}
	
	/**
	 * Split the bounding box at the given position and get the right part
	 * @param splitPosition
	 * @param splitPositionIncluded
	 * @return
	 */
	public BoundingBox splitAndGetRight(final double splitPosition, final int splitDimension, final boolean splitPositionIncluded) {
		if(splitDimension > getDimension()) {
			throw new IllegalArgumentException("Unable to split a bounding box with " + getDimension() + " dimensions in dimension" + splitDimension);
		}
		
		if(! isCoveringPointInDimension(splitPosition, splitDimension)) {
			throw new IllegalArgumentException("Unable to split, point " + splitDimension + " is not covered in dimension " + splitDimension);
		}
		
		final List<DoubleInterval> intervals = new ArrayList<DoubleInterval>(boundingBox);
		final DoubleInterval splitInterval = intervals.get(splitDimension);
		final DoubleInterval newInterval = splitInterval.splitAndGetRightPart(splitPosition, splitPositionIncluded);
		intervals.set(splitDimension, newInterval);
		return new BoundingBox(intervals);
	}

	/**
	 * Convert to a readable string
	 * 
	 */
	@Override
	public String toString() {
		final StringBuilder sb = new StringBuilder("BoundingBox [dimensions=");
		sb.append(getDimension());
		
		for(int d = 0; d < getDimension(); d++) {
			sb.append(", dimension ");
			sb.append(d);
			sb.append(" low: ");
			sb.append(getCoordinateLow(d));
			sb.append(" high: ");
			sb.append(getCoordinateHigh(d));
		}
				
		sb.append("]");
		return sb.toString();
	}

	/**
	 * Convert into a hashcode
	 * 
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((boundingBox == null) ? 0 : boundingBox.hashCode());
		return result;
	}

	/**
	 * Is equals with an other object
	 * 
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		BoundingBox other = (BoundingBox) obj;
		if (boundingBox == null) {
			if (other.boundingBox != null)
				return false;
		} else if (!boundingBox.equals(other.boundingBox))
			return false;
		return true;
	}

	/**
	 * Compare to an other boudning box
	 */
	@Override
	public int compareTo(final BoundingBox otherBox) {
		
		// Check number od dimensions
		if(getDimension() != otherBox.getDimension()) {
			return getDimension() - otherBox.getDimension(); 
		}
		
		// Check start point of each dimension
		for(int d = 0; d < getDimension(); d++) {
			if(getCoordinateLow(d) != otherBox.getCoordinateLow(d)) {
				if(getCoordinateLow(d) > otherBox.getCoordinateLow(d)) {
					return 1;
				} else {
					return -1;
				}
			}
		}
		
		// Objects are equal
		return 0;
	}
	
	/**
	 * Get the bounding box of two bounding boxes
	 * @param boundingBox1
	 * @param boundingBox2
	 * @return
	 */
	public static BoundingBox getCoveringBox(final BoundingBox... boundingBoxes) {
		
		// No argument
		if(boundingBoxes.length == 0) {
			return BoundingBox.EMPTY_BOX;
		}
		
		// Only 1 argument
		if(boundingBoxes.length == 1) {
			return boundingBoxes[0];
		}
		
		int dimensions = boundingBoxes[0].getDimension();
		
		// All bounding boxes need the same dimension
		for(int i = 1 ; i < boundingBoxes.length; i++) {
			
			final BoundingBox currentBox = boundingBoxes[i];
			
			// Bounding box could be null, e.g. for DeletedTuple instances
			if(currentBox == null) {
				continue;
			}
			
			// Don't merge the empty box
			if(currentBox == EMPTY_BOX) {
				continue;
			}
			
			if(dimensions != currentBox.getDimension()) {
				final String errorMessage = "Merging bounding boxes with different dimensions: " 
						+ dimensions + "/" + currentBox.getDimension()
						+ " Box 0: " + boundingBoxes[0]
						+ " Other box: " + currentBox;
				
				throw new IllegalArgumentException(errorMessage);
			}
		}
		
		// Array with data for the result box
{'fraction_non_alphanumeric': 0.12119937760208588, 'fraction_numerical': 0.0047941460952941675, 'mean_word_length': 3.564299424184261, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6957569', 'n_tokens_mistral': 8025, 'n_tokens_neox': 6759, 'n_words': 2048}