Initial copy

master
Julian M. Kunkel 2018-05-05 23:18:02 +01:00
parent 86d2e00bf0
commit 5cf362c910
1359 changed files with 170737 additions and 54 deletions

View File

@ -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

View File

@ -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>

View File

@ -7,5 +7,5 @@ Sphinx
pymongo
celery
markdown
psycopg2
psycopg2-binary
social-auth-app-django

View File

@ -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

16
oer/README.md 100644
View File

@ -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

View File

@ -0,0 +1 @@
{"title": "C Advanced", "description": "Some further material for the C-Programming-Language"}

View File

@ -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.

View File

@ -0,0 +1 @@
{"title": "Introduction"}

View File

@ -0,0 +1 @@
text/html

View File

@ -0,0 +1,8 @@
all:
gcc program.c -o program
#gcc solution.c -o solution
clean:
rm -f program
rm -f solution

View File

@ -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.

View File

@ -0,0 +1 @@
{"title": "Singly Linked List"}

View File

@ -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);
}

View File

@ -0,0 +1 @@
^a b c d $

View File

@ -0,0 +1 @@
program/match-regex

View File

@ -0,0 +1,8 @@
all:
gcc program.c -o program
#gcc solution.c -o solution
clean:
rm -f program
rm -f solution

View File

@ -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.

View File

@ -0,0 +1 @@
{"title": "Double Linked List"}

View File

@ -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);
}

View File

@ -0,0 +1 @@
^a b c d\s*a b c\s*$

View File

@ -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);
}

View File

@ -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);
}

View File

@ -0,0 +1 @@
program/match-regex

View File

@ -0,0 +1,8 @@
all:
gcc program.c -o program
#gcc solution.c -o solution
clean:
rm -f program
rm -f solution

View File

@ -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.

View File

@ -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);
}
}

View File

@ -0,0 +1 @@
{"title": "Hash Table"}

View File

@ -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");
}
}

View File

@ -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$

View File

@ -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");
}
}

View File

@ -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);
}

View File

@ -0,0 +1 @@
program/match-regex

View File

@ -0,0 +1,8 @@
all:
gcc program.c -o program
#gcc solution.c -o solution
clean:
rm -f program
rm -f solution

View File

@ -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.

View File

@ -0,0 +1 @@
{"title": "Queue"}

View File

@ -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));
}
}

View File

@ -0,0 +1 @@
^HelloWorld!$

View File

@ -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));
}
}

View File

@ -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);
}

View File

@ -0,0 +1 @@
program/match-regex

View File

@ -0,0 +1,8 @@
all:
gcc program.c -o program
#gcc solution.c -o solution
clean:
rm -f program
rm -f solution

View File

@ -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.

View File

@ -0,0 +1 @@
{"title": "Stack"}

View File

@ -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));
}
}

View File

@ -0,0 +1 @@
^Hello World!$

View File

@ -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));
}
}

View File

@ -0,0 +1 @@
program/match-regex

View File

@ -0,0 +1,8 @@
all:
gcc program.c -o program
#gcc solution.c -o solution
clean:
rm -f program
rm -f solution

View File

@ -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.

View File

@ -0,0 +1 @@
{"title": "Tree"}

View File

@ -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");
}

View File

@ -0,0 +1 @@
^Found 9 with direct child 3 and 128\nSuccesfully deleted 9\n1 3 6 7 9 10 42 128 500 9001 $

View File

@ -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");
}

View File

@ -0,0 +1 @@
program/match-regex

View File

@ -0,0 +1,8 @@
all:
gcc program.c -o program
#gcc solution.c -o solution
clean:
rm -f program
rm -f solution

View File

@ -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.

View File

@ -0,0 +1 @@
{"title": "Graphs"}

View File

@ -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");
}

View File

@ -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}$

View File

@ -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]);
}
}
}
}
}

View File

@ -0,0 +1 @@
program/match-regex

View File

@ -0,0 +1 @@
{"title": "Basic Datastructures", "description": "Implement your own Datastrucutes"}

View File

@ -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

View File

@ -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.

View File

@ -0,0 +1,3 @@
rand=$(( $RANDOM % 100))
./program $rand

View File

@ -0,0 +1 @@
{"title": "Producer & Consumer"}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -0,0 +1 @@
program/scriptgrade

View File

@ -0,0 +1,8 @@
all:
gcc program.c -o program
gcc solution.c -o solution
clean:
rm -f program
rm -f solution

View File

@ -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.

View File

@ -0,0 +1,11 @@
#!/bin/bash
arguments=$(./term_generator.sh)
program_out=$(./program "$arguments")
echo "term: $arguments"
echo "$program_out"

View File

@ -0,0 +1 @@
{"title": "Stack - Calculator"}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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;
}

View File

@ -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"

View File

@ -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

View File

@ -0,0 +1 @@
program/scriptgrade

View File

@ -0,0 +1 @@
{"title": "Example Algorithms", "description": "Implement algorithms, which use the data-structures from the previous section "}

View File

@ -0,0 +1,3 @@
*.pyc
__pycache__
program

View File

@ -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."}

View File

@ -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>

View File

@ -0,0 +1 @@
{"title": "Introduction"}

View File

@ -0,0 +1 @@
text/html

View File

@ -0,0 +1,6 @@
all:
gcc --std=c99 program.c -o program
clean:
rm -f program

View File

@ -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 &lt;stdio.h&gt;</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>

View File

@ -0,0 +1 @@
{"title": "Hello World!"}

View File

@ -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
}

View File

@ -0,0 +1 @@
^Hello World!$

View File

@ -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;
}

View File

@ -0,0 +1 @@
program/match-regex

View File

@ -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 &lt;program_name&gt; &lt;source_code_file&gt;.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>

View File

@ -0,0 +1 @@
{"title": "Compilers"}

View File

@ -0,0 +1 @@
text/html

View File

@ -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

View File

@ -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 &lt;your name&gt;.
</p>

View File

@ -0,0 +1 @@
{"title": "Assembly"}

Some files were not shown because too many files have changed in this diff Show More