1 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 /**
3  *	Contains source code from the article "Radix Sort Revisited".
4  *	\file		IceRevisitedRadix.cpp
5  *	\author		Pierre Terdiman
6  *	\date		April, 4, 2000
7  */
8 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9 
10 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11 /**
12  *	Revisited Radix Sort.
13  *	This is my new radix routine:
14  *  - it uses indices and doesn't recopy the values anymore, hence wasting less ram
15  *  - it creates all the histograms in one run instead of four
16  *  - it sorts words faster than dwords and bytes faster than words
17  *  - it correctly sorts negative floating-point values by patching the offsets
18  *  - it automatically takes advantage of temporal coherence
19  *  - multiple keys support is a side effect of temporal coherence
20  *  - it may be worth recoding in asm... (mainly to use FCOMI, FCMOV, etc) [it's probably memory-bound anyway]
21  *
22  *	History:
23  *	- 08.15.98: very first version
24  *	- 04.04.00: recoded for the radix article
25  *	- 12.xx.00: code lifting
26  *	- 09.18.01: faster CHECK_PASS_VALIDITY thanks to Mark D. Shattuck (who provided other tips, not included here)
27  *	- 10.11.01: added local ram support
28  *	- 01.20.02: bugfix! In very particular cases the last pass was skipped in the float code-path, leading to incorrect sorting......
29  *	- 01.02.02:	- "mIndices" renamed => "mRanks". That's a rank sorter after all.
30  *				- ranks are not "reset" anymore, but implicit on first calls
31  *	- 07.05.02:	- offsets rewritten with one less indirection.
32  *	- 11.03.02:	- "bool" replaced with RadixHint enum
33  *
34  *	\class		RadixSort
35  *	\author		Pierre Terdiman
36  *	\version	1.4
37  *	\date		August, 15, 1998
38  */
39 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
40 
41 /*
42 To do:
43 	- add an offset parameter between two input values (avoid some data recopy sometimes)
44 	- unroll ? asm ?
45 	- 11 bits trick & 3 passes as Michael did
46 	- prefetch stuff the day I have a P3
47 	- make a version with 16-bits indices ?
48 */
49 
50 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
51 // Precompiled Header
52 #include "Stdafx.h"
53 
54 using namespace IceCore;
55 
56 #define INVALIDATE_RANKS	mCurrentSize|=0x80000000
57 #define VALIDATE_RANKS		mCurrentSize&=0x7fffffff
58 #define CURRENT_SIZE		(mCurrentSize&0x7fffffff)
59 #define INVALID_RANKS		(mCurrentSize&0x80000000)
60 
61 #define CHECK_RESIZE(n)																		\
62 	if(n!=mPreviousSize)																	\
63 	{																						\
64 				if(n>mCurrentSize)	Resize(n);												\
65 		else						ResetRanks();											\
66 		mPreviousSize = n;																	\
67 	}
68 
69 #define CREATE_HISTOGRAMS(type, buffer)														\
70 	/* Clear counters/histograms */															\
71 	ZeroMemory(mHistogram, 256*4*sizeof(udword));											\
72 																							\
73 	/* Prepare to count */																	\
74 	ubyte* p = (ubyte*)input;																\
75 	ubyte* pe = &p[nb*4];																	\
76 	udword* h0= &mHistogram[0];		/* Histogram for first pass (LSB)	*/					\
77 	udword* h1= &mHistogram[256];	/* Histogram for second pass		*/					\
78 	udword* h2= &mHistogram[512];	/* Histogram for third pass			*/					\
79 	udword* h3= &mHistogram[768];	/* Histogram for last pass (MSB)	*/					\
80 																							\
81 	bool AlreadySorted = true;	/* Optimism... */											\
82 																							\
83 	if(INVALID_RANKS)																		\
84 	{																						\
85 		/* Prepare for temporal coherence */												\
86 		type* Running = (type*)buffer;														\
87 		type PrevVal = *Running;															\
88 																							\
89 		while(p!=pe)																		\
90 		{																					\
91 			/* Read input buffer in previous sorted order */								\
92 			type Val = *Running++;															\
93 			/* Check whether already sorted or not */										\
94 			if(Val<PrevVal)	{ AlreadySorted = false; break; } /* Early out */				\
95 			/* Update for next iteration */													\
96 			PrevVal = Val;																	\
97 																							\
98 			/* Create histograms */															\
99 			h0[*p++]++;	h1[*p++]++;	h2[*p++]++;	h3[*p++]++;									\
100 		}																					\
101 																							\
102 		/* If all input values are already sorted, we just have to return and leave the */	\
103 		/* previous list unchanged. That way the routine may take advantage of temporal */	\
104 		/* coherence, for example when used to sort transparent faces.					*/	\
105 		if(AlreadySorted)																	\
106 		{																					\
107 			mNbHits++;																		\
108 			for(udword i=0;i<nb;i++)	mRanks[i] = i;										\
109 			return *this;																	\
110 		}																					\
111 	}																						\
112 	else																					\
113 	{																						\
114 		/* Prepare for temporal coherence */												\
115 		udword* Indices = mRanks;															\
116 		type PrevVal = (type)buffer[*Indices];												\
117 																							\
118 		while(p!=pe)																		\
119 		{																					\
120 			/* Read input buffer in previous sorted order */								\
121 			type Val = (type)buffer[*Indices++];											\
122 			/* Check whether already sorted or not */										\
123 			if(Val<PrevVal)	{ AlreadySorted = false; break; } /* Early out */				\
124 			/* Update for next iteration */													\
125 			PrevVal = Val;																	\
126 																							\
127 			/* Create histograms */															\
128 			h0[*p++]++;	h1[*p++]++;	h2[*p++]++;	h3[*p++]++;									\
129 		}																					\
130 																							\
131 		/* If all input values are already sorted, we just have to return and leave the */	\
132 		/* previous list unchanged. That way the routine may take advantage of temporal */	\
133 		/* coherence, for example when used to sort transparent faces.					*/	\
134 		if(AlreadySorted)	{ mNbHits++; return *this;	}									\
135 	}																						\
136 																							\
137 	/* Else there has been an early out and we must finish computing the histograms */		\
138 	while(p!=pe)																			\
139 	{																						\
140 		/* Create histograms without the previous overhead */								\
141 		h0[*p++]++;	h1[*p++]++;	h2[*p++]++;	h3[*p++]++;										\
142 	}
143 
144 #define CHECK_PASS_VALIDITY(pass)															\
145 	/* Shortcut to current counters */														\
146 	udword* CurCount = &mHistogram[pass<<8];												\
147 																							\
148 	/* Reset flag. The sorting pass is supposed to be performed. (default) */				\
149 	bool PerformPass = true;																\
150 																							\
151 	/* Check pass validity */																\
152 																							\
153 	/* If all values have the same byte, sorting is useless. */								\
154 	/* It may happen when sorting bytes or words instead of dwords. */						\
155 	/* This routine actually sorts words faster than dwords, and bytes */					\
156 	/* faster than words. Standard running time (O(4*n))is reduced to O(2*n) */				\
157 	/* for words and O(n) for bytes. Running time for floats depends on actual values... */	\
158 																							\
159 	/* Get first byte */																	\
160 	ubyte UniqueVal = *(((ubyte*)input)+pass);												\
161 																							\
162 	/* Check that byte's counter */															\
163 	if(CurCount[UniqueVal]==nb)	PerformPass=false;
164 
165 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
166 /**
167  *	Constructor.
168  */
169 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RadixSort()170 RadixSort::RadixSort() : mRanks(null), mRanks2(null), mCurrentSize(0), mTotalCalls(0), mNbHits(0)
171 {
172 #ifndef RADIX_LOCAL_RAM
173 	// Allocate input-independent ram
174 	mHistogram	= new udword[256*4];
175 	mOffset		= new udword[256];
176 #endif
177 	// Initialize indices
178 	INVALIDATE_RANKS;
179 }
180 
181 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
182 /**
183  *	Destructor.
184  */
185 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
~RadixSort()186 RadixSort::~RadixSort()
187 {
188 	// Release everything
189 #ifndef RADIX_LOCAL_RAM
190 	DELETEARRAY(mOffset);
191 	DELETEARRAY(mHistogram);
192 #endif
193 	DELETEARRAY(mRanks2);
194 	DELETEARRAY(mRanks);
195 }
196 
197 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
198 /**
199  *	Resizes the inner lists.
200  *	\param		nb	[in] new size (number of dwords)
201  *	\return		true if success
202  */
203 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Resize(udword nb)204 bool RadixSort::Resize(udword nb)
205 {
206 	// Free previously used ram
207 	DELETEARRAY(mRanks2);
208 	DELETEARRAY(mRanks);
209 
210 	// Get some fresh one
211 	mRanks	= new udword[nb];	CHECKALLOC(mRanks);
212 	mRanks2	= new udword[nb];	CHECKALLOC(mRanks2);
213 
214 	return true;
215 }
216 
CheckResize(udword nb)217 inline_ void RadixSort::CheckResize(udword nb)
218 {
219 	udword CurSize = CURRENT_SIZE;
220 	if(nb!=CurSize)
221 	{
222 		if(nb>CurSize)	Resize(nb);
223 		mCurrentSize = nb;
224 		INVALIDATE_RANKS;
225 	}
226 }
227 
228 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
229 /**
230  *	Main sort routine.
231  *	This one is for integer values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data.
232  *	\param		input	[in] a list of integer values to sort
233  *	\param		nb		[in] number of values to sort, must be < 2^31
234  *	\param		hint	[in] RADIX_SIGNED to handle negative values, RADIX_UNSIGNED if you know your input buffer only contains positive values
235  *	\return		Self-Reference
236  */
237 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Sort(const udword * input,udword nb,RadixHint hint)238 RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint)
239 {
240 	// Checkings
241 	if(!input || !nb || nb&0x80000000)	return *this;
242 
243 	// Stats
244 	mTotalCalls++;
245 
246 	// Resize lists if needed
247 	CheckResize(nb);
248 
249 #ifdef RADIX_LOCAL_RAM
250 	// Allocate histograms & offsets on the stack
251 	udword mHistogram[256*4];
252 //	udword mOffset[256];
253 	udword* mLink[256];
254 #endif
255 
256 	// Create histograms (counters). Counters for all passes are created in one run.
257 	// Pros:	read input buffer once instead of four times
258 	// Cons:	mHistogram is 4Kb instead of 1Kb
259 	// We must take care of signed/unsigned values for temporal coherence.... I just
260 	// have 2 code paths even if just a single opcode changes. Self-modifying code, someone?
261 	if(hint==RADIX_UNSIGNED)	{ CREATE_HISTOGRAMS(udword, input);	}
262 	else						{ CREATE_HISTOGRAMS(sdword, input);	}
263 
264 	// Compute #negative values involved if needed
265 	udword NbNegativeValues = 0;
266 	if(hint==RADIX_SIGNED)
267 	{
268 		// An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128
269 		// last values of the last histogram. Last histogram because that's the one for the Most Significant Byte,
270 		// responsible for the sign. 128 last values because the 128 first ones are related to positive numbers.
271 		udword* h3= &mHistogram[768];
272 		for(udword i=128;i<256;i++)	NbNegativeValues += h3[i];	// 768 for last histogram, 128 for negative part
273 	}
274 
275 	// Radix sort, j is the pass number (0=LSB, 3=MSB)
276 	for(udword j=0;j<4;j++)
277 	{
278 		CHECK_PASS_VALIDITY(j);
279 
280 		// Sometimes the fourth (negative) pass is skipped because all numbers are negative and the MSB is 0xFF (for example). This is
281 		// not a problem, numbers are correctly sorted anyway.
282 		if(PerformPass)
283 		{
284 			// Should we care about negative values?
285 			if(j!=3 || hint==RADIX_UNSIGNED)
286 			{
287 				// Here we deal with positive values only
288 
289 				// Create offsets
290 //				mOffset[0] = 0;
291 //				for(udword i=1;i<256;i++)		mOffset[i] = mOffset[i-1] + CurCount[i-1];
292 				mLink[0] = mRanks2;
293 				for(udword i=1;i<256;i++)		mLink[i] = mLink[i-1] + CurCount[i-1];
294 			}
295 			else
296 			{
297 				// This is a special case to correctly handle negative integers. They're sorted in the right order but at the wrong place.
298 
299 				// Create biased offsets, in order for negative numbers to be sorted as well
300 //				mOffset[0] = NbNegativeValues;												// First positive number takes place after the negative ones
301 				mLink[0] = &mRanks2[NbNegativeValues];										// First positive number takes place after the negative ones
302 //				for(udword i=1;i<128;i++)		mOffset[i] = mOffset[i-1] + CurCount[i-1];	// 1 to 128 for positive numbers
303 				for(udword i=1;i<128;i++)		mLink[i] = mLink[i-1] + CurCount[i-1];		// 1 to 128 for positive numbers
304 
305 				// Fixing the wrong place for negative values
306 //				mOffset[128] = 0;
307 				mLink[128] = mRanks2;
308 //				for(i=129;i<256;i++)			mOffset[i] = mOffset[i-1] + CurCount[i-1];
309 				for(udword i=129;i<256;i++)		mLink[i] = mLink[i-1] + CurCount[i-1];
310 			}
311 
312 			// Perform Radix Sort
313 			ubyte* InputBytes	= (ubyte*)input;
314 			InputBytes += j;
315 			if(INVALID_RANKS)
316 			{
317 //				for(udword i=0;i<nb;i++)	mRanks2[mOffset[InputBytes[i<<2]]++] = i;
318 				for(udword i=0;i<nb;i++)	*mLink[InputBytes[i<<2]]++ = i;
319 				VALIDATE_RANKS;
320 			}
321 			else
322 			{
323 				udword* Indices		= mRanks;
324 				udword* IndicesEnd	= &mRanks[nb];
325 				while(Indices!=IndicesEnd)
326 				{
327 					udword id = *Indices++;
328 //					mRanks2[mOffset[InputBytes[id<<2]]++] = id;
329 					*mLink[InputBytes[id<<2]]++ = id;
330 				}
331 			}
332 
333 			// Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
334 			udword* Tmp	= mRanks;	mRanks = mRanks2; mRanks2 = Tmp;
335 		}
336 	}
337 	return *this;
338 }
339 
340 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
341 /**
342  *	Main sort routine.
343  *	This one is for floating-point values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data.
344  *	\param		input			[in] a list of floating-point values to sort
345  *	\param		nb				[in] number of values to sort, must be < 2^31
346  *	\return		Self-Reference
347  *	\warning	only sorts IEEE floating-point values
348  */
349 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Sort(const float * input2,udword nb)350 RadixSort& RadixSort::Sort(const float* input2, udword nb)
351 {
352 	// Checkings
353 	if(!input2 || !nb || nb&0x80000000)	return *this;
354 
355 	// Stats
356 	mTotalCalls++;
357 
358 	udword* input = (udword*)input2;
359 
360 	// Resize lists if needed
361 	CheckResize(nb);
362 
363 #ifdef RADIX_LOCAL_RAM
364 	// Allocate histograms & offsets on the stack
365 	udword mHistogram[256*4];
366 //	udword mOffset[256];
367 	udword* mLink[256];
368 #endif
369 
370 	// Create histograms (counters). Counters for all passes are created in one run.
371 	// Pros:	read input buffer once instead of four times
372 	// Cons:	mHistogram is 4Kb instead of 1Kb
373 	// Floating-point values are always supposed to be signed values, so there's only one code path there.
374 	// Please note the floating point comparison needed for temporal coherence! Although the resulting asm code
375 	// is dreadful, this is surprisingly not such a performance hit - well, I suppose that's a big one on first
376 	// generation Pentiums....We can't make comparison on integer representations because, as Chris said, it just
377 	// wouldn't work with mixed positive/negative values....
378 	{ CREATE_HISTOGRAMS(float, input2); }
379 
380 	// Compute #negative values involved if needed
381 	udword NbNegativeValues = 0;
382 	// An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128
383 	// last values of the last histogram. Last histogram because that's the one for the Most Significant Byte,
384 	// responsible for the sign. 128 last values because the 128 first ones are related to positive numbers.
385 	udword* h3= &mHistogram[768];
386 	for(udword i=128;i<256;i++)	NbNegativeValues += h3[i];	// 768 for last histogram, 128 for negative part
387 
388 	// Radix sort, j is the pass number (0=LSB, 3=MSB)
389 	for(udword j=0;j<4;j++)
390 	{
391 		// Should we care about negative values?
392 		if(j!=3)
393 		{
394 			// Here we deal with positive values only
395 			CHECK_PASS_VALIDITY(j);
396 
397 			if(PerformPass)
398 			{
399 				// Create offsets
400 //				mOffset[0] = 0;
401 				mLink[0] = mRanks2;
402 //				for(udword i=1;i<256;i++)		mOffset[i] = mOffset[i-1] + CurCount[i-1];
403 				for(udword i=1;i<256;i++)		mLink[i] = mLink[i-1] + CurCount[i-1];
404 
405 				// Perform Radix Sort
406 				ubyte* InputBytes = (ubyte*)input;
407 				InputBytes += j;
408 				if(INVALID_RANKS)
409 				{
410 //					for(i=0;i<nb;i++)	mRanks2[mOffset[InputBytes[i<<2]]++] = i;
411 					for(udword i=0;i<nb;i++)	*mLink[InputBytes[i<<2]]++ = i;
412 					VALIDATE_RANKS;
413 				}
414 				else
415 				{
416 					udword* Indices		= mRanks;
417 					udword* IndicesEnd	= &mRanks[nb];
418 					while(Indices!=IndicesEnd)
419 					{
420 						udword id = *Indices++;
421 //						mRanks2[mOffset[InputBytes[id<<2]]++] = id;
422 						*mLink[InputBytes[id<<2]]++ = id;
423 					}
424 				}
425 
426 				// Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
427 				udword* Tmp	= mRanks;	mRanks = mRanks2; mRanks2 = Tmp;
428 			}
429 		}
430 		else
431 		{
432 			// This is a special case to correctly handle negative values
433 			CHECK_PASS_VALIDITY(j);
434 
435 			if(PerformPass)
436 			{
437 				// Create biased offsets, in order for negative numbers to be sorted as well
438 //				mOffset[0] = NbNegativeValues;												// First positive number takes place after the negative ones
439 				mLink[0] = &mRanks2[NbNegativeValues];										// First positive number takes place after the negative ones
440 //				for(udword i=1;i<128;i++)		mOffset[i] = mOffset[i-1] + CurCount[i-1];	// 1 to 128 for positive numbers
441 				for(udword i=1;i<128;i++)		mLink[i] = mLink[i-1] + CurCount[i-1];		// 1 to 128 for positive numbers
442 
443 				// We must reverse the sorting order for negative numbers!
444 //				mOffset[255] = 0;
445 				mLink[255] = mRanks2;
446 //				for(i=0;i<127;i++)		mOffset[254-i] = mOffset[255-i] + CurCount[255-i];	// Fixing the wrong order for negative values
447 				for(udword i=0;i<127;i++)	mLink[254-i] = mLink[255-i] + CurCount[255-i];		// Fixing the wrong order for negative values
448 //				for(i=128;i<256;i++)	mOffset[i] += CurCount[i];							// Fixing the wrong place for negative values
449 				for(udword i=128;i<256;i++)	mLink[i] += CurCount[i];							// Fixing the wrong place for negative values
450 
451 				// Perform Radix Sort
452 				if(INVALID_RANKS)
453 				{
454 					for(udword i=0;i<nb;i++)
455 					{
456 						udword Radix = input[i]>>24;							// Radix byte, same as above. AND is useless here (udword).
457 						// ### cmp to be killed. Not good. Later.
458 //						if(Radix<128)		mRanks2[mOffset[Radix]++] = i;		// Number is positive, same as above
459 //						else				mRanks2[--mOffset[Radix]] = i;		// Number is negative, flip the sorting order
460 						if(Radix<128)		*mLink[Radix]++ = i;		// Number is positive, same as above
461 						else				*(--mLink[Radix]) = i;		// Number is negative, flip the sorting order
462 					}
463 					VALIDATE_RANKS;
464 				}
465 				else
466 				{
467 					for(udword i=0;i<nb;i++)
468 					{
469 						udword Radix = input[mRanks[i]]>>24;							// Radix byte, same as above. AND is useless here (udword).
470 						// ### cmp to be killed. Not good. Later.
471 //						if(Radix<128)		mRanks2[mOffset[Radix]++] = mRanks[i];		// Number is positive, same as above
472 //						else				mRanks2[--mOffset[Radix]] = mRanks[i];		// Number is negative, flip the sorting order
473 						if(Radix<128)		*mLink[Radix]++ = mRanks[i];		// Number is positive, same as above
474 						else				*(--mLink[Radix]) = mRanks[i];		// Number is negative, flip the sorting order
475 					}
476 				}
477 				// Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
478 				udword* Tmp	= mRanks;	mRanks = mRanks2; mRanks2 = Tmp;
479 			}
480 			else
481 			{
482 				// The pass is useless, yet we still have to reverse the order of current list if all values are negative.
483 				if(UniqueVal>=128)
484 				{
485 					if(INVALID_RANKS)
486 					{
487 						// ###Possible?
488 						for(udword i=0;i<nb;i++)	mRanks2[i] = nb-i-1;
489 						VALIDATE_RANKS;
490 					}
491 					else
492 					{
493 						for(udword i=0;i<nb;i++)	mRanks2[i] = mRanks[nb-i-1];
494 					}
495 
496 					// Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
497 					udword* Tmp	= mRanks;	mRanks = mRanks2; mRanks2 = Tmp;
498 				}
499 			}
500 		}
501 	}
502 	return *this;
503 }
504 
505 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
506 /**
507  *	Gets the ram used.
508  *	\return		memory used in bytes
509  */
510 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
GetUsedRam() const511 udword RadixSort::GetUsedRam() const
512 {
513 	udword UsedRam = sizeof(RadixSort);
514 #ifndef RADIX_LOCAL_RAM
515 	UsedRam += 256*4*sizeof(udword);			// Histograms
516 	UsedRam += 256*sizeof(udword);				// Offsets
517 #endif
518 	UsedRam += 2*CURRENT_SIZE*sizeof(udword);	// 2 lists of indices
519 	return UsedRam;
520 }
521