RiksOrderedCollection

After glancing at the C implementations of OrderedCollection, I figured that it's worth posting a version which is prettier than some, works better than others, and is at least as easy to understand as any other (recognizing our natural bias in understanding what we did ourselves). As of class-time Wednesday, I had most of OC going, with non-exhaustive testing of everything so far. I demonstrated a little of "code-a-little, test-a-little" and made only a little progress during class: are we surprised?

Here is the program as it looked by the end of class Wednesday, 10.12: (a later version is far below) code format="c"
 * 1) include #include 
 * 2) include 
 * 3) include 


 * 1) define OCinitialSize 10

struct OrderedCollection { int *theData;  // type casting allows us to store pointers also int firstIx, lastIx; // C programs are filled with abbreviations. When in Rome... int capacity; };   // "OC" shall abbreviate  OrderedCollection

struct OrderedCollection aStaticOC; struct OrderedCollection *myOC = &aStaticOC;

initializeOC(struct OrderedCollection *anOC) { anOC->capacity = OCinitialSize; anOC->theData = malloc(anOC->capacity);   anOC->firstIx = anOC->capacity/2; anOC->lastIx = (anOC->firstIx) - 1; // The 's are necessary  8-( }

bool isEmpty(struct OrderedCollection *anOC) { return(anOC->lastIx < anOC->firstIx); }

bool hasRoomAtLast(struct OrderedCollection *anOC) { return(anOC->lastIx < anOC->capacity); }

bool hasRoomAtFirst(struct OrderedCollection *anOC) { return(anOC->firstIx > 0); }

int at(struct OrderedCollection *anOC, int index) { return( anOC->theData[(anOC->firstIndex) + index]); }

showOC(struct OrderedCollection *anOC) { int ix; printf("theData @%d firstIx=%d lastIx=%i   size=%i\n", &anOC->theData, anOC->firstIx, anOC->lastIx, sizeOC(anOC)); if (isEmpty(anOC)) { printf("Empty\n"); } else { for (ix = anOC->firstIx; ix <= anOC->lastIx; ix++) { printf("%i @%i: %i\n", ix, &anOC->theData[ix], anOC->theData[ix]); }   } }

growUp(struct OrderedCollection *anOC) { int *oldData, oldSize, ix; oldData = anOC->theData; oldSize = anOC->capacity; anOC->capacity = (anOC->capacity) * 2; anOC->theData = malloc(anOC->capacity); for(ix=0; ixtheData[ix] = oldData[ix]; } free(oldData); }

void assureRoomAtLast(struct OrderedCollection *anOC) { if (hasRoomAtLast(anOC)) return; printf("***** growing up (at last)\n"); growUp(anOC); }

addLast(struct OrderedCollection *anOC, int anInt) { int ix; assureRoomAtLast(anOC); ix = (anOC->lastIx = anOC->lastIx + 1); anOC->theData[ix] = anInt; printf("addLast %i @%i: %i\n", ix, &anOC->theData[ix], anOC->theData[ix]); }

growDown(struct OrderedCollection *anOC) { int *oldData, oldSize, ix; oldData = anOC->theData; oldSize = anOC->capacity; anOC->capacity = (anOC->capacity) * 2; anOC->theData = malloc(anOC->capacity); for(ix=0; ixtheData[ix+oldSize] = oldData[ix]; } anOC->firstIx = (anOC->firstIx) + oldSize; anOC->lastIx = (anOC->lastIx) + oldSize; free(oldData); }

void assureRoomAtFirst(struct OrderedCollection *anOC) { if (hasRoomAtFirst(anOC)) return; printf("***** growing down .... vvvvvvvvvvv\n"); growDown(anOC); }

addFirst(struct OrderedCollection *anOC, int anInt) { int ix; assureRoomAtFirst(anOC); ix = (anOC->firstIx = anOC->firstIx - 1); anOC->theData[ix] = anInt; printf("addFirst %i @%i: %i\n", ix, &anOC->theData[ix], anOC->theData[ix]); }

push(struct OrderedCollection *anOC, int anInt) { // alias for using anOC as a Stack addLast(anOC, anInt); }

sizeOC(struct OrderedCollection *anOC) { return(anOC->lastIx - anOC->firstIx + 1); }

findPosition(struct OrderedCollection *anOC, int newInt) { // answer the relative index where the new guy would get stuck int ix; for(ix = 0; ix < sizeOC(anOC); ix++) { if(newInt < at(anOC, ix)) { return(ix); } } }

main { int jx; initializeOC(myOC); showOC(myOC); for(jx=4; jx<1000; jx = jx*2 -1) { push(myOC, jx); showOC(myOC); }   printf("\nGoing down....................\n"); for(jx=-2; jx> -1000; jx = jx*2 -1) { addFirst(myOC, jx); showOC(myOC); } }

code Where the most recent addition is the "findPosition" function that Roger suggested. Rik is uncomfortable that there were 3 new functions, none yet tested. That is too much coding without any testing. Note that I ammended showOC to print sizeOC. We were meticulous in defining what findPosition returns. The most important thing is being consistent and agreeing what it means. People on a project could argue in favor of exposing more of the underlying structure, thus forcing more places to know that structure... and that could work if everybody agrees.

There WAS a bug in code bool hasRoomAtLast(struct OrderedCollection *anOC) { return((anOC->lastIx+1) < anOC->capacity); } //  Indexing from 0 can cause so much hassle

code The +1 fixes that problem.

code 102 findPosition(struct OrderedCollection *anOC, int newInt) { 103 // answer the relative index where the new guy would get stuck 104        int ix; 105        for(ix = 0; ix < sizeOC(anOC); ix++) { 106                if(newInt < at(anOC, ix)) { return(ix); } 107        } 108 }

code and test code: code 123   printf("findIndex (0) = %i =? 9\n", findPosition(myOC, 0)); 124   printf("findIndex (-1111) = %i =? 0\n", findPosition(myOC, -1111)); 125   printf("findIndex (1024) = %i =? 18\n", findPosition(myOC, 1024)); code worked first time (whew): code findIndex (0) = 9 =? 9 findIndex (-1111) = 0 =? 0 findIndex (1024) = 18 =? 18

code OK, so now that we can compute the slot a new item goes in, we might need to makeSpaceAtPosition(pos) which will sometimes need to shoveUp everything starting from a given relative position. code 110 shoveUp(struct OrderedCollection *anOC, int startPos) { 111   int ix; 112   assureRoomAtLast(anOC); 113   for (ix = anOC->lastIx; ix >= anOC->firstIx + startPos; ix--) { 114        anOC->theData[ix+1] = anOC->theData[ix]; } 115   // leaves the data with a duplicated item at startPos 116 }

And a simple test: 134   shoveUp(myOC, 9); 135   printf("See duplicate at pos 9???\n"); 136   showOC(myOC);

shows: . . . 7: 23 @1049068: -5 8: 24 @1049072: -2 9: 25 @1049076: 4 10: 26 @1049080: 4 11: 27 @1049084: 7 12: 28 @1049088: 13 . ..

code Then I created a procedure for internal use only... makeSpaceAtPosition(pos) which makes the space and leaves junk in it. code 116 makeSpaceAtPosition(struct OrderedCollection *anOC, int aPos) { 117       // Always increases the sizeOC, the item at aPos is ready to be over-written 118    if (aPos <= 0) { 119        assureRoomAtFirst(anOC); 120        anOC->firstIx = anOC->firstIx -1; 121        return; } 122    if (aPos >= sizeOC(anOC)) { 123        assureRoomAtLast(anOC); 124        anOC->lastIx = anOC->lastIx + 1; 125        return; } 126               // until someone implements shoveDown... it always shoves Up. 127               //    Legal, corecct, but a bit less efficient 128    shoveUp(anOC, aPos);129 }

and one test: 150   makeSpaceAtPosition(myOC, 5); 151        printf("Do we have a duplicate at pos 5???\n"); 152   showOC(myOC);

and I saw the expected duplication: 4: 20 @1049056: -47 5: 21 @1049060: -23 6: 22 @1049064: -23 7: 23 @1049068: -11 code I feel assured that I have not made obvious off-by-one errors. Now insertAt is easy: code 132 insertAt(struct OrderedCollection *anOC, int aPos, int newInt) { 133        makeSpaceAtPosition(anOC, aPos); 134        anOC->theData[aPos + anOC->firstIx] = newInt; 135 } and a little test code: 159   insertAt(myOC, 0, 1000); 160   insertAt(myOC, 10, 2000); 161   insertAt(myOC, 22, 3333); 162   showOC(myOC);

code If you are planning on a bubble sort, you'd need to code 137 swapPositions(struct OrderedCollection *anOC, int jPos, int kPos) { 138        int tmp; 139        tmp = anOC->theData[jPos + anOC->firstIx]; 140        anOC->theData[jPos + anOC->firstIx] = anOC->theData[kPos + anOC->firstIx]; 141        anOC->theData[kPos + anOC->firstIx] = tmp; 142 }

with just one test 170   swapPositions(myOC, 21, 1); 171   showOC(myOC); which worked as expected. code Most of these little bricks are fairly easy. They still mount up into a pile of code. Even the simplest of complexity, repetition of simple pieces, generates complexity.

I am just about ready to implement insertNoteCardForStudentBySID(anOC, aStudent). I would first implement findPositionBySID(anOC, aSID) where it's up to you to make sure that each thing in your OC is a note card that refers to a Student. With some careful type casting, the address of your NoteCard can be treated as an int and stored in an OC. But I leave that as the crucial exercise for this assignment.

As promised, here is my "finished" version of utilities and some tests of their sanity, in two forms: [|OrderedCollectionRik.c] code format="c" cat OrderedCollectionRik.c
 * 1) include 
 * 2) include 
 * 3) include 
 * 4) include 


 * 1) define OCinitialSize 10

struct OrderedCollection { long int *theData;  // type casting allows us to store pointers also int firstIx, lastIx; // C programs are filled with abbreviations. When in Rome... int capacity; };   // "OC" shall abbreviate  OrderedCollection

struct OrderedCollection aStaticOC; struct OrderedCollection *myOC = &aStaticOC;

initializeOC(struct OrderedCollection *anOC) { anOC->capacity = OCinitialSize; anOC->theData = malloc((sizeof(long int *)) * anOC->capacity); // The lack of space had been a killer anOC->firstIx = anOC->capacity/2; anOC->lastIx = (anOC->firstIx) - 1; // The 's are necessary  8-( }

bool isEmpty(struct OrderedCollection *anOC) { return(anOC->lastIx < anOC->firstIx); }

bool hasRoomAtLast(struct OrderedCollection *anOC) { return((anOC->lastIx+1) < anOC->capacity); } //  Indexing from 0 can cause so much hassle

bool hasRoomAtFirst(struct OrderedCollection *anOC) { return(anOC->firstIx > 0); }

long int at(struct OrderedCollection *anOC, int index) { return( anOC->theData[(anOC->firstIx) + index]); }

showOC(struct OrderedCollection *anOC) { int ix; printf("theData @%d firstIx=%d lastIx=%i   size=%i  capacity=%i\n", anOC->theData, anOC->firstIx, anOC->lastIx, sizeOC(anOC), anOC->capacity); if (isEmpty(anOC)) { printf("Empty\n"); } else { for (ix = anOC->firstIx; ix <= anOC->lastIx; ix++) { printf("%i: %i @%i: %i\n", ix-anOC->firstIx, ix, &anOC->theData[ix], anOC->theData[ix]); }   } }

growUp(struct OrderedCollection *anOC) { long int *oldData; int oldSize, ix; oldData = anOC->theData; oldSize = anOC->capacity; anOC->capacity = (anOC->capacity) * 2; anOC->theData = malloc((sizeof(long int *)) * anOC->capacity); for(ix=0; ixtheData[ix] = oldData[ix]; } free(oldData); }

void assureRoomAtLast(struct OrderedCollection *anOC) { if (hasRoomAtLast(anOC)) return; printf("***** growing up (at last)\n"); growUp(anOC); }

addLast(struct OrderedCollection *anOC, int anInt) { int ix; assureRoomAtLast(anOC); ix = (anOC->lastIx = anOC->lastIx + 1); anOC->theData[ix] = anInt; printf("addLast %i @%i: %i\n", ix, &anOC->theData[ix], anOC->theData[ix]); }

growDown(struct OrderedCollection *anOC) { long int *oldData; int oldSize, ix; oldData = anOC->theData; oldSize = anOC->capacity; anOC->capacity = (anOC->capacity) * 2; anOC->theData = malloc((sizeof(long int *)) * anOC->capacity); for(ix=0; ixtheData[ix+oldSize] = oldData[ix]; } anOC->firstIx = (anOC->firstIx) + oldSize; anOC->lastIx = (anOC->lastIx) + oldSize; free(oldData); }

void assureRoomAtFirst(struct OrderedCollection *anOC) { if (hasRoomAtFirst(anOC)) return; printf("***** growing down .... vvvvvvvvvvv\n"); growDown(anOC); }

addFirst(struct OrderedCollection *anOC, int anInt) { int ix; assureRoomAtFirst(anOC); ix = (anOC->firstIx = anOC->firstIx - 1); anOC->theData[ix] = anInt; printf("addFirst %i @%i: %i\n", ix, &anOC->theData[ix], anOC->theData[ix]); }

push(struct OrderedCollection *anOC, int anInt) { // alias for using anOC as a Stack addLast(anOC, anInt); }

sizeOC(struct OrderedCollection *anOC) { return(anOC->lastIx - anOC->firstIx + 1);  }

findPosition(struct OrderedCollection *anOC, int newInt) { // answer the relative index where the new guy would get stuck int ix; for(ix = 0; ix < sizeOC(anOC); ix++) { if(newInt < at(anOC, ix)) { return(ix); } } }

shoveUp(struct OrderedCollection *anOC, int startPos) { int ix; assureRoomAtLast(anOC); for (ix = anOC->lastIx; ix >= anOC->firstIx + startPos; ix--) { anOC->theData[ix+1] = anOC->theData[ix]; } anOC->lastIx = anOC->lastIx+1; // leaves the data with a duplicated item at startPos }

makeSpaceAtPosition(struct OrderedCollection *anOC, int aPos) { // Always increases the sizeOC, the item at aPos is ready to be over-written if (aPos <= 0) { assureRoomAtFirst(anOC); anOC->firstIx = anOC->firstIx -1; return; } if (aPos >= sizeOC(anOC)) { assureRoomAtLast(anOC); anOC->lastIx = anOC->lastIx + 1; return; } // until someone implements shoveDown... it always shoves Up. //   Legal, corecct, but a bit less efficient shoveUp(anOC, aPos); }

insertAt(struct OrderedCollection *anOC, int aPos, int newInt) { makeSpaceAtPosition(anOC, aPos); anOC->theData[aPos + anOC->firstIx] = newInt; }

swapPositions(struct OrderedCollection *anOC, int jPos, int kPos) { int tmp; tmp = anOC->theData[jPos + anOC->firstIx]; anOC->theData[jPos + anOC->firstIx] = anOC->theData[kPos + anOC->firstIx]; anOC->theData[kPos + anOC->firstIx] = tmp; }

main { int jx; initializeOC(myOC); showOC(myOC); for(jx=4; jx<1000; jx = jx*2 -1) { push(myOC, jx); showOC(myOC); }   printf("\nGoing down....................\n"); for(jx=-2; jx> -1000; jx = jx*2 -1) { addFirst(myOC, jx); showOC(myOC); }  printf("findIndex (0) = %i =? 9\n", findPosition(myOC, 0)); printf("findIndex (-1111) = %i =? 0\n", findPosition(myOC, -1111)); printf("findIndex (1024) = %i =? 18\n", findPosition(myOC, 1024)); shoveUp(myOC, 9); printf("See duplicate at pos 9???\n"); showOC(myOC); makeSpaceAtPosition(myOC, 5); printf("Do we have a duplicate at pos 5???\n"); showOC(myOC); insertAt(myOC, 0, 1000); insertAt(myOC, 10, 2000); insertAt(myOC, 22, 3333); showOC(myOC); swapPositions(myOC, 21, 1); showOC(myOC); }

code I discovered a couple of problems with the above code when students tried applying it in various platforms... I've just fixed that last version in place... see the history of the page if you want to see what has changed. I probably missed some "long"s and it won' twork as above. Caveat user.