/*
* This file is part of MoleculeViewer.
*
* MoleculeViewer 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.
*
* MoleculeViewer 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 MoleculeViewer. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Class for performing near neighbour calculations.
*/
package astex;
import it.unimi.dsi.fastutil.ints.IntArrayList;
public class Lattice {
/** List of cells to search for all pairs search. */
private static final int offsets[][] = {
{ 1, -1, 0 },
{ 1, 0, 0 },
{ 1, 1, 0 },
{ 0, 1, 0 },
{ -1, -1, 1 },
{ 0, -1, 1 },
{ 1, -1, 1 },
{ -1, 0, 1 },
{ 0, 0, 1 },
{ 1, 0, 1 },
{ -1, 1, 1 },
{ 0, 1, 1 },
{ 1, 1, 1 }
};
/** Maximum distance we will search. */
private double maxDistance = -1.0;
/** Set the maximum search distance. */
private void setMaximumDistance(double d){
Log.check(d > 0.0, "search distance must be > 0.0");
if(maxDistance > 0.0){
Log.error("can't reset maximum distance");
}else{
maxDistance = d;
}
}
/** Return the maximum search distance. */
public double getMaximumDistance(){
return maxDistance;
}
/** Create an empty lattice object. */
public Lattice(double d){
setMaximumDistance(d);
}
/** Add an object reference to the lattice. */
public void add(int id, double x, double y, double z){
int i = BOX(x);
int j = BOX(y);
int k = BOX(z);
// map into an internal id range
// so that we can trivially handle
// non-contiguous ids
int actualId = ids.size();
ids.add(id);
int c = findcell(i, j, k);
if(c == -1){
int hashval = HASH(i, j, k);
IntArrayList cellList = hashTable[hashval];
if(cellList == null){
cellList = new IntArrayList();
hashTable[hashval] = cellList;
}
c = celli.size();
cellList.add(c);
// -1 indicates the cell is currently empty
// this will get changed below
head.add(-1);
celli.add(i);
cellj.add(j);
cellk.add(k);
}
// shuffle the object id's
// to add the new object to the cell chain
list.add(head.getInt(c));
head.set(c, actualId);
}
/** Get the contents of the cell. */
public int getCellContents(int icell, IntArrayList c){
if(icell == -1){
return 0;
}
int j = head.getInt(icell);
if(j == -1){
return 0;
}
while(j >= 0){
c.add(ids.getInt(j));
j = list.getInt(j);
}
return c.size();
}
/** Find the cell that corresponds to the id's. */
private int findcell(int i, int j, int k){
int hashval = HASH(i, j, k);
// search list with this hashval, if not present return -1
IntArrayList cellList = hashTable[hashval];
if(cellList != null){
int ci[] = celli.toIntArray();
int cj[] = cellj.toIntArray();
int ck[] = cellk.toIntArray();
int cl[] = cellList.toIntArray();
int cellEntries = cellList.size();
for(int c = 0; c < cellEntries; c++){
int cellIndex = cl[c];
if(ci[cellIndex] == i &&
cj[cellIndex] == j &&
ck[cellIndex] == k){
return cellIndex;
}
}
}
return -1;
}
/**
* Return the possible neighbours of the point.
*/
public int getPossibleNeighbours(int id,
double x, double y, double z,
IntArrayList neighbours,
boolean allNeighbours){
int ibox = BOX(x);
int jbox = BOX(y);
int kbox = BOX(z);
int h[] = head.toIntArray();
int l[] = list.toIntArray();
int idsArray[] = ids.toIntArray();
for(int i = -1; i <= 1; i++){
int ii = ibox + i;
for(int j = -1; j <= 1; j++){
int jj = jbox + j;
for(int k = -1; k <= 1; k++){
int kk = kbox + k;
int c = findcell(ii, jj, kk);
if(c != -1){
int iobj = h[c];
if(iobj != -1){
if(allNeighbours){
if(id == Undefined){
while(iobj >= 0){
neighbours.add(idsArray[iobj]);
iobj = l[iobj];
}
}else{
while(iobj >= 0){
// don't put ourselves
// in the list of neighbours
if(idsArray[iobj] != id){
neighbours.add(idsArray[iobj]);
}
iobj = l[iobj];
}
}
}else{
while(iobj >= 0){
// don't put things less than us
// in the list of neighbours
if(idsArray[iobj] > id){
neighbours.add(idsArray[iobj]);
}
iobj = l[iobj];
}
}
}
}
}
}
}
return neighbours.size();
}
/** Working space for cell objects gathers. */
private IntArrayList cell1 = new IntArrayList();
private IntArrayList cell2 = new IntArrayList();
/** Get the possible pairs of neighbours from a cell. */
public int getPossibleCellNeighbours(int cid, IntArrayList objects){
cell1.clear();
getCellContents(cid, cell1);
int count1 = cell1.size();
int c1[] = cell1.toIntArray();
for(int i = 0; i < count1; i++){
int oi = c1[i];
for(int j = 0; j < count1; j++){
if(i != j){
int oj = c1[j];
if(oi < oj){
objects.add(oi);
objects.add(oj);
}
}
}
}
int icell = celli.getInt(cid);
int jcell = cellj.getInt(cid);
int kcell = cellk.getInt(cid);
for(int ioff = 0; ioff < offsets.length; ioff++){
int ii = icell + offsets[ioff][0];
int jj = jcell + offsets[ioff][1];
int kk = kcell + offsets[ioff][2];
int c = findcell(ii, jj, kk);
if(c != -1){
cell2.clear();
getCellContents(c, cell2);
int count2 = cell2.size();
int c2[] = cell2.toIntArray();
for(int i = 0; i < count1; i++){
int oi = c1[i];
for(int j = 0; j < count2; j++){
int oj = c2[j];
objects.add(ids.getInt(oi));
objects.add(ids.getInt(oj));
}
}
}
}
return objects.size();
}
/** Print info about the Lattice object. */
public void printStatistics(int info){
int occupiedHashSlots = 0;
int minCells = Integer.MAX_VALUE;
int maxCells = Integer.MIN_VALUE;
int zeroCells = 0;
for(int i = 0; i < HASHTABLESIZE; i++){
if(hashTable[i] != null){
occupiedHashSlots++;
IntArrayList cellList = hashTable[i];
int cellCount = cellList.size();
if(cellCount > maxCells){
maxCells = cellCount;
}
if(cellCount < minCells){
minCells = cellCount;
}
}else{
zeroCells++;
}
}
FILE.out.print("hash table size %5d\n", HASHTABLESIZE);
FILE.out.print("occupied cells %5d\n", occupiedHashSlots);
FILE.out.print("zero cells %5d\n", zeroCells);
FILE.out.print("max cells/slot %5d\n", maxCells);
FILE.out.print("ave cells/slot %7.1f\n",
(double)celli.size()/(double)HASHTABLESIZE);
}
/** Return hash value for object cell. */
private int HASH(int i, int j, int k){
if(i < 0) i = -i;
if(j < 0) j = -j;
if(k < 0) k = -k;
return (i&HS_MASK) | ((j&HS_MASK) << SHIFT) | ((k&HS_MASK) << SHIFT2);
}
/** Return the cell box id along one axis. */
public int BOX(double x){
if(x > 0.0)
return (int)(x/maxDistance);
return (int)(x/maxDistance)-1;
}
/** Size of Hash box. */
private static final int HS = 16;
/** Mask for mapping into Hash box range. */
private static final int HS_MASK = HS - 1;
/** Shift for mapping into Hash box range [ln2(HS)]. */
private static final int SHIFT = 4;
/** Shift for mapping into Hash box range [ln2(HS)]. */
private static final int SHIFT2 = 2 * SHIFT;
/** Size of hashtable. */
private static final int HASHTABLESIZE = HS*HS*HS;
/** Table of indexes to cells. */
private IntArrayList hashTable[] = new IntArrayList[HASHTABLESIZE];
/** The cell indexes for each cell. */
private IntArrayList celli = new IntArrayList();
private IntArrayList cellj = new IntArrayList();
private IntArrayList cellk = new IntArrayList();
/** The head and list pointers for each cell. */
private IntArrayList head = new IntArrayList();
private IntArrayList list = new IntArrayList();
/** Mapping from passed ids to internal ids. */
private IntArrayList ids = new IntArrayList();
/** Constant to indicate we don't care about ids. */
public final static int Undefined = Integer.MIN_VALUE;
}