package net.seninp.jmotif.sax.registry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
/**
* The convenient way to keep track of visited locations. Note that a new unvisited location is
* searched for by random generator-backed search. which may take time... sometimes :).
*
* @author Pavel Senin.
*/
public class VisitRegistry implements Cloneable {
private static final byte ZERO = 0;
private static final byte ONE = 1;
protected byte[] registry; // 1 visited, 0 unvisited
private int unvisitedCount; // unvisited counter
private final Random randomizer = new Random(System.currentTimeMillis());
/**
* Constructor.
*
* @param capacity The initial capacity.
*/
public VisitRegistry(int capacity) {
super();
this.registry = new byte[capacity];
this.unvisitedCount = capacity;
}
/**
* Disabling the default constructor.
*/
@SuppressWarnings("unused")
private VisitRegistry() {
super();
}
/**
* Marks location visited. If it was unvisited, counter decremented.
*
* @param loc The location to mark.
*/
public void markVisited(int loc) {
if (checkBounds(loc)) {
if (ZERO == this.registry[loc]) {
this.unvisitedCount--;
}
this.registry[loc] = ONE;
}
else {
throw new RuntimeException(
"The location " + loc + " out of bounds [0," + (this.registry.length - 1) + "]");
}
}
/**
* Marks as visited a range of locations.
*
* @param from the start of labeling (inclusive).
* @param upTo the end of labeling (exclusive).
*/
public void markVisited(int from, int upTo) {
if (checkBounds(from) && checkBounds(upTo - 1)) {
for (int i = from; i < upTo; i++) {
this.markVisited(i);
}
}
else {
throw new RuntimeException("The location " + from + "," + upTo + " out of bounds [0,"
+ (this.registry.length - 1) + "]");
}
}
/**
* Get the next random unvisited position.
*
* @return The next unvisited position.
*/
public int getNextRandomUnvisitedPosition() {
// if all are visited, return -1
//
if (0 == this.unvisitedCount) {
return -1;
}
// if there is space continue with random sampling
//
int i = this.randomizer.nextInt(this.registry.length);
while (ONE == registry[i]) {
i = this.randomizer.nextInt(this.registry.length);
}
return i;
}
/**
* Check if position is not visited.
*
* @param loc The index.
* @return true if not visited.
*/
public boolean isNotVisited(int loc) {
if (checkBounds(loc)) {
return (ZERO == this.registry[loc]);
}
else {
throw new RuntimeException(
"The location " + loc + " out of bounds [0," + (this.registry.length - 1) + "]");
}
}
/**
* Check if the interval and its boundaries were visited.
*
* @param from The interval start (inclusive).
* @param upTo The interval end (exclusive).
* @return True if visited.
*/
public boolean isVisited(int from, int upTo) {
if (checkBounds(from) && checkBounds(upTo - 1)) {
// perform the visit check
//
for (int i = from; i < upTo; i++) {
if (ONE == this.registry[i]) {
return true;
}
}
return false;
}
else {
throw new RuntimeException("The location " + from + "," + upTo + " out of bounds [0,"
+ (this.registry.length - 1) + "]");
}
}
/**
* Check if the location specified is visited.
*
* @param loc the location.
* @return true if visited
*/
public boolean isVisited(int loc) {
if (checkBounds(loc)) {
return (ONE == this.registry[loc]);
}
else {
throw new RuntimeException(
"The location " + loc + " out of bounds [0," + (this.registry.length - 1) + "]");
}
}
/**
* Get the list of unvisited positions.
*
* @return list of unvisited positions.
*/
public ArrayList<Integer> getUnvisited() {
if (0 == this.unvisitedCount) {
return new ArrayList<Integer>();
}
ArrayList<Integer> res = new ArrayList<Integer>(this.unvisitedCount);
for (int i = 0; i < this.registry.length; i++) {
if (ZERO == this.registry[i]) {
res.add(i);
}
}
return res;
}
/**
* Get the list of visited positions. Returns NULL if none are visited.
*
* @return list of visited positions.
*/
public ArrayList<Integer> getVisited() {
if (0 == (this.registry.length - this.unvisitedCount)) {
return new ArrayList<Integer>();
}
ArrayList<Integer> res = new ArrayList<Integer>(this.registry.length - this.unvisitedCount);
for (int i = 0; i < this.registry.length; i++) {
if (ONE == this.registry[i]) {
res.add(i);
}
}
return res;
}
/**
* Transfers all visited entries from another registry to current.
*
* @param discordRegistry The discords registry to copy from.
*/
public void transferVisited(VisitRegistry discordRegistry) {
for (int v : discordRegistry.getVisited()) {
this.markVisited(v);
}
}
/**
* Creates the copy of a registry.
*
* @return the complete copy.
* @throws CloneNotSupportedException if error occurs.
*/
public VisitRegistry clone() throws CloneNotSupportedException {
VisitRegistry res = (VisitRegistry) super.clone();
res.unvisitedCount = this.unvisitedCount;
res.registry = Arrays.copyOfRange(this.registry, 0, this.registry.length);
return res;
}
/**
* The registry size.
*
* @return the registry size.
*/
public int size() {
return this.registry.length;
}
/**
* Check the bounds.
*
* @param pos the pos to check.
* @return true if within the bounds.
*/
private boolean checkBounds(int pos) {
if (pos < 0 || pos >= this.registry.length) {
return false;
}
return true;
}
public int getUnvisitedCount() {
return this.unvisitedCount;
}
}