Laboratory Solutions
test_stack.c
////////////////////////////////////////////////////////////////////////
// COMP2521 19T0 -- Test a Stack ADT implementation.
//
// 2018-11-29 Jashank Jeremy <jashankj@cse.unsw.edu.au>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "item.h"
#include "stack.h"
#include "testable.h"
static void test_empty_stack (void);
static void test_one_item_stack (void);
static void test_two_item_stack (void);
static void test_twenty_item_stack (void);
static void test_rolling_stack (void);
int main (void)
{
white_box_tests ();
puts ("");
test_empty_stack ();
test_one_item_stack ();
test_two_item_stack ();
test_twenty_item_stack ();
test_rolling_stack ();
puts ("\nAll tests passed. You are awesome!");
return EXIT_SUCCESS;
}
static void test_empty_stack (void)
{
puts ("Test 1: testing an empty stack.");
Stack s = stack_new ();
assert (stack_size (s) == 0);
stack_drop (s);
}
static void test_one_item_stack (void)
{
puts ("Test 2: testing a stack with one item.");
Stack s = stack_new ();
stack_push (s, 1);
assert (stack_size (s) == 1);
assert (stack_pop (s) == 1);
assert (stack_size (s) == 0);
stack_drop (s);
}
static void test_two_item_stack (void)
{
puts ("Test 3: testing a stack with two items.");
Stack s = stack_new ();
stack_push (s, 'a');
stack_push (s, 'b');
assert (stack_size (s) == 2);
assert (stack_pop (s) == 'b');
assert (stack_size (s) == 1);
assert (stack_pop (s) == 'a');
assert (stack_size (s) == 0);
stack_drop (s);
}
static void test_twenty_item_stack (void)
{
puts ("Test 4: testing a stack with twenty items.");
Stack s = stack_new ();
for (size_t i = 0; i < 20; i++)
stack_push (s, (Item) i);
assert (stack_size (s) == 20);
for (size_t i = 20; i > 0; i--) {
assert (stack_pop (s) == (Item) i - 1);
assert (stack_size (s) == i - 1);
}
stack_drop (s);
}
static void test_rolling_stack (void)
{
puts ("Test 5: testing a stack with churn.");
Stack s = stack_new ();
for (size_t n = 0; n < 5; n++) {
for (size_t i = 0; i < 80; i++)
stack_push (s, (Item) i);
assert (stack_size (s) == 80);
for (size_t i = 80; i > 0; i--) {
assert (stack_pop (s) == (Item) i - 1);
assert (stack_size (s) == i - 1);
}
}
stack_drop (s);
}
stack_array.c
////////////////////////////////////////////////////////////////////////
// COMP2521 19T0 -- A Stack ADT implementation, using arrays.
//
// 2018-11-29 Jashank Jeremy <jashankj@cse.unsw.edu.au>
// YYYY-mm-dd Your Name Here <zNNNNNNN@student.unsw.edu.au>
#include <assert.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include "item.h"
#include "stack.h"
#include "test1511.h"
#include "testable.h"
#define DEFAULT_SIZE 10
typedef struct stack stack;
struct stack {
size_t n_items;
size_t capacity;
Item *items;
};
static Item *stack_realloc_items (Item *its, size_t new_cap);
static bool stack_should_grow_p (stack *s);
static bool stack_should_shrink_p (stack *s);
/** Create a new, empty Stack. */
stack *stack_new (void)
{
stack *new = malloc (sizeof *new);
if (new == NULL)
err (EX_OSERR, "couldn't allocate stack");
(*new) = (stack) { .n_items = 0, .capacity = DEFAULT_SIZE };
new->items = calloc (DEFAULT_SIZE, sizeof(Item));
if (new->items == NULL)
err (EX_OSERR, "couldn't allocate stack items");
return new;
}
/** Destroy a Stack. */
void stack_drop (stack *s)
{
assert (s != NULL);
free (s->items);
free (s);
}
/** Add an item to the top of a Stack. */
void stack_push (stack *s, Item it)
{
assert (s != NULL);
if (stack_should_grow_p (s))
s->items = stack_realloc_items (s->items, s->capacity *= 2);
s->items[s->n_items] = it;
s->n_items++;
return;
}
/** Remove an item from the top of a Stack. */
Item stack_pop (stack *s)
{
assert (s != NULL);
Item it = s->items[s->n_items - 1];
s->n_items--;
if (stack_should_shrink_p (s))
s->items = stack_realloc_items (s->items, s->capacity /= 2);
return it;
}
/** Get the number of items in a Stack. */
size_t stack_size (stack *s)
{
assert (s != NULL);
return s->n_items;
}
/** Reallocate the items array, checking for _realloc(3)_ failure. */
static Item *stack_realloc_items (Item *its, size_t new_cap)
{
#ifdef DEBUG_STACK_ARRAY
warnx ("stack: realloc_items to %zu", new_cap);
#endif
Item *xs = realloc (its, new_cap * sizeof (Item));
if (xs == NULL) err (EX_OSERR, "couldn't realloc stack items");
return xs;
}
/** Should the stack grow? */
static bool stack_should_grow_p (stack *s)
{
return s->n_items == s->capacity
;
}
/** Should the stack shrink? */
static bool stack_should_shrink_p (stack *s)
{
return s->capacity >= s->n_items * 4
&& s->capacity >= 2 * DEFAULT_SIZE
;
}
#ifndef nitems
#define nitems(x) (sizeof((x))/(sizeof((x)[0])))
#endif
#define VT_INVERSE "\033[7m"
#define VT_NORMAL "\033[0m"
static inline void test_header (const char *, const char *);
static void wbt_stack_new (void);
static void wbt_stack_push (void);
static void wbt_stack_pop (void);
static void wbt_stack_size (void);
void white_box_tests (void)
{
wbt_stack_new ();
wbt_stack_push ();
wbt_stack_pop ();
wbt_stack_size ();
}
static inline void test_header (const char *n, const char *descr)
{
printf ("%s%s%s: %s\n", VT_INVERSE, n, VT_NORMAL, descr);
}
/** Test that `stack_new' and `stack_drop' work. */
static void wbt_stack_new (void)
{
test_header ("Test 1", "`stack_new', `stack_drop'");
stack *s = stack_new ();
assert (s != NULL);
assert (s->items != NULL);
assert (s->n_items == 0);
assert (s->capacity == DEFAULT_SIZE);
#ifdef IN_ASAN
assert (! mem_address_is_poisoned (s));
assert (! mem_region_is_poisoned (s, sizeof (stack)));
assert (! mem_address_is_poisoned (s->items));
assert (! mem_region_is_poisoned (
s->items, sizeof (Item) * DEFAULT_SIZE));
#endif
stack_drop (s);
#ifdef IN_ASAN
assert (mem_address_is_poisoned (s));
assert (mem_region_is_poisoned (s, sizeof (stack)));
#endif
}
/** Test that `stack_push' works. */
static void wbt_stack_push (void)
{
test_header ("Test 2", "`stack_push'");
stack s_ = (struct stack){
.n_items = 0,
.capacity = DEFAULT_SIZE,
.items = calloc (DEFAULT_SIZE, sizeof (Item))
};
stack *s = &s_;
assert (s->items != NULL);
Item items[] = {
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
};
for (size_t i = 0; i < nitems (items); i++) {
stack_push (s, items[i]);
assert (s->n_items == i + 1);
assert (
s->capacity ==
((s->n_items > DEFAULT_SIZE) ? 2 : 1) * DEFAULT_SIZE
);
for (size_t j = 0; j <= i; j++) {
assert (s->items[j] == items[j]);
}
}
free (s->items);
}
/** Test that `stack_pop' works. */
static void wbt_stack_pop (void)
{
test_header ("Test 3", "`stack_pop'");
Item items[] = {
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
};
stack s_ = (struct stack){
.n_items = nitems (items),
.capacity = 2 * DEFAULT_SIZE,
.items = calloc (2 * DEFAULT_SIZE, sizeof (Item))
};
stack *s = &s_;
assert (s->items != NULL);
memcpy (s->items, items, sizeof (items));
for (size_t i = 0; i < nitems (items); i++) {
Item it = stack_pop (s);
assert (it == s->items[nitems(items) - i - 1]);
assert (s->n_items == nitems(items) - i - 1);
assert (
s->capacity ==
((s->n_items <= DEFAULT_SIZE / 2) ? 1 : 2) * DEFAULT_SIZE
);
}
free (s->items);
}
/** Test that stack size functions (`stack_size', `stack_should_grow_p'
* and `stack_should_shrink_p') all work. */
static void wbt_stack_size (void)
{
test_header ("Test 4", "stack size functions");
stack s_ = (struct stack){
.n_items = 0,
.capacity = 10,
.items = NULL
};
stack *s = &s_;
assert (stack_size (s) == 0);
s->n_items = 10;
assert (stack_size (s) == 10);
assert (stack_should_grow_p (s) == true);
assert (stack_should_shrink_p (s) == false);
s->capacity = 20;
assert (stack_should_grow_p (s) == false);
assert (stack_should_shrink_p (s) == false);
s->capacity = 20;
s->n_items = 10;
assert (stack_should_grow_p (s) == false);
assert (stack_should_shrink_p (s) == false);
s->n_items = 5;
assert (stack_size (s) == 5);
assert (stack_should_grow_p (s) == false);
assert (stack_should_shrink_p (s) == true);
}
test_queue.c
////////////////////////////////////////////////////////////////////////
// COMP2521 19T0 -- Test a Queue ADT implementation.
//
// 2018-12-01 Jashank Jeremy <jashankj@cse.unsw.edu.au>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "item.h"
#include "queue.h"
#include "testable.h"
static void test_empty_queue (void);
static void test_one_item_queue (void);
static void test_two_item_queue (void);
static void test_ten_item_queue (void);
static void test_rolling_queue (void);
static void test_webcms3_2714021 (void);
int main (void)
{
white_box_tests ();
puts ("");
test_empty_queue ();
test_one_item_queue ();
test_two_item_queue ();
test_ten_item_queue ();
test_rolling_queue ();
test_webcms3_2714021 ();
puts ("\nAll tests passed. You are awesome!");
return EXIT_SUCCESS;
}
static void test_empty_queue (void)
{
puts ("Test 1: testing an empty queue.");
Queue q = queue_new ();
assert (queue_size (q) == 0);
queue_drop (q);
}
static void test_one_item_queue (void)
{
puts ("Test 2: testing a queue with one item.");
Queue q = queue_new ();
queue_en (q, 1);
assert (queue_size (q) == 1);
assert (queue_de (q) == 1);
assert (queue_size (q) == 0);
queue_drop (q);
}
static void test_two_item_queue (void)
{
puts ("Test 3: testing a queue with two items.");
Queue q = queue_new ();
queue_en (q, 'a');
queue_en (q, 'b');
assert (queue_size (q) == 2);
assert (queue_de (q) == 'a');
assert (queue_size (q) == 1);
assert (queue_de (q) == 'b');
assert (queue_size (q) == 0);
queue_drop (q);
}
static void test_ten_item_queue (void)
{
puts ("Test 4: testing a queue with ten items.");
Queue q = queue_new ();
for (size_t i = 0; i < 10; i++)
queue_en (q, (Item) i);
assert (queue_size (q) == 10);
for (size_t i = 0; i < 10; i++) {
assert (queue_de (q) == (Item) i);
assert (queue_size (q) == 10 - i - 1);
}
queue_drop (q);
}
static void test_rolling_queue (void)
{
puts ("Test 5: testing a queue with churn.");
Queue q = queue_new ();
assert (q != NULL);
assert (queue_size (q) == 0);
for (int i = 0; i < 10; i++)
queue_en (q, i);
assert (queue_size (q) == 10);
for (int i = 10; i < 100; i++) {
Item qv = queue_de (q); // get a value out
// printf ("%d ", qv);
assert (qv == i - 10); // make sure it's what we expect
assert (queue_size (q) == 9);
queue_en (q, i); // put another value in
assert (queue_size (q) == 10);
}
queue_drop (q);
}
static void test_webcms3_2714021 (void)
{
puts ("Test 6: /forums/2714016 fix");
Queue q = queue_new();
queue_en (q, 2);
assert (queue_de(q) == 2);
queue_en (q, 3); // fails here
assert (queue_de(q) == 3);
queue_drop (q);
}
queue_array.c
////////////////////////////////////////////////////////////////////////
// COMP2521 19T0 -- A Queue ADT implementation, using arrays.
//
// 2018-12-01 Jashank Jeremy <jashankj@cse.unsw.edu.au>
// YYYY-mm-dd Your Name Here <zNNNNNNN@student.unsw.edu.au>
#include <assert.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include "item.h"
#include "queue.h"
#include "testable.h"
#define DEFAULT_SIZE 10
typedef struct queue queue;
struct queue {
size_t n_items;
size_t capacity;
Item *items;
};
/** Create a new, empty Stack. $ O(1) $. */
queue *queue_new (void)
{
queue *new = malloc (sizeof *new);
if (new == NULL) err (EX_OSERR, "couldn't allocate queue");
(*new) = (queue) { .n_items = 0, .capacity = DEFAULT_SIZE };
new->items = calloc (DEFAULT_SIZE, sizeof(Item));
if (new->items == NULL)
err (EX_OSERR, "couldn't allocate queue items");
return new;
}
/** Destroy a Queue. $ O(1) $. */
void queue_drop (queue *q)
{
assert (q != NULL);
free (q->items);
free (q);
}
/** Add an item to the end of a Queue. $ O(1) $.
* Sometimes referred to as "enqueue" or "unshift". */
void queue_en (queue *q, Item it)
{
assert (q != NULL);
assert (q->n_items < q->capacity);
q->items[q->n_items++] = it;
}
/** Remove an item from the front of a Queue. $ O(n) $.
* Sometimes referred to as "dequeue" or "shift". */
Item queue_de (queue *q)
{
assert (q != NULL);
if (q->n_items == 0) {
fprintf (stderr, "queue underflow!\n");
abort();
}
Item it = q->items[0];
q->n_items--;
// shift the elements across; still O(n) but faster?
memmove (&q->items[0], &q->items[1], q->n_items * sizeof (Item));
return it;
}
/** Get the number of items in a Queue. $ O(1) $. */
size_t queue_size (queue *q)
{
assert (q != NULL);
return q->n_items;
}
void white_box_tests (void)
{
// ... you need to write these!
}
queue_list.c
////////////////////////////////////////////////////////////////////////
// COMP2521 19T0 -- A Queue ADT implementation, using linked lists.
//
// 2018-12-01 Jashank Jeremy <jashankj@cse.unsw.edu.au>
#include <assert.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include "item.h"
#include "queue.h"
#include "testable.h"
typedef struct queue queue;
typedef struct queue_node queue_node;
struct queue {
size_t n_items;
queue_node *head, *tail;
};
struct queue_node {
Item item;
queue_node *next;
};
static queue_node *queue_node_new (Item);
/** Create a new, empty Queue. */
queue *queue_new (void)
{
queue *new = malloc (sizeof *new);
if (new == NULL) err (EX_OSERR, "couldn't allocate queue");
(*new) = (queue) { .n_items = 0, .head = NULL, .tail = NULL };
return new;
}
/** Destroy a Queue, releasing all resources associated with it. */
void queue_drop (queue *q)
{
assert (q != NULL);
queue_node *curr = q->head;
while (curr != NULL) {
queue_node *tmp = curr->next;
free (curr);
curr = tmp;
}
free (q);
}
/** Add an item to the end of a Queue.
* Sometimes referred to as "enqueue" or "unshift". */
void queue_en (queue *q, Item it)
{
assert (q != NULL);
queue_node *node = queue_node_new (it);
if (q->head == NULL) {
assert (q->head == NULL && q->tail == NULL && q->n_items == 0);
q->head = node;
} else {
assert (q->head != NULL && q->tail != NULL && q->n_items != 0);
q->tail->next = node;
}
q->tail = node;
q->n_items++;
return;
}
/** Remove an item from the front of a Queue.
* Sometimes referred to as "dequeue" or "shift". */
Item queue_de (queue *q)
{
assert (q != NULL);
if (q->head == NULL) {
fprintf (stderr, "queue underflow!\n");
assert (q->n_items == 0);
abort();
}
queue_node *del = q->head;
Item it = del->item;
q->head = del->next;
if (q->head == NULL)
q->tail = NULL;
q->n_items--;
free (del);
return it;
}
/** Get the number of items in a Queue. */
size_t queue_size (queue *q)
{
assert (q != NULL);
return q->n_items;
}
static queue_node *queue_node_new (Item it)
{
queue_node *new = malloc (sizeof *new);
if (new == NULL) err (EX_OSERR, "couldn't allocate queue_node");
(*new) = (queue_node) { .item = it, .next = NULL };
return new;
}
void white_box_tests (void)
{
// ... you need to write these!
}