1 package ukmer;
2 
3 import java.util.Arrays;
4 
5 import dna.AminoAcid;
6 import shared.Tools;
7 import structures.ByteBuilder;
8 
9 /**
10  * @author Brian Bushnell
11  * @date Jul 9, 2015
12  *
13  */
14 public class Kmer implements Cloneable {
15 
Kmer(Kmer o)16 	public Kmer(Kmer o){
17 		this(o.k, o.mult);
18 		setFrom(o);
19 	}
20 
Kmer(int kbig_)21 	public Kmer(int kbig_){
22 		this(getK(kbig_), getMult(kbig_));
23 	}
24 
25 	@Override
clone()26 	public Kmer clone(){
27 		return new Kmer(this);
28 	}
29 
Kmer(int k_, int mult_)30 	public Kmer(int k_, int mult_){
31 		k=k_;
32 		mult=mult_;
33 		maxindex=mult-1;
34 		shift=2*k;
35 		shift2=shift-2;
36 		mask=(shift>63 ? -1L : ~((-1L)<<shift));
37 		coreMask=toCoreMask(k);
38 
39 		kbig=k*mult;
40 		array1=new long[mult];
41 		array2=new long[mult];
42 		key=null;
43 	}
44 
toCoreMask(int k)45 	public static final long toCoreMask(int k){
46 //		System.err.println(k+", "+MASK_CORE);
47 		return MASK_CORE ? ((~((-1L)<<(2*k)))>>2)&~(3L) : -1L;
48 	}
49 
getMult(int kbig)50 	public static int getMult(int kbig){
51 		final int mult=getMult0(kbig);
52 //		assert(mult==getMult0(kbig*(mult/mult))) : mult+", "+getMult0(mult*(kbig/mult));
53 		return mult;
54 	}
55 
getKbig(int kbig)56 	public static int getKbig(int kbig){
57 		int x=getMult(kbig)*getK(kbig);
58 		assert(x<=kbig) : x+", "+kbig;
59 		assert(kbig>31 || x==kbig);
60 		return x;
61 	}
62 
getMult0(int kbig)63 	private static int getMult0(int kbig){
64 //		if(true){return 2;}//TODO: 123 //Enable to allow multi-word arrays for k<32
65 		final int word=31;
66 
67 		final int mult1=(kbig+word-1)/word;
68 		final int mult2=Tools.max(1, kbig/word);
69 		if(mult1==mult2){return mult1;}
70 
71 		final int k1=Tools.min(word, kbig/mult1);
72 		final int k2=Tools.min(word, kbig/mult2);
73 
74 		final int kbig1=k1*mult1;
75 		final int kbig2=k2*mult2;
76 
77 		assert(kbig1<=kbig);
78 		assert(kbig2<=kbig);
79 		assert(mult2<=mult1);
80 
81 //		assert(false) : mult1+", "+mult2+", "+k1+", "+k2;
82 
83 		final int mult=kbig2>=kbig1 ? mult2 : mult1;
84 
85 		return mult;
86 	}
87 
getK(int kbig)88 	public static int getK(int kbig){
89 		int mult=getMult(kbig);
90 		int x=kbig/mult;
91 		assert(x*mult<=kbig) : x+", "+kbig;
92 		assert(x<=31) : kbig+", "+mult+", "+x;
93 		return x;
94 	}
95 
setFrom(Kmer o)96 	public Kmer setFrom(Kmer o){
97 		for(int i=0; i<mult; i++){
98 			array1[i]=o.array1[i];
99 			array2[i]=o.array2[i];
100 			len=o.len;
101 		}
102 		incarnation++;
103 		return this;
104 	}
105 
setFrom(long[] array)106 	public Kmer setFrom(long[] array){
107 		for(int i=0; i<mult; i++){
108 			array1[i]=array[i];
109 		}
110 		fillArray2();
111 		incarnation++;
112 		return this;
113 	}
114 
clear()115 	public void clear() {
116 		len=0;
117 		for(int i=0; i<mult; i++){
118 			array1[i]=0;
119 			array2[i]=0;
120 		}
121 		lastIncarnation=-1;
122 		incarnation=0;
123 		//incarnation++;
124 	}
125 
clearFast()126 	public void clearFast() {
127 		len=0;
128 		lastIncarnation=-1;
129 		incarnation=0;
130 		//incarnation++;
131 	}
132 
verify(boolean update)133 	public boolean verify(boolean update){
134 //		boolean b=verify();
135 //		if(b){
136 //			if(update){update();}
137 //			b=verify();
138 //			assert(len<kbig || incarnation==lastIncarnation);
139 //		}
140 		if(update){
141 			update();
142 			assert(len<kbig || incarnation==lastIncarnation) : "incarnation="+incarnation+", last="+lastIncarnation+", len="+len+", kbig="+kbig;
143 		}
144 		boolean b=verify();
145 		return b;
146 	}
147 
148 	private boolean verify(){
149 		if(len<kbig){return true;}
150 		for(int i=maxindex, j=0; j<mult; j++, i--){
151 			long kmer=array1[i];
152 			long rkmer=array2[j];
153 			if(kmer!=AminoAcid.reverseComplementBinaryFast(rkmer, k)){
154 //				assert(false) : Arrays.toString(array1);
155 				return false;
156 			}
157 		}
158 		assert(incarnation==lastIncarnation) : "incarnation="+incarnation+", last="+lastIncarnation+", len="+len+", kbig="+kbig;
159 		return true;
160 	}
161 
162 	public byte addRight(final byte b){
163 		long x=AminoAcid.baseToNumber[b];
164 		return AminoAcid.numberToBase[(int)addRightNumeric(x)];
165 	}
166 
167 	public byte addRight(final char b){
168 		long x=AminoAcid.baseToNumber[b];
169 		return AminoAcid.numberToBase[(int)addRightNumeric(x)];
170 	}
171 
172 	public byte addLeft(final byte b){
173 		long x=AminoAcid.baseToNumber[b];
174 		return AminoAcid.numberToBase[(int)addLeftNumeric(x)];
175 	}
176 
177 	public long addRightNumeric(long x){
178 		long x2;
179 
180 		if(x<0){
181 			x=0;
182 			x2=3;
183 			len=0;
184 		}else{
185 			x2=AminoAcid.numberToComplement[(int)x];
186 			len++;
187 		}
188 
189 		for(int i=maxindex, j=0; j<mult; j++, i--){
190 
191 			long y=(array1[i]>>>shift2)&3L;
192 			long y2=array2[j]&3L;
193 
194 			//Update kmers
195 			array1[i]=((array1[i]<<2)|x)&mask;
196 			array2[j]=((array2[j]>>>2)|(x2<<shift2))&mask;
197 
198 			x=y;
199 			x2=y2;
200 		}
201 		incarnation++;
202 		return x;
203 	}
204 
205 	public long addLeftNumeric(long x){
206 		assert(x>=0 && x<4) : x;
207 		long x2=AminoAcid.numberToComplement[(int)x];
208 
209 		assert(x>=0);
210 		assert(len>=kbig);
211 
212 		for(int i=0, j=maxindex; i<mult; i++, j--){
213 
214 			long y=array1[i]&3L;
215 			long y2=(array2[j]>>>shift2)&3L;
216 
217 			//Update kmers
218 			array1[i]=(array1[i]>>>2)|(x<<shift2);
219 			array2[j]=((array2[j]<<2)|x2)&mask;
220 
221 			x=y;
222 			x2=y2;
223 		}
224 		incarnation++;
225 		return x;
226 	}
227 
fillArray2()228 	public void fillArray2() {
229 		for(int i=maxindex, j=0; j<mult; j++, i--){
230 			array2[j]=AminoAcid.reverseComplementBinaryFast(array1[i], k);
231 		}
232 		len=kbig;
233 		incarnation++;
234 	}
235 
236 	@Override
toString()237 	public String toString(){
238 //		update();
239 		assert(verify(true));
240 		ByteBuilder bb=new ByteBuilder();
241 		for(int i=0; i<mult; i++){
242 			bb.appendKmer(array1[i], k);
243 //			bb.append(" ");
244 		}
245 ////		bb.append("~");
246 //		for(int i=0; i<mult; i++){
247 //			bb.appendKmer(array2[i], k);
248 ////			bb.append(" ");
249 //		}
250 		return bb.toString();
251 	}
252 
equals(Kmer x)253 	public boolean equals(Kmer x){
254 		if(xor()!=x.xor()){return false;}
255 		return AbstractKmerTableU.equals(key(), x.key());
256 	}
257 
sameOrientation(Kmer x)258 	public boolean sameOrientation(Kmer x){
259 		if(xor()!=x.xor()){return false;}
260 		return Tools.equals(array1, array2);
261 	}
262 
compareTo(Kmer x)263 	public int compareTo(Kmer x){
264 		return compare(key(), x.key());
265 	}
266 
compareTo(long[] key2)267 	public int compareTo(long[] key2){
268 		assert(false);
269 		return compare(key(), key2);
270 	}
271 
compare(long[] key1, long[] key2)272 	public static int compare(long[] key1, long[] key2){
273 //		assert(false); //Why was this here?
274 		return AbstractKmerTableU.compare(key1, key2);
275 	}
276 
equals(long[] key1, long[] key2)277 	public static boolean equals(long[] key1, long[] key2){
278 		assert(false);
279 		return AbstractKmerTableU.equals(key1, key2);
280 	}
281 
array1()282 	public long[] array1(){return array1;}
283 
array2()284 	public long[] array2(){return array2;}
285 
286 	/** WARNING!
287 	 * Do not confuse this with xor()! */
key()288 	public long[] key(){
289 		update();
290 //		assert(verify(false));
291 		return key;
292 	}
293 
corePalindrome()294 	public boolean corePalindrome(){//TODO: This can be set as a flag from setKey0
295 		update();
296 		return corePalindrome;
297 	}
298 
setKey0()299 	private void setKey0(){
300 		corePalindrome=false;
301 		key=array1;
302 		if(!rcomp){return;}
303 		for(int i=0; i<mult; i++){
304 			final long a=array1[i]&coreMask, b=array2[i]&coreMask;
305 			if(a>b){return;}
306 			else if(a<b){
307 				key=array2;
308 				return;
309 			}
310 		}
311 		corePalindrome=true;
312 		setKey0safe();
313 	}
314 
setKey0safe()315 	private void setKey0safe(){
316 		key=array1;
317 		for(int i=0; i<mult; i++){
318 			final long a=array1[i], b=array2[i];
319 			if(a>b){break;}
320 			else if(a<b){
321 				key=array2;
322 				break;
323 			}
324 		}
325 	}
326 
xor(long[] key, long coreMask)327 	public static long xor(long[] key, long coreMask){
328 		long xor=key[0]&coreMask;
329 		for(int i=1; i<key.length; i++){
330 			xor=(Long.rotateLeft(xor, 25))^(key[i]&coreMask);
331 		}
332 		return xor&mask63;
333 	}
334 
335 	/** WARNING!
336 	 * Do not confuse this with key()! */
xor()337 	public long xor(){
338 		update();
339 		return lastXor;
340 	}
341 
342 	/**
343 	 * @param divisor
344 	 * @return This kmer's xor modulo the divisor
345 	 */
mod(int divisor)346 	public int mod(int divisor) {
347 		int x=(int)(xor()%divisor);
348 //		System.err.println(xor()+"%"+value+"="+x);
349 		return x;
350 	}
351 
rcomp()352 	public void rcomp() {
353 		long[] temp=array1;
354 		array1=array2;
355 		array2=temp;
356 	}
357 
update()358 	private void update(){
359 		if(verbose){System.err.println("update() - len="+len);}
360 		assert(TESTMODE || len>=kbig) : len+", "+kbig+", "+array1[0];
361 		if(incarnation==lastIncarnation){return;}
362 		setKey0();
363 		lastXor=xor0();
364 		lastIncarnation=incarnation;
365 		if(verbose){System.err.println("After update - kmer "+this+"; key="+Arrays.toString(key)+"; a1="+Arrays.toString(array1())+"; a2="+Arrays.toString(array2()));}
366 	}
367 
xor0()368 	private long xor0(){
369 		return xor(key, coreMask);
370 	}
371 
arraysToString()372 	public String arraysToString() {
373 		return "key="+Arrays.toString(key)+", a1="+Arrays.toString(array1)+", a2="+Arrays.toString(array2);
374 	}
375 
gc()376 	public final int gc(){
377 		int gc=0;
378 		for(long kmer : array1){
379 			while(kmer>0){
380 				long x=kmer&3;
381 				kmer>>>=2;
382 				if(x==1 || x==2){gc++;}
383 			}
384 		}
385 		return gc;
386 	}
387 
388 	static boolean rcomp=true;
389 
390 	private long lastXor=-1;
391 	private long incarnation=0;
392 	private long lastIncarnation=-1;
393 	private boolean corePalindrome=false;
394 	private long[] key=null;
395 
396 	private long[] array1;
397 	private long[] array2;
398 	public final int kbig;
399 	public final int k;
400 	final int mult, maxindex;
401 
402 	private final int shift;
403 	private final int shift2;
404 	private final long mask;
405 	private final long coreMask;
406 
407 	public int len=0; //TODO: Make private; use getter.
len()408 	public final int len(){return len;}
409 
410 	public static boolean MASK_CORE=false;
411 	private static final long mask63=Long.MAX_VALUE;
412 	private static final boolean TESTMODE=false; //123
413 	private static final boolean verbose=false;
414 }
415