Initial copy
This commit is contained in:
parent
86d2e00bf0
commit
5cf362c910
|
@ -5,9 +5,22 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y apache2
|
|||
|
||||
RUN rm /etc/apache2/sites-enabled/000-default.conf
|
||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y postgresql libapache2-mod-wsgi-py3
|
||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y virtualenv
|
||||
# make for testing
|
||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y virtualenv make sudo
|
||||
|
||||
RUN ln -s /data/dev/apache-local.conf /etc/apache2/sites-enabled/
|
||||
#RUN a2enmod rewrite
|
||||
|
||||
|
||||
RUN sed -i "s#data_directory = '.*'#data_directory = '/data/run/postgres'#" /etc/postgresql/10/main/postgresql.conf
|
||||
RUN echo "host all all 0.0.0.0/0 md5" >> /etc/postgresql/10/main/pg_hba.conf
|
||||
RUN echo "listen_addresses='*'" >> /etc/postgresql/10/main/postgresql.conf
|
||||
RUN /etc/init.d/postgresql restart
|
||||
|
||||
RUN sudo -u postgres psql -c "CREATE USER hoou WITH PASSWORD 'hoohoohoo123';"
|
||||
RUN sudo -u postgres psql -c "CREATE DATABASE hoou;"
|
||||
RUN sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE hoou to hoou;"
|
||||
|
||||
# psql -h localhost -U hoou hoou # should work
|
||||
|
||||
CMD /data/dev/run-internal.sh
|
||||
|
|
|
@ -1,61 +1,24 @@
|
|||
<VirtualHost *:80>
|
||||
ServerAdmin admin@wr.informatik.uni-hamburg.de
|
||||
ServerName oer.wr.informatik.uni-hamburg.de
|
||||
ServerAdmin admin@hps.vi4io.org
|
||||
#ServerName oer.hps.vi4io.org
|
||||
|
||||
Redirect permanent / https://oer.wr.informatik.uni-hamburg.de/
|
||||
WSGIScriptAlias / "/data/src/main/wsgi.py"
|
||||
WSGIDaemonProcess hoou python-home=/data/run/virtualenv python-path=/data/src/ home=/data/src/ inactivity-timeout=10 request-timeout=10
|
||||
WSGIProcessGroup hoou
|
||||
WSGIApplicationGroup %{GLOBAL}
|
||||
|
||||
<Directory />
|
||||
Options SymLinksIfOwnerMatch
|
||||
AllowOverride None
|
||||
Require all denied
|
||||
DocumentRoot /data/src/
|
||||
Alias /static/ "/data/run/static/"
|
||||
|
||||
<Directory "/data/run/static/">
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<Directory "/data/src/main/">
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
LogLevel warn
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
</VirtualHost>
|
||||
|
||||
|
||||
<IfModule mod_ssl.c>
|
||||
<VirtualHost *:443>
|
||||
ServerAdmin admin@wr.informatik.uni-hamburg.de
|
||||
ServerName oer.wr.informatik.uni-hamburg.de
|
||||
|
||||
#RewriteEngine on
|
||||
#RewriteRule "^/$" "http://cluster.wr.informatik.uni-hamburg.de:8000/" [R,L]
|
||||
#RewriteRule "^/(.+)$" "http://cluster.wr.informatik.uni-hamburg.de:8000/$1" [R,L]
|
||||
|
||||
WSGIScriptAlias / "/home/hoou/git/HOOU/django-platform/main/wsgi.py"
|
||||
WSGIDaemonProcess hoou python-home=/home/hoou/git/HOOU/virtualenv python-path=/home/hoou/git/HOOU/django-platform/ home=/home/hoou/git/HOOU/django-platform/ inactivity-timeout=10 request-timeout=10
|
||||
WSGIProcessGroup hoou
|
||||
WSGIApplicationGroup %{GLOBAL}
|
||||
|
||||
DocumentRoot /home/hoou/git/HOOU/django-platform/
|
||||
Alias /static/ "/home/hoou/git/HOOU/django-platform/apache/static/"
|
||||
|
||||
<Directory "/home/hoou/git/HOOU/django-platform/apache/static/">
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<Directory "/home/hoou/git/HOOU/django-platform/main/">
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error-oer.log
|
||||
LogLevel warn
|
||||
CustomLog ${APACHE_LOG_DIR}/oer-access.log combined
|
||||
|
||||
SSLEngine on
|
||||
SSLCertificateFile /etc/letsencrypt/live/oer.wr.informatik.uni-hamburg.de/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/oer.wr.informatik.uni-hamburg.de/privkey.pem
|
||||
|
||||
|
||||
Alias "/.well-known/acme-challenge/" "/var/www/certbot/.well-known/acme-challenge/"
|
||||
|
||||
<Directory /var/www/certbot>
|
||||
Options SymLinksIfOwnerMatch
|
||||
AllowOverride None
|
||||
Require all granted
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
</IfModule>
|
||||
|
|
|
@ -7,5 +7,5 @@ Sphinx
|
|||
pymongo
|
||||
celery
|
||||
markdown
|
||||
psycopg2
|
||||
psycopg2-binary
|
||||
social-auth-app-django
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
#!/bin/bash -e
|
||||
adduser --system --no-create-home --home /data --uid 1000 www-user
|
||||
sed -i "s/APACHE_RUN_USER=www-data/APACHE_RUN_USER=www-user/" /etc/apache2/envvars
|
||||
sed -i "s#data_directory = '.*'#data_directory = '/data/run/postgres'#" /etc/postgresql/10/main/postgresql.conf
|
||||
|
||||
/etc/init.d/postgresql restart
|
||||
|
||||
/etc/init.d/apache2 start
|
||||
tail -f /var/log/apache2/error.log &
|
||||
|
||||
export OER_SRC_DIR=/data/oer
|
||||
export PLATFORM_PATH=/data/src
|
||||
|
||||
V="/data/run/virtualenv"
|
||||
if [[ ! -e $V ]] ; then
|
||||
mkdir -p $V
|
||||
|
@ -11,6 +18,10 @@ if [[ ! -e $V ]] ; then
|
|||
cd $V
|
||||
source $V/bin/activate
|
||||
pip3 install -U -r /data/dev/requirements.txt
|
||||
python3 ./manage.py migrate
|
||||
python3 ./manage.py collectstatic
|
||||
fi
|
||||
source $V/bin/activate
|
||||
|
||||
|
||||
/bin/bash
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# Open Educational Resources
|
||||
|
||||
While many standards for open educational resources (OER) are available.
|
||||
Unfortunetly many ignore the special needs for automatic evaluation and grading
|
||||
for programming courses and parallel programming in particular. As such, until
|
||||
a more universal format is determined, this project sticks with a very
|
||||
simple file and directory based format and mostly plaintext based metadata.
|
||||
Tools to work with the web platform and execution runners are provided.
|
||||
|
||||
## Structure
|
||||
|
||||
Lectures/courses and exercises are decoupled, to allow for multiple courses to
|
||||
use the same exercise.
|
||||
|
||||
./courses # a course allows to specify a sequence or directed graph of excercises
|
||||
./exercises # provides a pool of exercises which can be used to create courses
|
|
@ -0,0 +1 @@
|
|||
{"title": "C Advanced", "description": "Some further material for the C-Programming-Language"}
|
|
@ -0,0 +1,10 @@
|
|||
Welcome to the C-Advanced course. This course is targeted for those,
|
||||
who feel comfortable with pointers, memory management and all the basic
|
||||
c syntax.
|
||||
|
||||
We will guide you on, how to use c to implement your own data-structures,
|
||||
writing your own libraries and give you some more advanced task to
|
||||
test your abilities.
|
||||
|
||||
Most of these task can take some time to get everything right. So don't be discouraged, whenn
|
||||
need to take a break. Often those breaks help one see the mistakes or misunderstandings.
|
|
@ -0,0 +1 @@
|
|||
{"title": "Introduction"}
|
|
@ -0,0 +1 @@
|
|||
text/html
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
all:
|
||||
gcc program.c -o program
|
||||
#gcc solution.c -o solution
|
||||
|
||||
clean:
|
||||
rm -f program
|
||||
rm -f solution
|
|
@ -0,0 +1,28 @@
|
|||
The first basic data-structure everybody should learn is the singly linked list.
|
||||
|
||||
In a singly linked list you save your data in nodes, a combination of data and a pointer
|
||||
to the next node. To add nodes you simply allocate them and rewire the pointers,
|
||||
such that it appears in the right spot in the list.
|
||||
|
||||
To remove an element you have to rewire the previous and next node, such that their
|
||||
pointers point to each other, and then free the memory.
|
||||
|
||||
The idea behind this data-structures is, that you can add and remove elements easily
|
||||
from the end, front and middle of the list. And because you just allocate more memory,
|
||||
the list can grow dynamically.
|
||||
|
||||
The problem is to get to the elements.
|
||||
Because they are allocated on the heap the elements are all over the place and can only
|
||||
be found via the pointers. So, when you want to get to the middle, you have to traverse
|
||||
the linked list.
|
||||
|
||||
Another downside of singly linked list is, that you can't go backwards.
|
||||
That is were the doubly linked list comes in, but more on that later.
|
||||
|
||||
The last disadvantage is, when you want to process lot's of data in a short amount of time,
|
||||
you are often limited by cache misses. And linked list in general are terrible for that kind
|
||||
of work, because most elements aren't in the same cache line.
|
||||
|
||||
Your task will be to implement the functions for add, insert_after, remove and get.
|
||||
It is often helpful to draw the operations you want to do on paper or
|
||||
look at some pictures.
|
|
@ -0,0 +1 @@
|
|||
{"title": "Singly Linked List"}
|
|
@ -0,0 +1,66 @@
|
|||
#include <stdio.h>
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
char Character;
|
||||
struct node *Next;
|
||||
}node;
|
||||
|
||||
// NOTE: adds an element to the beginning
|
||||
void list_add(node **Head, char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
// NOTE: inserts after a specific node
|
||||
void list_insert_after(node *Node, char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
// NOTE: get's a node with the specified character
|
||||
node *list_get(node **Head, char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE: removes a specific node from the list
|
||||
void list_remove(node **Head, node *Node)
|
||||
{
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
// NOTE: frees the whole list
|
||||
void list_free(node **Head)
|
||||
{
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// NOTE: beginning of the list
|
||||
// right now the list is empty
|
||||
node *Head = 0;
|
||||
|
||||
list_add(&Head, 'c');
|
||||
list_add(&Head, 'y');
|
||||
list_add(&Head, 'b');
|
||||
|
||||
node *ToBeRemoved = list_get(&Head, 'y');
|
||||
list_remove(Head, ToBeRemoved);
|
||||
|
||||
list_add(&Head, 'a');
|
||||
node *InsertAfter = list_get(&Head, 'c');
|
||||
list_insert_after(InsertAfter, 'd');
|
||||
|
||||
for(node *Iter = Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
printf("%c ", Iter->Character);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
list_free(Head);
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
^a b c d $
|
|
@ -0,0 +1 @@
|
|||
program/match-regex
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
all:
|
||||
gcc program.c -o program
|
||||
#gcc solution.c -o solution
|
||||
|
||||
clean:
|
||||
rm -f program
|
||||
rm -f solution
|
|
@ -0,0 +1,11 @@
|
|||
The next step up from a sinlgy linked list is the double linked list.
|
||||
It is basically the same. It just stores another pointer for the previous node.
|
||||
This makes some operations way easier and some a little harder.
|
||||
But most of the time it is a trade of worth taking.
|
||||
|
||||
A common thing people do is to make double linked list(in short dlist) circular.
|
||||
To mark the head and the tail they use a special node called the sentinel.
|
||||
This makes some operations easier, but for now we will concentrate on the basic dlist.
|
||||
|
||||
Your task is to change the singly linked list to a double linked list.
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"title": "Double Linked List"}
|
|
@ -0,0 +1,111 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
char Character;
|
||||
struct node *Next;
|
||||
struct node *Prev;
|
||||
}node;
|
||||
|
||||
// TODO: change all the function to use a dlinked list
|
||||
node *alloc_node(char c, node *Next, node *Prev)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Character = c;
|
||||
NewNode->Next = Next;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
// TIP: In the next 2 function you probably need to check for null-pointers
|
||||
// NOTE: adds an element to the beginning
|
||||
void list_add(node **Head, char c)
|
||||
{
|
||||
node *NewNode = alloc_node(c, *Head, 0);
|
||||
*Head = NewNode;
|
||||
}
|
||||
|
||||
// NOTE: inserts after a specific node
|
||||
void list_insert_after(node *Node, char c)
|
||||
{
|
||||
node *NewNode = alloc_node(c, Node->Next, 0);
|
||||
Node->Next = NewNode;
|
||||
}
|
||||
|
||||
// NOTE: get's a node with the specified character
|
||||
node *list_get(node **Head, char c)
|
||||
{
|
||||
node *Result;
|
||||
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
if(Iter->Character == c)
|
||||
{
|
||||
Result = Iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
// NOTE: removes a specific node from the list
|
||||
void list_remove(node **Head, node *Node)
|
||||
{
|
||||
// NOTE: this should get simpler
|
||||
// Find prev node
|
||||
node *Prev = 0;
|
||||
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
if(Iter == Node)
|
||||
break;
|
||||
Prev = Iter;
|
||||
}
|
||||
if(Prev)
|
||||
Prev->Next = Node->Next;
|
||||
free(Node);
|
||||
}
|
||||
|
||||
// NOTE: frees the whole list
|
||||
void list_free(node **Head)
|
||||
{
|
||||
for(node *Iter = *Head; Iter; )
|
||||
{
|
||||
node *ToFree = Iter;
|
||||
Iter = Iter->Next;
|
||||
|
||||
free(ToFree);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
node *Head = 0;
|
||||
|
||||
list_add(&Head, 'c');
|
||||
list_add(&Head, 'y');
|
||||
list_add(&Head, 'b');
|
||||
|
||||
node *ToBeRemoved = list_get(&Head, 'y');
|
||||
list_remove(&Head, ToBeRemoved);
|
||||
|
||||
list_add(&Head, 'a');
|
||||
node *InsertAfter = list_get(&Head, 'c');
|
||||
list_insert_after(InsertAfter, 'd');
|
||||
|
||||
// NOTE: prints out the whole list
|
||||
for(node *Iter = Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
printf("%c ", Iter->Character);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// NOTE: prints out the previous Node of B, B and the next one
|
||||
node *B = list_get(&Head, 'b');
|
||||
if(B)
|
||||
{
|
||||
printf("%s %c %s\n",B->Prev? B->Prev: "Null", B->Character, B->Next? B->Next: "Null");
|
||||
}
|
||||
|
||||
list_free(&Head);
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
^a b c d\s*a b c\s*$
|
|
@ -0,0 +1,113 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
char Character;
|
||||
struct node *Next;
|
||||
struct node *Prev;
|
||||
}node;
|
||||
|
||||
// TODO: chane all the function to use a dlinked list
|
||||
node *alloc_node(char c, node *Next, node *Prev)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Character = c;
|
||||
NewNode->Next = Next;
|
||||
NewNode->Prev = Prev;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
// NOTE: adds an element to the beginning
|
||||
void list_add(node **Head, char c)
|
||||
{
|
||||
node *NewNode = alloc_node(c, *Head, 0);
|
||||
if(*Head)
|
||||
(*Head)->Prev = NewNode;
|
||||
*Head = NewNode;
|
||||
}
|
||||
|
||||
// NOTE: inserts after a specific node
|
||||
void list_insert_after(node *Node, char c)
|
||||
{
|
||||
node *NewNode = alloc_node(c, Node->Next, Node);
|
||||
node *Next = Node->Next;
|
||||
if(Next)
|
||||
{
|
||||
Next->Prev = NewNode;
|
||||
}
|
||||
Node->Next = NewNode;
|
||||
}
|
||||
|
||||
// NOTE: get's a node with the specified character
|
||||
node *list_get(node **Head, char c)
|
||||
{
|
||||
node *Result;
|
||||
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
if(Iter->Character == c)
|
||||
{
|
||||
Result = Iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
// NOTE: removes a specific node from the list
|
||||
void list_remove(node **Head, node *Node)
|
||||
{
|
||||
// NOTE: this should get simpler
|
||||
// Find prev node
|
||||
node *Prev = Node->Prev;
|
||||
if(Prev)
|
||||
Prev->Next = Node->Next;
|
||||
node *Next = Node->Next;
|
||||
Next->Prev = Prev;
|
||||
|
||||
free(Node);
|
||||
}
|
||||
|
||||
// NOTE: frees the whole list
|
||||
void list_free(node **Head)
|
||||
{
|
||||
for(node *Iter = *Head; Iter; )
|
||||
{
|
||||
node *ToFree = Iter;
|
||||
Iter = Iter->Next;
|
||||
|
||||
free(ToFree);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
node *Head = 0;
|
||||
|
||||
list_add(&Head, 'c');
|
||||
list_add(&Head, 'y');
|
||||
list_add(&Head, 'b');
|
||||
|
||||
node *ToBeRemoved = list_get(&Head, 'y');
|
||||
list_remove(&Head, ToBeRemoved);
|
||||
|
||||
list_add(&Head, 'a');
|
||||
node *InsertAfter = list_get(&Head, 'c');
|
||||
list_insert_after(InsertAfter, 'd');
|
||||
|
||||
// NOTE: Prints out the whole list
|
||||
for(node *Iter = Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
printf("%c ", Iter->Character);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
node *B = list_get(&Head, 'b');
|
||||
if(B)
|
||||
{
|
||||
printf("%s %c %s\n",B->Prev? B->Prev: "Null", B->Character, B->Next? B->Next: "Null");
|
||||
}
|
||||
list_free(&Head);
|
||||
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
char Character;
|
||||
struct node *Next;
|
||||
}node;
|
||||
|
||||
node *alloc_node(char c, node *Next)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Character = c;
|
||||
NewNode->Next = Next;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
// NOTE: adds an element to the beginning
|
||||
void list_add(node **Head, char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *NewNode = alloc_node(c, *Head);
|
||||
*Head = NewNode;
|
||||
}
|
||||
|
||||
// NOTE: inserts after a specific node
|
||||
void list_insert_after(node *Node, char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *NewNode = alloc_node(c, Node->Next);
|
||||
Node->Next = NewNode;
|
||||
}
|
||||
|
||||
// NOTE: get's a node with the specified character
|
||||
node *list_get(node **Head, char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *Result;
|
||||
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
if(Iter->Character == c)
|
||||
{
|
||||
Result = Iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
// NOTE: removes a specific node from the list
|
||||
void list_remove(node **Head, node *Node)
|
||||
{
|
||||
// TODO: implement this
|
||||
// Find prev node
|
||||
node *Prev = 0;
|
||||
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
if(Iter == Node)
|
||||
break;
|
||||
Prev = Iter;
|
||||
}
|
||||
if(Prev)
|
||||
Prev->Next = Node->Next;
|
||||
free(Node);
|
||||
}
|
||||
|
||||
// NOTE: frees the whole list
|
||||
void list_free(node **Head)
|
||||
{
|
||||
for(node *Iter = *Head; Iter; )
|
||||
{
|
||||
node *ToFree = Iter;
|
||||
Iter = Iter->Next;
|
||||
|
||||
free(ToFree);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// NOTE: beginning of the list
|
||||
// right now the list is empty
|
||||
node *Head = 0;
|
||||
|
||||
list_add(&Head, 'c');
|
||||
list_add(&Head, 'y');
|
||||
list_add(&Head, 'b');
|
||||
|
||||
node *ToBeRemoved = list_get(&Head, 'y');
|
||||
list_remove(&Head, ToBeRemoved);
|
||||
|
||||
list_add(&Head, 'a');
|
||||
node *InsertAfter = list_get(&Head, 'c');
|
||||
list_insert_after(InsertAfter, 'd');
|
||||
|
||||
for(node *Iter = Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
printf("%c ", Iter->Character);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
list_free(&Head);
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
program/match-regex
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
all:
|
||||
gcc program.c -o program
|
||||
#gcc solution.c -o solution
|
||||
|
||||
clean:
|
||||
rm -f program
|
||||
rm -f solution
|
|
@ -0,0 +1,21 @@
|
|||
The next data-structure is the hash table.
|
||||
The hash table solves the problem of off having some key associated with
|
||||
your data and wanting to get the entry in a table.
|
||||
|
||||
In an normal array you would have to search the whole list. The same is
|
||||
true for linked list. A hash table solves this problem by using the key as the index into an array.
|
||||
|
||||
The key is normally referred to as the hash and is computed by putting your data trough a hash-function.
|
||||
|
||||
The first problem you can easily imagine is, that two different entries get the same hash. This
|
||||
is called a collision. There are different strategies of solving those collisions.
|
||||
Some are inline solutions and use the next indices to store the entry others require other data-structures.
|
||||
|
||||
Our hash-table will use a dlinked-list for these collision. If we detect a collision the new entry is
|
||||
saved as the head of the linked list.
|
||||
|
||||
A downside of a hash table is that it is hard to balance wasted space and avoiding collision.
|
||||
You have to have a good hash function and good testing to determine with what kind of space you can
|
||||
get away with.
|
||||
|
||||
You task is to implement the hash table.
|
|
@ -0,0 +1,53 @@
|
|||
node *alloc_node(int Value, int x, int y, node *Next)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Value = Value;
|
||||
NewNode->x = x;
|
||||
NewNode->y = y;
|
||||
NewNode->Next = Next;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
void list_add(node **Head, int Value, int x, int y)
|
||||
{
|
||||
node *NewNode = alloc_node(Value, x, y, *Head);
|
||||
*Head = NewNode;
|
||||
}
|
||||
|
||||
node *list_get(node **Head, int x, int y)
|
||||
{
|
||||
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
if(Iter->x == x && Iter->y == y)
|
||||
{
|
||||
return Iter;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void list_remove(node **Head, node *Node)
|
||||
{
|
||||
node *Prev = 0;
|
||||
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
if(Iter == Node)
|
||||
break;
|
||||
Prev = Iter;
|
||||
}
|
||||
if(Prev)
|
||||
Prev->Next = Node->Next;
|
||||
free(Node);
|
||||
}
|
||||
|
||||
void list_free(node **Head)
|
||||
{
|
||||
for(node *Iter = *Head; Iter; )
|
||||
{
|
||||
node *ToFree = Iter;
|
||||
Iter = Iter->Next;
|
||||
|
||||
free(ToFree);
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{"title": "Hash Table"}
|
|
@ -0,0 +1,110 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define HASH_TABLE_SIZE 32
|
||||
|
||||
// NOTE: the data is some value, which is on a certain 2D point (x,y)
|
||||
typedef struct node
|
||||
{
|
||||
// the data
|
||||
int x,y;
|
||||
float Value;
|
||||
|
||||
// next entry in the hash-table, if there were a collision
|
||||
struct node *Next;
|
||||
}node;
|
||||
|
||||
#include "linked_list.h"
|
||||
|
||||
/*
|
||||
* Linked-list interface
|
||||
*
|
||||
* void list_add(node **Head, int Value, int x, int y);
|
||||
*
|
||||
* node *list_get(node **Head, int x, int y);
|
||||
*
|
||||
* void list_remove(node **Head, node *Node);
|
||||
*
|
||||
*/
|
||||
|
||||
// NOTE: this computes the hash slot of the entry
|
||||
int hash(int x, int y)
|
||||
{
|
||||
int Result = (x + y)%HASH_TABLE_SIZE;
|
||||
|
||||
if(Result < 0)
|
||||
Result *= -1;
|
||||
return Result;
|
||||
}
|
||||
|
||||
// NOTE: adds a new entry to the hash table.
|
||||
// new entries go at the head of the linked-list
|
||||
void hash_add(node **HashTable, float Value, int x, int y)
|
||||
{
|
||||
// Don't forget to update the hash-tables pointer to the head.
|
||||
}
|
||||
|
||||
// NOTE: gets the note at the position (x,y)
|
||||
node *hash_get(node **HashTable, int x, int y)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE: removes the node at position (x,y),
|
||||
// if there is one
|
||||
void hash_remove(node **HashTable, int x, int y)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char *argv)
|
||||
{
|
||||
node *HashTable[HASH_TABLE_SIZE] = {};
|
||||
|
||||
|
||||
// NOTE: some test values
|
||||
hash_add(HashTable, 1.0, 1, 0);
|
||||
hash_add(HashTable, 3.0, 0, 1);
|
||||
hash_add(HashTable, 5.0, 3, -2);
|
||||
hash_add(HashTable, 7.0, 10, 3);
|
||||
|
||||
// NOTE: test adding with collisions
|
||||
node *Node = hash_get(HashTable, 1, 0);
|
||||
if(Node)
|
||||
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||
else
|
||||
printf("Failed to add or find value 1 at position (1,0)\n");
|
||||
|
||||
Node = hash_get(HashTable, 0, 1);
|
||||
int added01 = 0;
|
||||
if(Node)
|
||||
{
|
||||
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||
added01 = 1;
|
||||
}
|
||||
else
|
||||
printf("Failed to add or find value 3 at position (0,1)\n");
|
||||
|
||||
Node = hash_get(HashTable, 3, -2);
|
||||
if(Node)
|
||||
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||
else
|
||||
printf("Failed to add or find value 5 at position (-2,-2)\n");
|
||||
|
||||
Node = hash_get(HashTable, 10, 3);
|
||||
if(Node)
|
||||
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||
else
|
||||
printf("Failed to add or find value 7 at position (10,3)\n");
|
||||
|
||||
// NOTE: test removing element
|
||||
if(added01)
|
||||
{
|
||||
hash_remove(HashTable, 0, 1);
|
||||
Node = hash_get(HashTable, 0, 1);
|
||||
if(!Node)
|
||||
printf("Succesfully removed entrie at position (0,1) from the HashTable\n");
|
||||
else
|
||||
printf("Failed to remove entrie at position (0,1)\n");
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
^Succesfully added value 1.000000 at position (1,0) to the HashTable\nSuccesfully added value 3.000000 at position (0,1) to the HashTable\nSuccesfully added value 5.000000 at position (3,-2) to the HashTable\nSuccesfully added value 7.000000 at position (10,3) to the HashTable\nSuccesfully removed entrie at position (0,1) from the HashTable$
|
|
@ -0,0 +1,116 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define HASH_TABLE_SIZE 32
|
||||
|
||||
// NOTE: the data is some value, which is on a certain 2D point (x,y)
|
||||
typedef struct node
|
||||
{
|
||||
// the data
|
||||
int x,y;
|
||||
float Value;
|
||||
|
||||
// next entrie in the hashtable, if there were a collision
|
||||
struct node *Next;
|
||||
}node;
|
||||
|
||||
#include "linked_list.h"
|
||||
|
||||
/*
|
||||
* Linked-list interface
|
||||
*
|
||||
* void list_add(node **Head, int Value, int x, int y);
|
||||
*
|
||||
* node *list_get(node **Head, int x, int y);
|
||||
*
|
||||
* void list_remove(node **Head, node *Node);
|
||||
*
|
||||
*/
|
||||
|
||||
int hash(int x, int y)
|
||||
{
|
||||
int Result = (x + y)%HASH_TABLE_SIZE;
|
||||
|
||||
if(Result < 0)
|
||||
Result *= -1;
|
||||
return Result;
|
||||
}
|
||||
|
||||
void hash_add(node **HashTable, float Value, int x, int y)
|
||||
{
|
||||
int Hash = hash(x,y);
|
||||
node *Head = HashTable[Hash];
|
||||
list_add(&Head, Value, x, y);
|
||||
|
||||
// Don't forget to update the Hashtables pointer to the head.
|
||||
HashTable[Hash] = Head;
|
||||
}
|
||||
node *hash_get(node **HashTable, int x, int y)
|
||||
{
|
||||
int Hash = hash(x,y);
|
||||
node *Head = HashTable[Hash];
|
||||
node *Result = list_get(&Head, x, y);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
void hash_remove(node **HashTable, int x, int y)
|
||||
{
|
||||
int Hash = hash(x,y);
|
||||
node *Head = HashTable[Hash];
|
||||
node *Node = list_get(&Head, x, y);
|
||||
if(Node)
|
||||
list_remove(&Head, Node);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv)
|
||||
{
|
||||
node *HashTable[HASH_TABLE_SIZE] = {};
|
||||
|
||||
//payload D1 = {0, 0, 1.0, 0};
|
||||
|
||||
hash_add(HashTable, 1.0, 1, 0);
|
||||
hash_add(HashTable, 3.0, 0, 1);
|
||||
hash_add(HashTable, 5.0, 3, -2);
|
||||
hash_add(HashTable, 7.0, 10, 3);
|
||||
|
||||
// Note: Test adding with collisions
|
||||
node *Node = hash_get(HashTable, 1, 0);
|
||||
if(Node)
|
||||
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||
else
|
||||
printf("Failed to add or find value 1 at position (1,0)\n");
|
||||
|
||||
Node = hash_get(HashTable, 0, 1);
|
||||
int added01 = 0;
|
||||
if(Node)
|
||||
{
|
||||
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||
added01 = 1;
|
||||
}
|
||||
else
|
||||
printf("Failed to add or find value 3 at position (0,1)\n");
|
||||
|
||||
Node = hash_get(HashTable, 3, -2);
|
||||
if(Node)
|
||||
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||
else
|
||||
printf("Failed to add or find value 5 at position (-2,-2)\n");
|
||||
|
||||
Node = hash_get(HashTable, 10, 3);
|
||||
if(Node)
|
||||
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||
else
|
||||
printf("Failed to add or find value 7 at position (10,3)\n");
|
||||
|
||||
// NOTE: Test removing element
|
||||
if(added01)
|
||||
{
|
||||
hash_remove(HashTable, 0, 1);
|
||||
Node = hash_get(HashTable, 0, 1);
|
||||
if(!Node)
|
||||
printf("Succesfully removed entrie at position (0,1) from the HashTable\n");
|
||||
else
|
||||
printf("Failed to remove entrie at position (0,1)\n");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
char Character;
|
||||
struct node *Next;
|
||||
}node;
|
||||
|
||||
node *alloc_node(char c, node *Next)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Character = c;
|
||||
NewNode->Next = Next;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
// NOTE: adds an element to the beginning
|
||||
void list_add(node **Head, char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *NewNode = alloc_node(c, *Head);
|
||||
*Head = NewNode;
|
||||
}
|
||||
|
||||
// NOTE: inserts after a specific node
|
||||
void list_insert_after(node *Node, char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *NewNode = alloc_node(c, Node->Next);
|
||||
Node->Next = NewNode;
|
||||
}
|
||||
|
||||
// NOTE: get's a node with the specified character
|
||||
node *list_get(node **Head, char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *Result;
|
||||
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
if(Iter->Character == c)
|
||||
{
|
||||
Result = Iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
// NOTE: removes a specific node from the list
|
||||
void list_remove(node **Head, node *Node)
|
||||
{
|
||||
// TODO: implement this
|
||||
// Find prev node
|
||||
node *Prev = 0;
|
||||
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
if(Iter == Node)
|
||||
break;
|
||||
Prev = Iter;
|
||||
}
|
||||
if(Prev)
|
||||
Prev->Next = Node->Next;
|
||||
free(Node);
|
||||
}
|
||||
|
||||
// NOTE: frees the whole list
|
||||
void list_free(node **Head)
|
||||
{
|
||||
for(node *Iter = *Head; Iter; )
|
||||
{
|
||||
node *ToFree = Iter;
|
||||
Iter = Iter->Next;
|
||||
|
||||
free(ToFree);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// NOTE: beginning of the list
|
||||
// right now the list is empty
|
||||
node *Head = 0;
|
||||
|
||||
list_add(&Head, 'c');
|
||||
list_add(&Head, 'y');
|
||||
list_add(&Head, 'b');
|
||||
|
||||
node *ToBeRemoved = list_get(&Head, 'y');
|
||||
list_remove(&Head, ToBeRemoved);
|
||||
|
||||
list_add(&Head, 'a');
|
||||
node *InsertAfter = list_get(&Head, 'c');
|
||||
list_insert_after(InsertAfter, 'd');
|
||||
|
||||
for(node *Iter = Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
printf("%c ", Iter->Character);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
list_free(&Head);
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
program/match-regex
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
all:
|
||||
gcc program.c -o program
|
||||
#gcc solution.c -o solution
|
||||
|
||||
clean:
|
||||
rm -f program
|
||||
rm -f solution
|
|
@ -0,0 +1,18 @@
|
|||
As you noticed, we can use our basic data-structures, to form more complex ones, which fit more specific needs.
|
||||
This time we will take a look at the queue. This data-structures allows you to perform operations on your data
|
||||
with the **FIFO**(**F**irst **i**n **F**irst **o**ut) principle. This is critical, if you want to
|
||||
process the oldest data first, just like in a queue in the real world.
|
||||
|
||||
The queue data-structure is normally based on a linked list, such that it is also dynamically growing in size.
|
||||
But this does not have to be true, but usually, if you are using libraries, it is.
|
||||
|
||||
For the queue we want to perform three basic operations.
|
||||
-first we want to put something at the end of the queue. We call that ``enqueue``.
|
||||
-second we want to take something of the beginning of the queue. We call that ``dequeue``.
|
||||
-third we want just look at the end of the queue without taking it of. We call that ``peak``.
|
||||
|
||||
There are also other kinds of queues, which will not necessarily operate like a FIFO-queue.
|
||||
For example the priority-queue, which will sort the member with the highest priority
|
||||
to the beginning of the queue and uses a heap as it's underlying structure.
|
||||
|
||||
Your task is to implement the queue on the basis of a singly linked list.
|
|
@ -0,0 +1 @@
|
|||
{"title": "Queue"}
|
|
@ -0,0 +1,70 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
char Character;
|
||||
struct node *Next;
|
||||
}node;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
node *Head;
|
||||
node *Tail;
|
||||
}queue;
|
||||
|
||||
node *alloc_node(char c, node *Next)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Character = c;
|
||||
NewNode->Next = Next;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
// You could reuse the implementation of the singly linked list,
|
||||
// but in this case we can optimize our operations,
|
||||
// because we have pointers to the beginning end of the linked list.
|
||||
|
||||
// NOTE: this put elements on the end of the list
|
||||
// Don't forget to initialize the Head pointer
|
||||
void enqueue(queue *Queue, char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
// NOTE: This returns the char, because we don't want to manage
|
||||
// the memory in "user code"
|
||||
// Don't forget to free the node, otherwise this queue has a leak.
|
||||
char dequeue(queue *Queue)
|
||||
{
|
||||
// TODO: implement this
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE: in this example we only use this to check if the list is empty.
|
||||
// To do that we could also track the element count, but the peak operation
|
||||
// has also some other use cases.
|
||||
node *peak(queue *Queue)
|
||||
{
|
||||
// TODO: implement this
|
||||
return 0;
|
||||
}
|
||||
|
||||
// you can implement the queue-operations without ever searching through the list.
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
queue Queue = {};
|
||||
char *String = "HelloWorld!";
|
||||
|
||||
// The String should come out as it went in.
|
||||
while(*String)
|
||||
{
|
||||
enqueue(&Queue, *String++);
|
||||
}
|
||||
while(peak(&Queue))
|
||||
{
|
||||
printf("%c", dequeue(&Queue));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
^HelloWorld!$
|
|
@ -0,0 +1,69 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
char Character;
|
||||
struct node *Next;
|
||||
}node;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
node *Head;
|
||||
node *Tail;
|
||||
}queue;
|
||||
|
||||
node *alloc_node(char c, node *Next)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Character = c;
|
||||
NewNode->Next = Next;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
void enqueue(queue *Queue, char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *NewNode = alloc_node(c, 0);
|
||||
if(Queue->Tail)
|
||||
Queue->Tail->Next = NewNode;
|
||||
Queue->Tail = NewNode;
|
||||
if(!Queue->Head)
|
||||
{
|
||||
Queue->Head = NewNode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char dequeue(queue *Queue)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *First = Queue->Head;
|
||||
char Result = First->Character;
|
||||
Queue->Head = First->Next;
|
||||
free(First);
|
||||
return Result;
|
||||
}
|
||||
|
||||
node *peak(queue *Queue)
|
||||
{
|
||||
// TODO: implement this
|
||||
return Queue->Head;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
queue Queue = {};
|
||||
char *String = "HelloWorld!";
|
||||
|
||||
while(*String)
|
||||
{
|
||||
enqueue(&Queue, *String++);
|
||||
}
|
||||
while(peak(&Queue))
|
||||
{
|
||||
printf("%c", dequeue(&Queue));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
char Character;
|
||||
struct node *Next;
|
||||
}node;
|
||||
|
||||
node *alloc_node(char c, node *Next)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Character = c;
|
||||
NewNode->Next = Next;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
// NOTE: adds an element to the beginning
|
||||
void list_add(node **Head, char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *NewNode = alloc_node(c, *Head);
|
||||
*Head = NewNode;
|
||||
}
|
||||
|
||||
// NOTE: inserts after a specific node
|
||||
void list_insert_after(node *Node, char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *NewNode = alloc_node(c, Node->Next);
|
||||
Node->Next = NewNode;
|
||||
}
|
||||
|
||||
// NOTE: get's a node with the specified character
|
||||
node *list_get(node **Head, char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *Result;
|
||||
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
if(Iter->Character == c)
|
||||
{
|
||||
Result = Iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
// NOTE: removes a specific node from the list
|
||||
void list_remove(node **Head, node *Node)
|
||||
{
|
||||
// TODO: implement this
|
||||
// Find prev node
|
||||
node *Prev = 0;
|
||||
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
if(Iter == Node)
|
||||
break;
|
||||
Prev = Iter;
|
||||
}
|
||||
if(Prev)
|
||||
Prev->Next = Node->Next;
|
||||
free(Node);
|
||||
}
|
||||
|
||||
// NOTE: frees the whole list
|
||||
void list_free(node **Head)
|
||||
{
|
||||
for(node *Iter = *Head; Iter; )
|
||||
{
|
||||
node *ToFree = Iter;
|
||||
Iter = Iter->Next;
|
||||
|
||||
free(ToFree);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// NOTE: beginning of the list
|
||||
// right now the list is empty
|
||||
node *Head = 0;
|
||||
|
||||
list_add(&Head, 'c');
|
||||
list_add(&Head, 'y');
|
||||
list_add(&Head, 'b');
|
||||
|
||||
node *ToBeRemoved = list_get(&Head, 'y');
|
||||
list_remove(&Head, ToBeRemoved);
|
||||
|
||||
list_add(&Head, 'a');
|
||||
node *InsertAfter = list_get(&Head, 'c');
|
||||
list_insert_after(InsertAfter, 'd');
|
||||
|
||||
for(node *Iter = Head; Iter; Iter = Iter->Next)
|
||||
{
|
||||
printf("%c ", Iter->Character);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
list_free(&Head);
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
program/match-regex
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
all:
|
||||
gcc program.c -o program
|
||||
#gcc solution.c -o solution
|
||||
|
||||
clean:
|
||||
rm -f program
|
||||
rm -f solution
|
|
@ -0,0 +1,17 @@
|
|||
A data-structure often associated with the queue is the stack.
|
||||
That's because they are basically the opposite of one another.
|
||||
While the queue works on the FIFO-principle the stack works
|
||||
on the **LIFO**-principle (**L**ast **i**n **f**irst **o**ut).
|
||||
|
||||
Similar two the queue we have three basic operations, but they are called
|
||||
different to distinguish between the stack and the queue.
|
||||
- We put something at the top of the stack. That is called push.
|
||||
- We take something from the top of the stack. That is called pop.
|
||||
- As with the queue we sometimes one to just take a look at the top of the stack.
|
||||
So a peak operation is nice, but not necessary
|
||||
|
||||
Stacks are useful data-structures for parsing or when you want follow some data to the end
|
||||
and then back trace to a previous state, like in depth-fist-search.
|
||||
These are just some use cases for stacks, but they have many more.
|
||||
|
||||
Your task is to implement the push, pop and peak functions for the stack.
|
|
@ -0,0 +1 @@
|
|||
{"title": "Stack"}
|
|
@ -0,0 +1,57 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
char Character;
|
||||
struct node *Next;
|
||||
}node;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
node *Top;
|
||||
}stack;
|
||||
|
||||
node *alloc_node(char c, node *Next)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Character = c;
|
||||
NewNode->Next = Next;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
void push(stack *Stack,char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
// NOTE: as last time free the node here
|
||||
char pop(stack *Stack)
|
||||
{
|
||||
// TODO: implement this
|
||||
return 0;
|
||||
}
|
||||
|
||||
node *peak(stack *Stack)
|
||||
{
|
||||
// TODO: implement this
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
stack Stack = {};
|
||||
char *String = "!dlroW ollaH";
|
||||
|
||||
// When everything works, the program should print Hello World!.
|
||||
while(*String)
|
||||
{
|
||||
push(&Stack, *String++);
|
||||
}
|
||||
while(peak(&Stack))
|
||||
{
|
||||
printf("%c", pop(&Stack));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
^Hello World!$
|
|
@ -0,0 +1,62 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
char Character;
|
||||
struct node *Next;
|
||||
}node;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
node *Top;
|
||||
}stack;
|
||||
|
||||
node *alloc_node(char c, node *Next)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Character = c;
|
||||
NewNode->Next = Next;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
void push(stack *Stack,char c)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *NewNode = alloc_node(c, Stack->Top);
|
||||
Stack->Top = NewNode;
|
||||
}
|
||||
|
||||
char pop(stack *Stack)
|
||||
{
|
||||
// TODO: implement this
|
||||
char Result = Stack->Top->Character;
|
||||
node *Top = Stack->Top->Next;
|
||||
free(Stack->Top);
|
||||
Stack->Top = Top;
|
||||
return Result;
|
||||
}
|
||||
|
||||
node *peak(stack *Stack)
|
||||
{
|
||||
// TODO: implement this
|
||||
return Stack->Top;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
stack Stack = {};
|
||||
char *String = "!dlroW olleH";
|
||||
|
||||
// When everything works, the program should print Hello World!.
|
||||
while(*String)
|
||||
{
|
||||
push(&Stack, *String++);
|
||||
}
|
||||
while(peak(&Stack))
|
||||
{
|
||||
printf("%c", pop(&Stack));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
program/match-regex
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
all:
|
||||
gcc program.c -o program
|
||||
#gcc solution.c -o solution
|
||||
|
||||
clean:
|
||||
rm -f program
|
||||
rm -f solution
|
|
@ -0,0 +1,26 @@
|
|||
Now that we covered the basic linear data-structures, let's move on to once, which branch out.
|
||||
This task is about trees. A tree consists of connected nodes and each node has a certain amount of children nodes.
|
||||
The nodes, which don't have any children are called leaves and the start node is called a root.
|
||||
A tree also doesn't have any circles, meaning that no path from one node should lead back to it's self.
|
||||
Trees are often associated with sorting, hierarchical data or decisions.
|
||||
|
||||
This description is for trees in general. The one we will discuss is called a binary-search-tree.
|
||||
The only difference to the normal tree is, that it only has two children and all the left children have a lower
|
||||
search value than the node and all the right nodes have a higher one.
|
||||
These kind of trees are very useful for efficient searching.
|
||||
|
||||
For this tree we want to be able to perform four operations.
|
||||
- We want to be able to automatically insert an element into the right place.
|
||||
- We want to delete an element without destroying the definition of the binary-search-tree
|
||||
- We want to find an element in the tree
|
||||
- And we want to print out all the elements in a sorted way
|
||||
|
||||
Recursion is a good strategy to write functions for trees.
|
||||
|
||||
All but the delete functions are straight forward. The delete function needs some special casing.
|
||||
If there are both children present, you have to find the lowest value on the right and replace the
|
||||
current one with it and then recursivly delete that one.
|
||||
If there are less than two children, the deletion is trivial.
|
||||
|
||||
Your task is to implement all these functions.
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"title": "Tree"}
|
|
@ -0,0 +1,89 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
int Value;
|
||||
struct node *Left;
|
||||
struct node *Right;
|
||||
}node;
|
||||
|
||||
node *alloc_node(int Value, node *Left, node *Right)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Value = Value;
|
||||
NewNode->Left = Left;
|
||||
NewNode->Right = Right;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
// inserts into the right place in the tree
|
||||
void tree_insert(node *Root, int Value)
|
||||
{
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
// finds the node with the given value.
|
||||
// if it doesn't find one return null
|
||||
node *tree_find(node *Root, int Value)
|
||||
{
|
||||
// TODO: implement this
|
||||
return 0;
|
||||
}
|
||||
|
||||
// deletes the node with the value while remaining sorted
|
||||
// NOTE: feel free to change the function signiture
|
||||
void tree_delete(node *Root, int Value)
|
||||
{
|
||||
// TODO: implement this
|
||||
|
||||
// NOTE: when finding the node to delete,
|
||||
// keep track of the parent node to rewire
|
||||
// the pointer when deleting
|
||||
|
||||
// NOTE: don't forget to free the memory
|
||||
}
|
||||
|
||||
// prints every member in a sorted way
|
||||
int tree_print_sorted(node *Root)
|
||||
{
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
node Root = {};
|
||||
int Numbers[10] = {9, 128, 3, 1, 42, 9001, 7, 500, 6, 10};
|
||||
|
||||
// NOTE: filling the tree with numbers
|
||||
for(int i = 0 ; i < 10; i++)
|
||||
{
|
||||
tree_insert(&Root, Numbers[i]);
|
||||
}
|
||||
|
||||
node *Nine = tree_find(&Root, 9);
|
||||
int FoundNine = 0;
|
||||
if(Nine)
|
||||
{
|
||||
FoundNine = 1;
|
||||
node *Left = Nine->Left;
|
||||
node *Right = Nine->Right;
|
||||
printf("Found %d with direct child %d and %d\n", Nine->Value, (Left)? Left->Value: 0, (Right)? Right->Value : 0);
|
||||
}
|
||||
tree_delete(&Root, 9);
|
||||
Nine = tree_find(&Root, 9);
|
||||
if(Nine)
|
||||
{
|
||||
printf("Failed to delete %d\n", Nine->Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(FoundNine)
|
||||
printf("Succesfully deleted 9\n");
|
||||
}
|
||||
|
||||
tree_print_sorted(&Root);
|
||||
printf("\n");
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
^Found 9 with direct child 3 and 128\nSuccesfully deleted 9\n1 3 6 7 9 10 42 128 500 9001 $
|
|
@ -0,0 +1,176 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
int Value;
|
||||
struct node *Left;
|
||||
struct node *Right;
|
||||
}node;
|
||||
|
||||
node *alloc_node(int Value, node *Left, node *Right)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Value = Value;
|
||||
NewNode->Left = Left;
|
||||
NewNode->Right = Right;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
// inserts into the right place in the tree
|
||||
void tree_insert(node **Root, int Value)
|
||||
{
|
||||
// TODO: implement this
|
||||
if(*Root)
|
||||
{
|
||||
node *Node = *Root;
|
||||
if(Value <= Node->Value)
|
||||
{
|
||||
tree_insert(&Node->Left, Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
tree_insert(&Node->Right, Value);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
*Root = alloc_node(Value, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// finds the node with the given value.
|
||||
// if it doesn't find one return null
|
||||
node *tree_find(node *Root, int Value)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *Node = Root;
|
||||
if(Node)
|
||||
{
|
||||
if(Value < Node->Value)
|
||||
{
|
||||
tree_find(Node->Left, Value);
|
||||
}
|
||||
else if(Value > Node->Value)
|
||||
{
|
||||
tree_find(Node->Right, Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Node;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// deletes the node with the value while remaining sorted
|
||||
void tree_delete(node **Root, node *Parent, int Value)
|
||||
{
|
||||
node *Node = *Root;
|
||||
if(Node)
|
||||
{
|
||||
if(Value < Node->Value)
|
||||
{
|
||||
tree_delete(&Node->Left, Node, Value);
|
||||
}
|
||||
else if(Value > Node->Value)
|
||||
{
|
||||
tree_delete(&Node->Right, Node, Value);
|
||||
}
|
||||
else // Found node
|
||||
{
|
||||
node *NodeToDelete = Node;
|
||||
if(NodeToDelete->Left && NodeToDelete->Right)
|
||||
{
|
||||
node *RightSubTree = NodeToDelete->Right;
|
||||
if(RightSubTree)
|
||||
{
|
||||
node *LowestNode = RightSubTree->Left;
|
||||
node *Prev;
|
||||
while(LowestNode->Left)
|
||||
{
|
||||
Prev = LowestNode;
|
||||
LowestNode = LowestNode->Left;
|
||||
}
|
||||
NodeToDelete->Value = LowestNode->Value;
|
||||
Prev->Left = 0;
|
||||
tree_delete(&LowestNode, Prev, LowestNode->Value);
|
||||
}
|
||||
}
|
||||
else if(NodeToDelete->Left)
|
||||
{
|
||||
if(Parent)
|
||||
Parent->Left = NodeToDelete->Left;
|
||||
free(NodeToDelete);
|
||||
}
|
||||
else if(NodeToDelete->Right)
|
||||
{
|
||||
if(Parent)
|
||||
Parent->Left = NodeToDelete->Right;
|
||||
free(NodeToDelete);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Parent)
|
||||
Parent->Left = 0;
|
||||
free(NodeToDelete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
// prints every member in a sorted way
|
||||
void tree_print_sorted(node *Root)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *Node = Root;
|
||||
if(Node)
|
||||
{
|
||||
node *Left = Node->Left;
|
||||
tree_print_sorted(Left);
|
||||
printf("%d ", Node->Value);
|
||||
node *Right = Node->Right;
|
||||
tree_print_sorted(Right);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
node *Root;
|
||||
int Numbers[10] = {9, 128, 3, 1, 42, 9001, 7, 500, 6, 10};
|
||||
|
||||
// NOTE: filling the tree with numbers
|
||||
for(int i = 0 ; i < 10; i++)
|
||||
{
|
||||
tree_insert(&Root, Numbers[i]);
|
||||
}
|
||||
|
||||
node *Nine = tree_find(Root, 9);
|
||||
int FoundNine = 0;
|
||||
if(Nine)
|
||||
{
|
||||
FoundNine = 1;
|
||||
node *Left = Nine->Left;
|
||||
node *Right = Nine->Right;
|
||||
printf("Found %d with direct child %d and %d\n", Nine->Value, (Left)? Left->Value: 0, (Right)? Right->Value : 0);
|
||||
}
|
||||
tree_delete(&Root, 0, 9);
|
||||
Nine = tree_find(Root, 9);
|
||||
if(Nine)
|
||||
{
|
||||
printf("Failed to delete %d\n", Nine->Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(FoundNine)
|
||||
printf("Succesfully deleted 9\n");
|
||||
}
|
||||
|
||||
tree_print_sorted(Root);
|
||||
printf("\n");
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
program/match-regex
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
all:
|
||||
gcc program.c -o program
|
||||
#gcc solution.c -o solution
|
||||
|
||||
clean:
|
||||
rm -f program
|
||||
rm -f solution
|
Binary file not shown.
|
@ -0,0 +1,25 @@
|
|||
The last data-structure we are going to cover is the graph.
|
||||
It is similar to a tree. In fact a tree is a special case
|
||||
of a graph, because a graph is also allowed to have circles,
|
||||
nodes that have a path to them self.
|
||||
|
||||
Graphs are useful data-structure to encode relations.
|
||||
For example a road-network or friendships.
|
||||
|
||||
To encode the most basic graphs you need two things.
|
||||
A list of nodes, sometimes called vertices, and a list of edges.
|
||||
The nodes represent objects and the edges represent their relation.
|
||||
|
||||
For example you could have a graph of a road-network, where every
|
||||
intersection is a node and every street is an edge.
|
||||
|
||||
With the edges and nodes you could encode more information, to use in your
|
||||
algorithm. So the intersections could have an GPS-position and the street
|
||||
some kind of cost, which indicates the amount of time you need to get to the
|
||||
other side.
|
||||
|
||||
In this task we provided you with a very simplistic graph,
|
||||
which encodes the relationship between two people.
|
||||
Your task is to find out, who likes one, which likes them back.
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"title": "Graphs"}
|
|
@ -0,0 +1,40 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// NOTE: These are the nodes
|
||||
char *Names[5] =
|
||||
{
|
||||
"Mike",
|
||||
"Bob",
|
||||
"Anna",
|
||||
"Emma",
|
||||
"Nick"
|
||||
};
|
||||
|
||||
// NOTE: These are the edges
|
||||
// for now everyone can only like two other people
|
||||
int Likes[5][2] =
|
||||
{
|
||||
{1,3}, // Mike likes Bob and Emma
|
||||
{4,3},
|
||||
{0,4},
|
||||
{2,4},
|
||||
{1,2}
|
||||
};
|
||||
|
||||
// NOTE: if you wanted to be able to have as much edges as possible
|
||||
// you could use a 2D-Array with the size of the number
|
||||
// of Nodes. These Arrays are called adjacency-matrices.
|
||||
// But these are inherently inefficient, when you have a graph with
|
||||
// few connections.
|
||||
|
||||
// there are also other ways like storing the edges in the nodes
|
||||
// in a list of pointers etc
|
||||
|
||||
// TODO: print out everyone, who likes one, which likes them back.
|
||||
// if you want to you can try to prevent duplicates like A likes B
|
||||
// and B likes A, but this is not required.
|
||||
printf("%s and %s both like each other.\n", "Nobody", "No one");
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
^Bob and Nick both like eachother.\nAnna and Nick both like eachother.(\nNick and Bob both like eachother.\nNick and Anna both like eachother.){0,1}$
|
|
@ -0,0 +1,42 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// NOTE: These are the nodes
|
||||
char *Names[5] =
|
||||
{
|
||||
"Mike",
|
||||
"Bob",
|
||||
"Anna",
|
||||
"Emma",
|
||||
"Nick"
|
||||
};
|
||||
|
||||
// NOTE: These are the edges
|
||||
// for now everyone can only like two other people
|
||||
int Likes[5][2] =
|
||||
{
|
||||
{1,3}, // Mike likes Bob and Emma
|
||||
{4,3},
|
||||
{0,4},
|
||||
{2,4},
|
||||
{1,2}
|
||||
};
|
||||
// TODO: print out everyone, who likes one, which likes them back
|
||||
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
for(int j = 0; j < 2; j++)
|
||||
{
|
||||
int iLikes = Likes[i][j];
|
||||
for(int k = 0; k < 2; k++)
|
||||
{
|
||||
if(Likes[iLikes][k] == i)
|
||||
{
|
||||
printf("%s and %s both like each other.\n", Names[i], Names[iLikes]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
program/match-regex
|
|
@ -0,0 +1 @@
|
|||
{"title": "Basic Datastructures", "description": "Implement your own Datastrucutes"}
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
all:
|
||||
gcc -std=c99 -pthread program.c -o program
|
||||
gcc -std=c99 -pthread solution.c -o solution
|
||||
clean:
|
||||
rm -f program
|
||||
rm -f solution
|
|
@ -0,0 +1,14 @@
|
|||
The first usecase we are going to take a look at, is about the queue.
|
||||
There are trivial ones like a playlist, where you can add more music to.
|
||||
Another common usecase is the breadth first search for a tree traversal.
|
||||
But we are going to look at a more general concept.
|
||||
|
||||
Imagine you have some code, which produces some data and another part, which
|
||||
processes the data. This doesn't necessarily have to be in sync.
|
||||
Specially when you talk about different threads or processes.
|
||||
You then need a way to buffer the incomming data, otherwise you would
|
||||
overwrite the oldes one, which, depending on the circumstances, can lead to errors.
|
||||
A queue helps to get around this problem.
|
||||
|
||||
For this exercise we prepared a thread, which just produces some random numbers and pushes them on a queue.
|
||||
Your task is to add the oldest two together and print them out.
|
|
@ -0,0 +1,3 @@
|
|||
rand=$(( $RANDOM % 100))
|
||||
./program $rand
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"title": "Producer & Consumer"}
|
|
@ -0,0 +1,92 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
int Value;
|
||||
struct node *Next;
|
||||
}node;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
node *Head;
|
||||
node *Tail;
|
||||
pthread_mutex_t Mutex;
|
||||
}queue;
|
||||
|
||||
node *alloc_node(int Value, node *Next)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Value = Value;
|
||||
NewNode->Next = Next;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
// NOTE: we are not going to talk about threads.
|
||||
// the lock and unlock functions prevent the threads to execute the locked part at the same time,
|
||||
// which could lead to errors
|
||||
void enqueue(queue *Queue, int Value)
|
||||
{
|
||||
node *NewNode = alloc_node(Value, 0);
|
||||
pthread_mutex_lock(&Queue->Mutex);
|
||||
if(Queue->Tail)
|
||||
Queue->Tail->Next = NewNode;
|
||||
Queue->Tail = NewNode;
|
||||
if(!Queue->Head)
|
||||
{
|
||||
Queue->Head = NewNode;
|
||||
}
|
||||
pthread_mutex_unlock(&Queue->Mutex);
|
||||
}
|
||||
|
||||
|
||||
int dequeue(queue *Queue)
|
||||
{
|
||||
pthread_mutex_lock(&Queue->Mutex);
|
||||
node *First = Queue->Head;
|
||||
int Result = First->Value;
|
||||
Queue->Head = First->Next;
|
||||
free(First);
|
||||
pthread_mutex_unlock(&Queue->Mutex);
|
||||
return Result;
|
||||
}
|
||||
|
||||
node *peak(queue *Queue)
|
||||
{
|
||||
return Queue->Head;
|
||||
}
|
||||
|
||||
queue CommunicationQueue;
|
||||
|
||||
void *Producer_Proc(void *ptr)
|
||||
{
|
||||
int Seed = atoi((char *)ptr);
|
||||
srand(Seed);
|
||||
for (int i = 0; i < 100; i ++)
|
||||
{
|
||||
enqueue(&CommunicationQueue, (rand()%10)+1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CommunicationQueue.Head = 0;
|
||||
CommunicationQueue.Tail = 0;
|
||||
pthread_mutex_init(&CommunicationQueue.Mutex,0);
|
||||
|
||||
pthread_t Producer;
|
||||
|
||||
pthread_create(&Producer, 0, Producer_Proc, (void *)argv[1]);
|
||||
|
||||
// TODO: print 50 times the sum of the last two numbers from the queue
|
||||
// don't forget to check if there is even anything on the queue
|
||||
|
||||
pthread_join(Producer, 0);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
int Value;
|
||||
struct node *Next;
|
||||
}node;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
node *Head;
|
||||
node *Tail;
|
||||
pthread_mutex_t Mutex;
|
||||
}queue;
|
||||
|
||||
node *alloc_node(int Value, node *Next)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Value = Value;
|
||||
NewNode->Next = Next;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
// NOTE: we are not going to talk about threads.
|
||||
// the lock and unlock functions prevent the threads to execute the locked part at the same time,
|
||||
// which could lead to errors
|
||||
void enqueue(queue *Queue, int Value)
|
||||
{
|
||||
node *NewNode = alloc_node(Value, 0);
|
||||
pthread_mutex_lock(&Queue->Mutex);
|
||||
if(Queue->Tail)
|
||||
Queue->Tail->Next = NewNode;
|
||||
Queue->Tail = NewNode;
|
||||
if(!Queue->Head)
|
||||
{
|
||||
Queue->Head = NewNode;
|
||||
}
|
||||
pthread_mutex_unlock(&Queue->Mutex);
|
||||
}
|
||||
|
||||
|
||||
int dequeue(queue *Queue)
|
||||
{
|
||||
pthread_mutex_lock(&Queue->Mutex);
|
||||
node *First = Queue->Head;
|
||||
int Result = First->Value;
|
||||
Queue->Head = First->Next;
|
||||
free(First);
|
||||
pthread_mutex_unlock(&Queue->Mutex);
|
||||
return Result;
|
||||
}
|
||||
|
||||
node *peak(queue *Queue)
|
||||
{
|
||||
return Queue->Head;
|
||||
}
|
||||
|
||||
queue CommunicationQueue;
|
||||
|
||||
void *Producer_Proc(void *ptr)
|
||||
{
|
||||
int Seed = atoi((char *)ptr);
|
||||
srand(Seed);
|
||||
for (int i = 0; i < 100; i ++)
|
||||
{
|
||||
enqueue(&CommunicationQueue, (rand()%10)+1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CommunicationQueue.Head = 0;
|
||||
CommunicationQueue.Tail = 0;
|
||||
pthread_mutex_init(&CommunicationQueue.Mutex,0);
|
||||
|
||||
pthread_t Producer;
|
||||
|
||||
if(argc < 2)
|
||||
argv[1] = "1";
|
||||
pthread_create(&Producer, 0, Producer_Proc, (void *)argv[1]);
|
||||
|
||||
for(int i = 0; i < 50; i++)
|
||||
{
|
||||
while(!peak(&CommunicationQueue)){}
|
||||
int x = dequeue(&CommunicationQueue);
|
||||
|
||||
while(!peak(&CommunicationQueue)){}
|
||||
int y = dequeue(&CommunicationQueue);
|
||||
|
||||
printf("%d\n", x+y);
|
||||
}
|
||||
|
||||
pthread_join(Producer, 0);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
rand=$(( $RANDOM % 100))
|
||||
|
||||
program_out=$(./program $rand)
|
||||
solution_out=$(./solution $rand)
|
||||
|
||||
if [ "$program_out" == "$solution_out" ]
|
||||
then
|
||||
echo "PASS " $program_out " equals " $solution_out
|
||||
else
|
||||
echo "FAIL " $program_out " DOES NOT EQUAL " $solution_out
|
||||
correct=false
|
||||
fi
|
|
@ -0,0 +1 @@
|
|||
program/scriptgrade
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
all:
|
||||
gcc program.c -o program
|
||||
gcc solution.c -o solution
|
||||
|
||||
clean:
|
||||
rm -f program
|
||||
rm -f solution
|
|
@ -0,0 +1,10 @@
|
|||
For this task you are going to implement a very simple calculater using a stack.
|
||||
To simplify this we are ignoring precedence and use post-fix operators (sometimes referred to as reverse polish notation). Post-fix operators are just like your normal infix ones. You just write them
|
||||
at the end of the two operands. For example 3 + 4 becomes 3 4 +. This makes parsing way easier.
|
||||
And because we are ignoring precedence, the term 3 4 5 + * is equal to 35.
|
||||
|
||||
The concept of a stack based calculator is pretty simple. Numbers simply get pushed on the stack and
|
||||
operators pop the number of operands of the stack, calculate the result and push it back on.
|
||||
At the end you can just pop of the result.
|
||||
|
||||
Because the parsing and the calculator would be a bit much for one task, we already implemented the parsing. Your task is to fill in the rest of the calculator.
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
|
||||
arguments=$(./term_generator.sh)
|
||||
|
||||
program_out=$(./program "$arguments")
|
||||
|
||||
echo "term: $arguments"
|
||||
echo "$program_out"
|
||||
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"title": "Stack - Calculator"}
|
|
@ -0,0 +1,115 @@
|
|||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include "stack.h"
|
||||
|
||||
//
|
||||
// NOTE: helper functions for parsing
|
||||
//
|
||||
bool isWhitespace(char c)
|
||||
{
|
||||
return(c == ' ' || c == '\t' || c == '\n' || c == '\r' );
|
||||
}
|
||||
|
||||
char *eatAllWhitespaces(char *At)
|
||||
{
|
||||
while(*At && isWhitespace(*At))
|
||||
At++;
|
||||
return At;
|
||||
}
|
||||
|
||||
bool isDigit(char Digit)
|
||||
{
|
||||
return (Digit >= '0' && Digit <= '9');
|
||||
}
|
||||
|
||||
int isNumber(char *Number)
|
||||
{
|
||||
int Result = 0;
|
||||
while(*Number && *Number != ' ')
|
||||
{
|
||||
if(!isDigit(*Number))
|
||||
{
|
||||
Result = -1;
|
||||
return Result;
|
||||
}
|
||||
Number++;
|
||||
Result++;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
//
|
||||
// ----------------------------------
|
||||
//
|
||||
|
||||
|
||||
/*
|
||||
* Stack-Interface
|
||||
*
|
||||
* void push(stack *Stack, float Value);
|
||||
*
|
||||
* float pop(stack *Stack);
|
||||
*
|
||||
* node *peak(stack *Stack);
|
||||
*
|
||||
*/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
stack Stack = {};
|
||||
|
||||
//
|
||||
// Parsing
|
||||
//
|
||||
|
||||
// NOTE: the string to parse
|
||||
char *Input = argv[1];
|
||||
char *At = Input;
|
||||
while(*At)
|
||||
{
|
||||
At = eatAllWhitespaces(At);
|
||||
switch(*At)
|
||||
{
|
||||
case '+':
|
||||
{
|
||||
// TODO: implement addition
|
||||
}break;
|
||||
case '-':
|
||||
{
|
||||
// TODO: implement substraction
|
||||
// NOTE: substraction is orderdependent
|
||||
}break;
|
||||
case '*':
|
||||
{
|
||||
// TODO: implement multiplication
|
||||
}break;
|
||||
case '/':
|
||||
{
|
||||
// TODO: implement division
|
||||
// NOTE: division is orderdependent
|
||||
}break;
|
||||
default:
|
||||
{
|
||||
int Length = isNumber(At);
|
||||
if(Length != -1)
|
||||
{
|
||||
// TODO: implement the handling of new numbers
|
||||
At += Length -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("ERROR: Only Numbers and Operators + - * and / are allowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
At++;
|
||||
}
|
||||
|
||||
// NOTE: print out the remaining stack
|
||||
// if everything went right, this should only be the result
|
||||
while(peak(&Stack))
|
||||
{
|
||||
printf("%f ", pop(&Stack));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include "stack.h"
|
||||
|
||||
bool isWhitespace(char c)
|
||||
{
|
||||
return(c == ' ' || c == '\t' || c == '\n' || c == '\r' );
|
||||
}
|
||||
|
||||
char *eatAllWhitespaces(char *At)
|
||||
{
|
||||
while(*At && isWhitespace(*At))
|
||||
At++;
|
||||
return At;
|
||||
}
|
||||
|
||||
bool isDigit(char Digit)
|
||||
{
|
||||
return (Digit >= '0' && Digit <= '9');
|
||||
}
|
||||
|
||||
int isNumber(char *Number)
|
||||
{
|
||||
int Result = 0;
|
||||
while(*Number && *Number != ' ')
|
||||
{
|
||||
if(!isDigit(*Number))
|
||||
{
|
||||
Result = -1;
|
||||
return Result;
|
||||
}
|
||||
Number++;
|
||||
Result++;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
stack Stack = {};
|
||||
float Result = 0;
|
||||
//
|
||||
// Parsing
|
||||
//
|
||||
//char *Input = "6 3 + 4 - 20 *";
|
||||
char *Input = argv[1];
|
||||
char *At = Input;
|
||||
while(*At)
|
||||
{
|
||||
At = eatAllWhitespaces(At);
|
||||
switch(*At)
|
||||
{
|
||||
case '+':
|
||||
{
|
||||
float Addent1 = pop(&Stack);
|
||||
float Addent2 = pop(&Stack);
|
||||
push(&Stack, Addent1 + Addent2);
|
||||
}break;
|
||||
case '-':
|
||||
{
|
||||
float Subtrahend = pop(&Stack);
|
||||
float Minuend = pop(&Stack);
|
||||
|
||||
push(&Stack, Minuend - Subtrahend);
|
||||
}break;
|
||||
case '*':
|
||||
{
|
||||
float Multiplicand = pop(&Stack);
|
||||
float Multiplier = pop(&Stack);
|
||||
push(&Stack, Multiplier * Multiplicand);
|
||||
}break;
|
||||
case '/':
|
||||
{
|
||||
float divisor = pop(&Stack);
|
||||
float dividend = pop(&Stack);
|
||||
push(&Stack, dividend / divisor);
|
||||
}break;
|
||||
default:
|
||||
{
|
||||
int Length = isNumber(At);
|
||||
if(Length != -1)
|
||||
{
|
||||
push(&Stack, atof(At));
|
||||
At += Length -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("ERROR: Only Numbers and Operators + - * and / are allowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
At++;
|
||||
}
|
||||
while(peak(&Stack))
|
||||
{
|
||||
printf("%f ", pop(&Stack));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
float Value;
|
||||
struct node *Next;
|
||||
}node;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
node *Top;
|
||||
}stack;
|
||||
|
||||
node *alloc_node(float Value, node *Next)
|
||||
{
|
||||
node *NewNode = malloc(sizeof(node));
|
||||
NewNode->Value = Value;
|
||||
NewNode->Next = Next;
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
void push(stack *Stack, float Value)
|
||||
{
|
||||
// TODO: implement this
|
||||
node *NewNode = alloc_node(Value, Stack->Top);
|
||||
Stack->Top = NewNode;
|
||||
}
|
||||
|
||||
float pop(stack *Stack)
|
||||
{
|
||||
// TODO: implement this
|
||||
float Result = Stack->Top->Value;
|
||||
node *Top = Stack->Top->Next;
|
||||
free(Stack->Top);
|
||||
Stack->Top = Top;
|
||||
return Result;
|
||||
}
|
||||
|
||||
node *peak(stack *Stack)
|
||||
{
|
||||
// TODO: implement this
|
||||
return Stack->Top;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
#!/bin/bash
|
||||
|
||||
generating=true
|
||||
number_count=0
|
||||
arguments=""
|
||||
last_number=0
|
||||
|
||||
#number 1
|
||||
number_count=$((number_count+1))
|
||||
number=$(((RANDOM % 100)+1))
|
||||
arguments="$arguments $number"
|
||||
last_number=$number
|
||||
|
||||
#number 2
|
||||
number_count=$((number_count+1))
|
||||
number=$(((RANDOM % 100)+1))
|
||||
arguments="$arguments $number"
|
||||
last_number=$number
|
||||
|
||||
while [ "$generating" = "true" ]
|
||||
do
|
||||
if [ $number_count -ge 2 ]
|
||||
then
|
||||
choice=$((RANDOM % 2))
|
||||
if [ $choice = 0 ]
|
||||
then
|
||||
#operator
|
||||
operator=$((RANDOM % 4))
|
||||
if [ $operator = 0 ]
|
||||
then
|
||||
number_count=$((number_count-1))
|
||||
arguments="$arguments +"
|
||||
elif [ $operator = 1 ]
|
||||
then
|
||||
number_count=$((number_count-1))
|
||||
arguments="$arguments -"
|
||||
elif [ $operator = 2 ]
|
||||
then
|
||||
number_count=$((number_count-1))
|
||||
arguments="$arguments *"
|
||||
elif [ $operator = 3 ]
|
||||
then
|
||||
if [ $last_number -ne 0 ]
|
||||
then
|
||||
number_count=$((number_count-1))
|
||||
arguments="$arguments /"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
#number
|
||||
number_count=$((number_count+1))
|
||||
number=$(((RANDOM % 100)+1))
|
||||
arguments="$arguments $number"
|
||||
last_number=$number
|
||||
fi
|
||||
else
|
||||
choice=$((RANDOM % 2))
|
||||
if [ $choice = 0 ]
|
||||
then
|
||||
generating=false
|
||||
else
|
||||
#number
|
||||
number_count=$((number_count+1))
|
||||
number=$(((RANDOM % 100)+1))
|
||||
arguments="$arguments $number"
|
||||
last_number=$number
|
||||
fi
|
||||
fi
|
||||
done
|
||||
echo "$arguments"
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/bin/bash
|
||||
|
||||
arguments=$(./term_generator.sh)
|
||||
|
||||
program_out=$(./program "$arguments")
|
||||
solution_out=$(./solution "$arguments")
|
||||
|
||||
echo "term: $arguments"
|
||||
|
||||
if [ "$program_out" == "$solution_out" ]
|
||||
then
|
||||
echo "PASS " $program_out " equals " $solution_out
|
||||
else
|
||||
echo "FAIL " $program_out " DOES NOT EQUAL " $solution_out
|
||||
correct=false
|
||||
fi
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
program/scriptgrade
|
|
@ -0,0 +1 @@
|
|||
{"title": "Example Algorithms", "description": "Implement algorithms, which use the data-structures from the previous section "}
|
|
@ -0,0 +1,3 @@
|
|||
*.pyc
|
||||
__pycache__
|
||||
program
|
|
@ -0,0 +1 @@
|
|||
{"title": "C Basics", "description": "A introduction to the C programming language. Whenever performance matters and you are working close with the operating system knowledge in C is inevitable."}
|
|
@ -0,0 +1,11 @@
|
|||
<p>
|
||||
Welcome to the introductory course for learning the <b>C programming language</b>.
|
||||
This course is for people, who do have some experience in programming.
|
||||
You will be provided with task to train interactively and will get
|
||||
information on how C works and how you program in it.
|
||||
</p>
|
||||
<p>
|
||||
The beginning of the course covers basics, like what is a compiler, what are expressions
|
||||
and all the fundamental syntax. From there, we will tackle the daunting topic of
|
||||
pointers and will move on to more complex concepts.
|
||||
</p>
|
|
@ -0,0 +1 @@
|
|||
{"title": "Introduction"}
|
|
@ -0,0 +1 @@
|
|||
text/html
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
all:
|
||||
gcc --std=c99 program.c -o program
|
||||
|
||||
clean:
|
||||
rm -f program
|
|
@ -0,0 +1,24 @@
|
|||
<p>
|
||||
Welcome to the first lecture. This is what you will see most of the time.
|
||||
A text in the beginning, explaining all the things you need to know for the lecture and what you have to do,
|
||||
then a text-editor, where you supposed to edit the code to complete the given task and three buttons below the editor. The execute Button will compile
|
||||
and execute your code, the submit button will do the same and also check, if your solution is correct. If your solution was correct, you can press the
|
||||
continue button to go to the next lecture.
|
||||
</p>
|
||||
<p>
|
||||
Now back to the lecture. This is probably the most simple program. That is why you find it in almost any basic programming book or tutorial.
|
||||
Ignore for now the <code>#include <stdio.h></code>. It is not important for now. Below that you see the <b>main-function</b>.
|
||||
It marks the <b>entriepoint for your program</b>.
|
||||
From there on the computer will exactly <b>execute</b> what you write <b>in order</b> (with exceptions we won't go into). That is what is commonly know as <b>imperative programming</b>.
|
||||
</p>
|
||||
<p>
|
||||
The first thing in our program is a comment and is initiated by double slashes "//". Everything after that in the same line will be ignored by the compiler and therefore not be executed.
|
||||
For longer comments you can also surround it by "/* */" so you don't have to type double slashes in every line.
|
||||
</p>
|
||||
<p>
|
||||
So now to the exciting part. The line:</br>
|
||||
<code> printf("Hello User!"); </code></br>
|
||||
will write text to the console. In this case <it>Hello User!</it>. Every statement in C ends with a semicolon ";".
|
||||
Your task will be to experiment with that line. Change the program to print out Hello World!.
|
||||
</p>
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"title": "Hello World!"}
|
|
@ -0,0 +1,9 @@
|
|||
#include <stdio.h> // Ignore this
|
||||
|
||||
int main(int argc, const char *argv[]) // the main function is the entrypoint to your program
|
||||
{
|
||||
// TODO: use printf() to print "Hello World!" on the display.
|
||||
printf("Hello User!");
|
||||
|
||||
return 0; // Ignore this
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
^Hello World!$
|
|
@ -0,0 +1,8 @@
|
|||
#include <stdio.h>
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
// TODO: use printf() to print "Hello World!" on the display.
|
||||
printf("Hello World!");
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
program/match-regex
|
|
@ -0,0 +1,31 @@
|
|||
<p>
|
||||
You may have heard, that C is a <b>compiled language</b>. But what does that mean?
|
||||
Basically programming languages can be categorized into two classes. Compiled and Interpreted.
|
||||
An interpreted language will be interpreted at run time by another program, which will then send the necessary commands to the CPU to do, what you specified.
|
||||
Examples for that, are languages like Python and Perl.</br>
|
||||
C on the other hand is a compiled language. That means we have a program, the compiler, which <b>turns your code into executable binaries</b>.
|
||||
These can be run directly by the CPU without any overhead.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A compiler consist of multiple stages. First comes the lexer. It turns all the meaningless characters into tokens,
|
||||
which have meaning to the compiler. Then these tokens get parsed, to build the syntax-tree.
|
||||
With the syntax-tree the compiler can start code generation. This code can then be optimized by the optimizer,
|
||||
if you choose so. Bare in mind, that optimized code can be harder to debug.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
On top of that C has the <b>preprocessor</b>. It touches the code before the compiler gets it. It is used for macros,
|
||||
including header-files and other stuff <b>to prepare your code for compilation</b>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In this course you will not see the compiler, because we want you to concentrate on learning C.
|
||||
But if you are done with all the lectures, you should download one of the many compilers.
|
||||
We are using GCC and compiling for the c99 standard. But feel free to experiment with other compilers like Clang.
|
||||
<p>
|
||||
<p>
|
||||
A simple compile command in a command line with gcc could look like this:<br>
|
||||
<code>gcc -std=c99 -o <program_name> <source_code_file>.c</code>
|
||||
For further information read the <a href="https://gcc.gnu.org/onlinedocs/gcc-6.2.0/gcc/C-Dialect-Options.html#C-Dialect-Options">GCC-Documentation<a>.
|
||||
</p>
|
|
@ -0,0 +1 @@
|
|||
{"title": "Compilers"}
|
|
@ -0,0 +1 @@
|
|||
text/html
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
all:
|
||||
cp -f program.c program.S
|
||||
gcc -c program.S -o program.o
|
||||
gcc program.o -o program
|
||||
|
||||
clean:
|
||||
rm -f program
|
||||
rm -f program.o
|
||||
rm -f program.S
|
|
@ -0,0 +1,20 @@
|
|||
<p>
|
||||
Assembly-Language is the most basic form of programming languages. Normally <b>every line of assembly stands for one CPU-instruction</b>.
|
||||
Luckily most people don't program in assembly anymore, because it is hard to understand. Only a few people program part of their programs in it,
|
||||
if they need that extra bit of control, to get that last bit of performance out of that code.
|
||||
</p>
|
||||
<p>
|
||||
Nevertheless it is a very useful tool, to be able to understand assembly. Sometimes you just don't understand, why your code is slow or buggy and you want to know,
|
||||
what the compiler did. Then you can put your code in a disassembler to basically turn machine code into assembly and see exactly what your code is doing.
|
||||
</p>
|
||||
<p>
|
||||
We will only give you a brief introduction to assembly. But if you are interested or serious about low-level programming you should definitely take a look
|
||||
at the <a href="http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-manual-325462.html">
|
||||
<b>Intel 64 and IA-32 Architectures Software Developer's Manual</b></a> and some tutorials on assembly.</br>
|
||||
The following code is the disassembly of the Hello World Program. It may look daunting at first, but it is actually not that hard to understand.
|
||||
Lines with periods are commands to the assembler, words with colon at the end are labels and the rest are assembly instructions, which are usually a one to one mapping
|
||||
to CPU instructions. Please take your time to understand the code.
|
||||
</p>
|
||||
<p>
|
||||
Your task is to change the output of this program to print out Hello <your name>.
|
||||
</p>
|
|
@ -0,0 +1 @@
|
|||
{"title": "Assembly"}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue