package mikera.indexz;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import mikera.vectorz.AVector;
import mikera.vectorz.impl.IndexVector;
import mikera.vectorz.util.ErrorMessages;
import mikera.vectorz.util.IntArrays;
import mikera.vectorz.util.VectorzException;
/**
* Class to represent a mutable list of integer indexes, typically used for indexing into
* vectors or matrices.
*
* Backed by an int[] array.
*
* @author Mike
*
*/
public final class Index extends AIndex {
private static final long serialVersionUID = 8698831088064498284L;
public static final Index EMPTY = new Index(0);
public final int[] data;
public Index(int length) {
this(new int[length]);
}
private Index(int[] indexes) {
data=indexes;
}
/**
* Creates an Index using the values from the given ArrayList.
*
* Values are cast to integers as needed, according to the semantics of (int)value
*
* @param v
* @return
*/
public static Index create(ArrayList<Integer> v) {
int n=v.size();
Index ind=new Index(n);
for (int i=0; i<n ; i++) {
ind.data[i]=v.get(i);
}
return ind;
}
public static Index createSorted(Set<Integer> keySet) {
ArrayList<Integer> al=new ArrayList<Integer>();
al.addAll(keySet);
Collections.sort(al);
return create(al);
}
public static Index createSorted(SortedSet<Integer> keySet) {
int[] rs=new int[keySet.size()];
int i=0;
for (Integer x:keySet) {
rs[i++]=x;
}
if (i!=rs.length) throw new VectorzException(ErrorMessages.impossible());
return new Index(rs);
}
/**
* Creates an Index using the values from the given AVector.
*
* Values are cast to integers as needed, according to the semantics of (int)value
*
* @param v
* @return
*/
public static Index create(AVector v) {
int n=v.length();
Index ind=new Index(n);
for (int i=0; i<n ; i++) {
ind.data[i]=(int)v.unsafeGet(i);
}
return ind;
}
/**
* Creates a new Index, wrapping the provided index array
*/
public static Index wrap(int[] indexes) {
return new Index(indexes);
}
/**
* Creates a new Index, using the specified index values
*/
public static Index of(int... indexes) {
return new Index(indexes.clone());
}
/**
* Create a new zero-filled Index with the specified length
*/
public static Index createLength(int len) {
return new Index(len);
}
/**
* Swaps (in-place) the indexes at two positions
*/
@Override
public void swap(int i, int j) {
int t=data[i];
data[i]=data[j];
data[j]=t;
}
/**
* Reverses an index
*/
@Override
public void reverse() {
final int len=length();
int m=len/2;
for (int i=0; i<m; i++) {
swap(i,(len-1)-i);
}
}
@Override
public boolean isDistinctSorted() {
int len=length();
for (int i=1; i<len; i++) {
if (data[i-1]>=data[i]) return false;
}
return true;
}
@Override
public boolean isSorted() {
int len=length();
for (int i=1; i<len; i++) {
if (data[i-1]>data[i]) return false;
}
return true;
}
@Override
public boolean isPermutation() {
int n=length();
if (n>=64) {
return isLongPermutation();
} else {
return isShortPermutation();
}
}
private boolean isShortPermutation() {
int n=length();
long chk=0;
for (int i=0; i<n; i++) {
int v=data[i];
if ((v<0)||(v>=n)) return false;
chk=chk|(1L<<v);
}
return (chk+1)==(1L<<n);
}
private boolean isLongPermutation() {
int n=length();
boolean[] chk=new boolean[n];
for (int i=0; i<n; i++) {
int v=data[i];
if ((v<0)||(v>=n)||chk[v]) return false;
chk[v]=true;
}
for (int i=0; i<n; i++) {
if (!chk[i]) return false;
}
return true;
}
public Index includeSorted(Set<Integer> is) {
TreeSet<Integer> ss=new TreeSet<Integer>(this.toSet());
for (Integer i:is) {
ss.add(i);
}
return createSorted(ss);
}
public Index includeSorted(Index ind) {
TreeSet<Integer> ss=new TreeSet<Integer>(this.toSet());
for (Integer i:ind) {
ss.add(i);
}
return createSorted(ss);
}
public Set<Integer> toSet() {
TreeSet<Integer> ss=new TreeSet<Integer>();
for (int i=0; i<data.length; i++) {
ss.add(data[i]);
}
return ss;
}
public SortedSet<Integer> toSortedSet() {
TreeSet<Integer> ss=new TreeSet<Integer>();
for (int i=0; i<data.length; i++) {
ss.add(data[i]);
}
return ss;
}
/**
* Counts the number of swaps required to create this permutation.
*
* The index must represent a permutation, or the behaviour is undefined.
*
* @return
*/
public int swapCount() {
if (length()<=64) {
return swapCountSmall();
} else {
return swapCountLong();
}
}
private int swapCountLong() {
int n=length();
int swaps=0;
BitSet seen=new BitSet(n);
for (int i=0; i<n; i++) {
if (seen.get(i)) continue;
seen.set(i);
for(int j=data[i]; !seen.get(j); j=data[j]) {
seen.set(j);
swaps++;
}
}
return swaps;
}
private int swapCountSmall() {
int n=length();
int swaps=0;
long seen=0;
for (int i=0; i<n; i++) {
long mask=(1L<<i);
if ((seen&mask)!=0) continue;
seen|=mask;
for(int j=data[i]; (seen&(1L<<j))==0; j=data[j]) {
seen|=(1L<<j);
swaps++;
}
}
return swaps;
}
public boolean isOddPermutation() {
return (swapCount()&1)==1;
}
public boolean isEvenPermutation() {
return (swapCount()&1)==0;
}
@Override
public int get(int i) {
return data[i];
}
public int unsafeGet(int i) {
return data[i];
}
@Override
public void set(int i, int value) {
data[i]=value;
}
@Override
public int length() {
return data.length;
}
@Override
public Index clone() {
return new Index(IntArrays.copyOf(data));
}
/**
* Permutes this vector according to a given permutation index
* @param permutationIndex
*/
public void permute(Index permutationIndex) {
int len=length();
assert(len==permutationIndex.length());
int[] temp=data.clone();
for (int i=0; i<len; i++) {
data[i]=temp[permutationIndex.get(i)];
}
}
/**
* Sorts the Index (in-place)
*/
@Override
public void sort() {
Arrays.sort(data);
}
public AVector asVector() {
return IndexVector.wrap(this);
}
@Override
public boolean equals(Object o) {
if (o instanceof Index) return equals((Index)o);
return super.equals(o);
}
public boolean equals(Index o) {
int len=length();
if (len!=o.length()) return false;
for (int i=0; i<len; i++) {
if (data[i]!=o.data[i]) return false;
}
return true;
}
public int[] getData() {
return data;
}
@Override
public int[] toArray() {
return getData().clone();
}
@Override
public boolean isFullyMutable() {
return true;
}
public void lookupWith(Index source) {
int len=length();
for (int i=0; i<len; i++) {
data[i]=source.data[data[i]];
}
}
@Override
public Index compose(AIndex a) {
if (a instanceof Index) return compose((Index)a);
return super.compose(a);
}
public Index compose(Index a) {
int len=this.length();
Index r=new Index(len);
for (int i=0; i<len; i++) {
r.data[i]=a.data[data[i]];
}
return r;
}
/**
* Looks up an index value in the index, returning its position or -1 if not found
* Index must be both sorted and distinct.
* @param i
* @return
*/
public int indexPosition(int i) {
int min=0; int max=data.length;
while (min<max) {
int mid=(min+max)>>1;
int mi=data[mid];
if (i==mi) return mid;
if (i<mi) {
max=mid;
} else {
min=mid+1;
}
}
return -1;
}
@Override
public boolean containsSorted(int index) {
return indexPosition(index)>=0;
}
/**
* Returns a new Index with a value inserted at the specified position
*/
public Index insert(int position, int value) {
return new Index(IntArrays.insert(data,position,value));
}
/**
* Finds the position a value would take assuming a sorted index. Uses a binary search.
* @param i The position of the value - will point to either the value or the next higher value present
* @return
*/
public int seekPosition(int i) {
int min=0; int max=data.length;
while (min<max) {
int mid=(min+max)>>1;
int mi=data[mid];
if (i==mi) return mid;
if (i<mi) {
max=mid;
} else {
min=mid+1;
}
}
return min;
}
/**
* Finds a value in this Index and return's it's position, or -1 if not found
*
* @param value
* @return
*/
public int find(int value) {
for (int i=0; i<data.length; i++) {
if (data[i]==value) return i;
}
return -1;
}
/**
* Inverts the permutation represented by this Index
* @return
*/
public Index invert() {
int n=length();
Index ni=new Index(n);
for (int i=0; i<n; i++) {
ni.set(this.get(i), i);
}
return ni;
}
/**
* Checks that all values in this index are within the specified range of
* start (inclusive) to end (exclusive)
* @param start
* @param end
* @return
*/
public boolean allInRange(int start, int end) {
for (int i=0; i<data.length; i++) {
int a=data[i];
if ((a<start)||(a>=end)) return false;
}
return true;
}
public int[] getShape() {
return new int[length()];
}
}