/** * Bobo Browse Engine - High performance faceted/parametric search implementation * that handles various types of semi-structured data. Written in Java. * * Copyright (C) 2005-2006 Spackle * * This library 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 2.1 of the License, or (at your option) any later version. * * This library 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * To contact the project administrators for the bobo-browse project, * please go to https://sourceforge.net/projects/bobo-browse/, or * send mail to owner@browseengine.com. */ package com.browseengine.bobo.util; import java.util.BitSet; /** * @author spackle * */ public class SparseFloatArray { float[] _floats; BitSet _bits; /** * the number of bits set BEFORE the given reference point index*REFERENCE_POINT_EVERY. */ int[] _referencePoints; private int _capacity; private static final float ON_RATIO_CUTOFF = 0.75f; /** * 32 is 32 bits per 256 floats, which is the same as the 32 bits per 32 floats that are needed * in _bits. */ static final int REFERENCE_POINT_EVERY = 32;// 256; /** * Good for saving memory with sparse float arrays, when those arrays no longer need to be mutable. * * requires: floats never changes after this method is called returns. * in fact, you should lose all references to it, since this object * might save you a lot of memory. * * @param floats */ public SparseFloatArray(float[] floats) { _capacity = floats.length; condense(floats); } /** * Short-cut to quickly create a sparse float array representing * <code>this(new float[capacity]);</code>, but without reading through said array. * The advantage here is that the constructor is lightning-fast in the case that * all values in the float array are known to * <pre> * == 0f * <pre> * . * * @param capacity */ public SparseFloatArray(int capacity) { _capacity = capacity; _floats = null; _bits = null; _referencePoints = null; } void condense(float[] floats) { if (floats.length != _capacity) { throw new IllegalArgumentException("bad input float array of length " + floats.length + " for capacity: " + _capacity); } BitSet bits = new BitSet(floats.length); int on = 0; for (int i = 0; i < floats.length; i++) { if (floats[i] != 0f) { bits.set(i); on++; } } if (((float) on) / ((float) floats.length) < ON_RATIO_CUTOFF) { // it's worth compressing if (0 == on) { // it's worth super-compressing _floats = null; _bits = null; _referencePoints = null; // capacity is good. } else { _bits = bits; _floats = new float[_bits.cardinality()]; _referencePoints = new int[floats.length / REFERENCE_POINT_EVERY]; int i = 0; int floatsIdx = 0; int refIdx = 0; while (i < floats.length && (i = _bits.nextSetBit(i)) >= 0) { _floats[floatsIdx] = floats[i]; while (refIdx < i / REFERENCE_POINT_EVERY) { _referencePoints[refIdx++] = floatsIdx; } floatsIdx++; i++; } while (refIdx < _referencePoints.length) { _referencePoints[refIdx++] = floatsIdx; } } } else { // it's not worth compressing _floats = floats; _bits = null; } } /** * warning: DO NOT modify the return value at all. * the assumption is that these arrays are QUITE LARGE and that we would not want * to unnecessarily copy them. this method in many cases returns an array from its * internal representation. doing anything other than READING these values * results in UNDEFINED operations on this, from that point on. * */ public float[] expand() { if (null == _bits) { if (null == _floats) { // super-compressed, all zeros return new float[_capacity]; } else { return _floats; } } float[] all = new float[_capacity]; int floatsidx = 0; for (int idx = _bits.nextSetBit(0); idx >= 0 && idx < _capacity; idx = _bits .nextSetBit(idx + 1)) { all[idx] = _floats[floatsidx++]; } return all; } public float get(int idx) { if (null == _bits) { if (null == _floats) { // super-compressed, all zeros if (idx < 0 || idx >= _capacity) { throw new ArrayIndexOutOfBoundsException("bad index: " + idx + " for SparseFloatArray representing array of length " + _capacity); } return 0f; } else { return _floats[idx]; } } else { if (_bits.get(idx)) { // count the number of bits that are on BEFORE this idx int count; int ref = idx / REFERENCE_POINT_EVERY - 1; if (ref >= 0) { count = _referencePoints[ref]; } else { count = 0; } int i = idx - idx % REFERENCE_POINT_EVERY; while ((i = _bits.nextSetBit(i)) >= 0 && i < idx) { count++; i++; } return _floats[count]; } else { return 0f; } } } }