Of course

This commit is contained in:
MarcUs7i 2025-02-13 16:52:14 +01:00
parent d6cffc16e4
commit 8edc8dab71
13 changed files with 951 additions and 951 deletions

View file

@ -1 +1 @@
[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/Ra2fY2sl) [![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/Ra2fY2sl)

View file

@ -1,77 +1,77 @@
### if.03.22 Procedural Programming ### if.03.22 Procedural Programming
# Römische Zahlen # Römische Zahlen
Diese Aufgabe zielt auf die Analyse und Validierung von Zeichenketten, die Bearbeitung von Zeigern sowie eine einfache rekursive Berechnung ab. Diese Aufgabe zielt auf die Analyse und Validierung von Zeichenketten, die Bearbeitung von Zeigern sowie eine einfache rekursive Berechnung ab.
## Einführung ## Einführung
Römische Zahlen werden seit der Antike verwendet. Sie werden durch Buchstaben ausgedrückt, die folgende Werte haben: Römische Zahlen werden seit der Antike verwendet. Sie werden durch Buchstaben ausgedrückt, die folgende Werte haben:
+ I: 1 + I: 1
+ V: 5 + V: 5
+ X: 10 + X: 10
+ L: 50 + L: 50
+ C: 100 + C: 100
+ D: 500 + D: 500
+ M: 1000 + M: 1000
+ einige weitere Zeichen mit höheren Werten + einige weitere Zeichen mit höheren Werten
Der numerische Wert wird einfach durch Addition des Werts jedes Buchstabens berechnet: Der numerische Wert wird einfach durch Addition des Werts jedes Buchstabens berechnet:
> MMCLXXXVII => 1000 + 1000 + 100 + 50 + 10 + 10 + 10 + 5 + 1 + 1 = 2187 > MMCLXXXVII => 1000 + 1000 + 100 + 50 + 10 + 10 + 10 + 5 + 1 + 1 = 2187
Zusätzlich werden Werte abgezogen, wenn ein `I, X oder C` vor dem nächsten größeren Wert steht. Zusätzlich werden Werte abgezogen, wenn ein `I, X oder C` vor dem nächsten größeren Wert steht.
> IV => -1 + 5 = 4 > IV => -1 + 5 = 4
> IX => -1 + 10 = 9 > IX => -1 + 10 = 9
> XL => -10 + 50 = 40 > XL => -10 + 50 = 40
> CD => -100 + 500 = 400 > CD => -100 + 500 = 400
Eine Erweiterung dieser Regel erlaubt das Subtrahieren der darüber liegenden Buchstaben, wenn sie vor noch größeren Werten stehen. Eine Erweiterung dieser Regel erlaubt das Subtrahieren der darüber liegenden Buchstaben, wenn sie vor noch größeren Werten stehen.
> IC => -1 + 100 = 99 anstelle von XCIX => -10 + 100 - 1 + 10 > IC => -1 + 100 = 99 anstelle von XCIX => -10 + 100 - 1 + 10
was die Verwendung der Zahlen vereinfacht. Die Buchstaben `V, L, D` werden niemals als subtraktive Werte verwendet. was die Verwendung der Zahlen vereinfacht. Die Buchstaben `V, L, D` werden niemals als subtraktive Werte verwendet.
Römische Zahlen werden in absteigender Reihenfolge ihres Nennwerts geschrieben, mit Ausnahme des speziellen Falls der Subtraktion. Römische Zahlen werden in absteigender Reihenfolge ihres Nennwerts geschrieben, mit Ausnahme des speziellen Falls der Subtraktion.
> MCMLXVII (1967) darf nicht in anderer Reihenfolge geschrieben werden, z.B. IIVXLMCM ist nicht erlaubt > MCMLXVII (1967) darf nicht in anderer Reihenfolge geschrieben werden, z.B. IIVXLMCM ist nicht erlaubt
## Aufgabe ## Aufgabe
Ihre Aufgabe besteht darin, einen abstrakten Datentyp namens `RomanNumber` zu implementieren, der die numerische Darstellung einer Zeichenkette kapselt, die eine gültige römische Zahl darstellt. Ihre Aufgabe besteht darin, einen abstrakten Datentyp namens `RomanNumber` zu implementieren, der die numerische Darstellung einer Zeichenkette kapselt, die eine gültige römische Zahl darstellt.
Die folgenden Funktionen für den ADT `RomanNumber` sollen implementiert werden: Die folgenden Funktionen für den ADT `RomanNumber` sollen implementiert werden:
+ `rn_is_valid_number_str` + `rn_is_valid_number_str`
Eine private Funktion, die feststellt, ob eine gegebene Zeichenkette eine gültige römische Zahl darstellt. Wenn die Zeichenkette andere Zeichen als die oben definierten enthält, stellt sie KEINE gültige Zahl dar! Eine private Funktion, die feststellt, ob eine gegebene Zeichenkette eine gültige römische Zahl darstellt. Wenn die Zeichenkette andere Zeichen als die oben definierten enthält, stellt sie KEINE gültige Zahl dar!
Die Gültigkeit kann leicht überprüft werden, indem alle Zeichen der Zeichenkette durchlaufen und getestet wird, ob der aktuelle Buchstabe einer definierten entspricht und sein numerischer Wert gleich oder kleiner ist als der numerische Wert des vorherigen Buchstabens. Die einzige Ausnahme von dieser Regel sind gültige Subtraktionsmuster (z.B. IX). Wenn das aktuelle und das nächste Zeichen ein gültiges Subtraktionsmuster bilden, kann mit der bereits implementierten privaten Funktion `rn_is_valid_subtraction` getestet werden. Wenn ein solches gültiges Muster erkannt wurde, bleibt die Zeichenkette für den aktuellen Buchstaben gültig, und die oben genannte Überprüfung kann übersprungen werden. Trotzdem muss der 'nächste' Buchstabe (der zum Testen des Subtraktionsmusters verwendet wurde) weiterhin regelmäßig überprüft werden. Die Gültigkeit kann leicht überprüft werden, indem alle Zeichen der Zeichenkette durchlaufen und getestet wird, ob der aktuelle Buchstabe einer definierten entspricht und sein numerischer Wert gleich oder kleiner ist als der numerische Wert des vorherigen Buchstabens. Die einzige Ausnahme von dieser Regel sind gültige Subtraktionsmuster (z.B. IX). Wenn das aktuelle und das nächste Zeichen ein gültiges Subtraktionsmuster bilden, kann mit der bereits implementierten privaten Funktion `rn_is_valid_subtraction` getestet werden. Wenn ein solches gültiges Muster erkannt wurde, bleibt die Zeichenkette für den aktuellen Buchstaben gültig, und die oben genannte Überprüfung kann übersprungen werden. Trotzdem muss der 'nächste' Buchstabe (der zum Testen des Subtraktionsmusters verwendet wurde) weiterhin regelmäßig überprüft werden.
+ `rn_get_value_for_letter`: + `rn_get_value_for_letter`:
Eine private Funktion, die den numerischen Wert eines einzelnen Buchstabens liefert. Beachten Sie, dass `switch / case`-Anweisungen auch für Zeichen funktionieren. Eine private Funktion, die den numerischen Wert eines einzelnen Buchstabens liefert. Beachten Sie, dass `switch / case`-Anweisungen auch für Zeichen funktionieren.
+ `rn_create`: + `rn_create`:
Alloziert eine neue `RomanNumber` aus einem statisch allokierten Pool von Datenstrukturen für den ADT. Der Pool von `RomanNumber`-Daten wird im globalen Bereich mit einer Größe von `MAX_ROMAN_NUMBER_COUNT` wie in `roman_number.c` definiert, allokiert. Alloziert eine neue `RomanNumber` aus einem statisch allokierten Pool von Datenstrukturen für den ADT. Der Pool von `RomanNumber`-Daten wird im globalen Bereich mit einer Größe von `MAX_ROMAN_NUMBER_COUNT` wie in `roman_number.c` definiert, allokiert.
Es kann darauf vertraut werden, dass der Pool ausreichend groß ist, um Instanzen für alle Unittests bereitzustellen, ohne dass Instanzen an den Pool zurückgegeben werden müssen. Es kann darauf vertraut werden, dass der Pool ausreichend groß ist, um Instanzen für alle Unittests bereitzustellen, ohne dass Instanzen an den Pool zurückgegeben werden müssen.
Es kann darauf vertraut werden, dass eine gültige Zeichenkette bereitgestellt wird, jedoch nicht unbedingt eine gültige römische Zahl. Wenn die Zeichenkette keine gültige römische Zahl darstellt, soll eine ungültige (nicht-null) `RomanNumber` bereitgestellt werden. Die gegebene Zeichenkette muss daher mit der privaten Funktion `rn_is_valid_number_str` überprüft werden, ob die Zeichenkette eine gültige römische Zahl darstellt. Es kann darauf vertraut werden, dass eine gültige Zeichenkette bereitgestellt wird, jedoch nicht unbedingt eine gültige römische Zahl. Wenn die Zeichenkette keine gültige römische Zahl darstellt, soll eine ungültige (nicht-null) `RomanNumber` bereitgestellt werden. Die gegebene Zeichenkette muss daher mit der privaten Funktion `rn_is_valid_number_str` überprüft werden, ob die Zeichenkette eine gültige römische Zahl darstellt.
Wenn die gegebene Zeichenkette eine gültige römische Zahl ist, soll ihr numerischer Wert bei der Erstellung berechnet und in den Daten des ADTs gespeichert werden. Die römische Zahl wird einfach durch Addition der Werte ihrer Buchstaben umgewandelt, es sei denn, ein Subtraktionsmuster wird erkannt. In diesem Fall muss der Wert des Buchstabens subtrahiert werden. Verwenden Sie die Funktion `rn_is_valid_subtraction`, um nach Subtraktionen zu suchen. Wenn die gegebene Zeichenkette eine gültige römische Zahl ist, soll ihr numerischer Wert bei der Erstellung berechnet und in den Daten des ADTs gespeichert werden. Die römische Zahl wird einfach durch Addition der Werte ihrer Buchstaben umgewandelt, es sei denn, ein Subtraktionsmuster wird erkannt. In diesem Fall muss der Wert des Buchstabens subtrahiert werden. Verwenden Sie die Funktion `rn_is_valid_subtraction`, um nach Subtraktionen zu suchen.
*Beachten Sie*, dass eine leere Zeichenkette gültig ist und zu einem Wert von null (0) führt, während eine null-Zeichenkette ungültig ist. *Beachten Sie*, dass eine leere Zeichenkette gültig ist und zu einem Wert von null (0) führt, während eine null-Zeichenkette ungültig ist.
+ `rn_is_valid`: + `rn_is_valid`:
Bestimmt, ob die gegebene `RomanNumber` gültig ist. Sie ist gültig, wenn sie weder 0 ist noch aus einer ungültigen römischen Zahl erstellt wurde. Bestimmt, ob die gegebene `RomanNumber` gültig ist. Sie ist gültig, wenn sie weder 0 ist noch aus einer ungültigen römischen Zahl erstellt wurde.
+ `rn_get_value`: + `rn_get_value`:
Liefert den numerischen Wert der gegebenen `RomanNumber`, wie er in den Daten des ADTs gespeichert ist, oder einen Wert kleiner als null, wenn die `RomanNumber` nicht gültig ist. Liefert den numerischen Wert der gegebenen `RomanNumber`, wie er in den Daten des ADTs gespeichert ist, oder einen Wert kleiner als null, wenn die `RomanNumber` nicht gültig ist.
+ `rn_gcd`: + `rn_gcd`:
Berechnet den größten gemeinsamen Teiler (GGT / GCD) von zwei `RomanNumber`s und gibt das Ergebnis erneut als `RomanNumber` zurück. Berechnet den größten gemeinsamen Teiler (GGT / GCD) von zwei `RomanNumber`s und gibt das Ergebnis erneut als `RomanNumber` zurück.
__Wichtiger Hinweis:__ Diese Funktion berechnet DEN GGT NICHT tatsächlich, sondern verwendet die Funktion `int_gcd` zu diesem Zweck. Sie wandelt die römische Zahl nur hin und her. __Wichtiger Hinweis:__ Diese Funktion berechnet DEN GGT NICHT tatsächlich, sondern verwendet die Funktion `int_gcd` zu diesem Zweck. Sie wandelt die römische Zahl nur hin und her.
+ `int_gcd`: + `int_gcd`:
Berechnet tatsächlich den größten gemeinsamen Teiler (GGT / GCD) von zwei positiven Ganzzahlen __mit Rekursion__. Es kann darauf vertraut werden, dass die gegebenen Ganzzahlen immer positiv sind. Berechnet tatsächlich den größten gemeinsamen Teiler (GGT / GCD) von zwei positiven Ganzzahlen __mit Rekursion__. Es kann darauf vertraut werden, dass die gegebenen Ganzzahlen immer positiv sind.
Der GGT ist definiert als: Der GGT ist definiert als:
- `ggd(x, 0) = x;` - `ggd(x, 0) = x;`
- `ggd(x, y) = ggd(y, x % y);` - `ggd(x, y) = ggd(y, x % y);`
wobei `%` der Modulo-Operator ist. wobei `%` der Modulo-Operator ist.
_Beachten Sie_, dass diese Funktion unabhängig getestet wird und implementiert werden kann, auch wenn der ADT nicht funktioniert. _Beachten Sie_, dass diese Funktion unabhängig getestet wird und implementiert werden kann, auch wenn der ADT nicht funktioniert.
## Aufgaben ## Aufgaben
+ Erstellen Sie eine Skelettimplementierung des ADTs, um die Unittests kompilierbar zu machen. + Erstellen Sie eine Skelettimplementierung des ADTs, um die Unittests kompilierbar zu machen.
+ Implementieren Sie alle erforderlichen Funktionen, um die Unittests zu bestehen. + Implementieren Sie alle erforderlichen Funktionen, um die Unittests zu bestehen.
+ Eine Hauptanwendung ist nicht erforderlich. + Eine Hauptanwendung ist nicht erforderlich.
### Viel Erfolg! ### Viel Erfolg!

View file

@ -1,79 +1,79 @@
### if.03.22 Procedural Programming ### if.03.22 Procedural Programming
# Roman numbers # Roman numbers
This assignment targets on string analysis and validation, This assignment targets on string analysis and validation,
pointer handling as well as a simple recursive calculation. pointer handling as well as a simple recursive calculation.
## Introduction ## Introduction
Roman numbers are used since ancient ages. They are expressed via letters which have the following values: Roman numbers are used since ancient ages. They are expressed via letters which have the following values:
+ I: 1 + I: 1
+ V: 5 + V: 5
+ X: 10 + X: 10
+ L: 50 + L: 50
+ C: 100 + C: 100
+ D: 500 + D: 500
+ M: 1000 + M: 1000
+ some more higher value signs + some more higher value signs
The numerical value is calculated by simply adding the value of each letter: The numerical value is calculated by simply adding the value of each letter:
> MMCLXXXVII => 1000 + 1000 + 100 + 50 + 10 + 10 + 10 + 5 + 1 + 1 = 2187 > MMCLXXXVII => 1000 + 1000 + 100 + 50 + 10 + 10 + 10 + 5 + 1 + 1 = 2187
In addition, values are subtracted, if one `I, X, or C` is placed before the next larger value. In addition, values are subtracted, if one `I, X, or C` is placed before the next larger value.
> IV => -1 + 5 = 4 > IV => -1 + 5 = 4
> IX => -1 + 10 = 9 > IX => -1 + 10 = 9
> XL => -10 + 50 = 40 > XL => -10 + 50 = 40
> CD => -100 + 500 = 400 > CD => -100 + 500 = 400
An extension of this rule allows subtraction of the letters above if they are placed in front of even larger values An extension of this rule allows subtraction of the letters above if they are placed in front of even larger values
> IC => -1 + 100 = 99 instead of XCIX => -10 + 100 - 1 + 10 > IC => -1 + 100 = 99 instead of XCIX => -10 + 100 - 1 + 10
which simplifies the usage of the numbers. which simplifies the usage of the numbers.
The letters `V, L, D` are never used as subtractive values. The letters `V, L, D` are never used as subtractive values.
Roman number letters are written in descending order of their nominal value, except for the special case of subtraction. Roman number letters are written in descending order of their nominal value, except for the special case of subtraction.
> MCMLXVII (1967) must not be written in different order, e.g. IIVXLMCM is not allowed > MCMLXVII (1967) must not be written in different order, e.g. IIVXLMCM is not allowed
## Assignment ## Assignment
Your task is to implement an abstract data type calls `RomanNumber`, which encapsulates the numerical representation of a string that expresses a valid roman number. Your task is to implement an abstract data type calls `RomanNumber`, which encapsulates the numerical representation of a string that expresses a valid roman number.
The following functions for the ADT `RomanNumber` shall be implemented: The following functions for the ADT `RomanNumber` shall be implemented:
+ `rn_is_valid_number_str` + `rn_is_valid_number_str`
A private function which determines whether a given string represents a valid roman number. If the string contains any other characters but the above defined ones, it DOES NOT represent a valid number! A private function which determines whether a given string represents a valid roman number. If the string contains any other characters but the above defined ones, it DOES NOT represent a valid number!
The validity can easily be done by iterating through all characters of the string and testing if the current letter is a defined one and its numerical value is equal or less the numerical letter of the previous letter. The only exception to this rule are valid subtraction patterns (e.g. IX). If the current and next letter form a valid subtraction pattern can be tested with the already implemented private function `rn_is_valid_subtraction`. If such a valid pattern was detected, the string remains valid for the current letter and the check stated above can be skipped. Nevertheless, the 'next' letter (which was used to test the subtraction pattern) still need regularly be checked. The validity can easily be done by iterating through all characters of the string and testing if the current letter is a defined one and its numerical value is equal or less the numerical letter of the previous letter. The only exception to this rule are valid subtraction patterns (e.g. IX). If the current and next letter form a valid subtraction pattern can be tested with the already implemented private function `rn_is_valid_subtraction`. If such a valid pattern was detected, the string remains valid for the current letter and the check stated above can be skipped. Nevertheless, the 'next' letter (which was used to test the subtraction pattern) still need regularly be checked.
+ `rn_get_value_for_letter`: + `rn_get_value_for_letter`:
A private function that provides the numerical value of a single letter. Note that `switch / case` instructions work for characters as well. A private function that provides the numerical value of a single letter. Note that `switch / case` instructions work for characters as well.
+ `rn_create`: + `rn_create`:
Allocates a new `RomanNumber` out of a statically allocated pool of data structs for the ADT. The pool of `RomanNumber` data is allocated on the global scope with a size of `MAX_ROMAN_NUMBER_COUNT` as defined in `roman_number.c`. Allocates a new `RomanNumber` out of a statically allocated pool of data structs for the ADT. The pool of `RomanNumber` data is allocated on the global scope with a size of `MAX_ROMAN_NUMBER_COUNT` as defined in `roman_number.c`.
It MAY be trusted, that the pool is sufficiently large to provide instances for all unit tests without the need of returning instances to the pool. It MAY be trusted, that the pool is sufficiently large to provide instances for all unit tests without the need of returning instances to the pool.
It MAY be trusted that a valid string is provided, although not necessarily a valid roman number string. If the string does not represent a valid roman number, an invalid (non-null)`RomanNumber` shall be provided. The given string need therefore be checked using the private function `rn_is_valid_number_str`, whether the string represents a valid roman number. It MAY be trusted that a valid string is provided, although not necessarily a valid roman number string. If the string does not represent a valid roman number, an invalid (non-null)`RomanNumber` shall be provided. The given string need therefore be checked using the private function `rn_is_valid_number_str`, whether the string represents a valid roman number.
If the given string is a valid roman number, its numerical value shall be calculated upon creation and stored within the ADTs data. The roman number is converted by simply adding the values of its letters, except a subtraction pattern is detected. In this case the value of the letter has to be subtracted. Use the function `rn_is_valid_subtraction` to check for subtractions. If the given string is a valid roman number, its numerical value shall be calculated upon creation and stored within the ADTs data. The roman number is converted by simply adding the values of its letters, except a subtraction pattern is detected. In this case the value of the letter has to be subtracted. Use the function `rn_is_valid_subtraction` to check for subtractions.
*Note* that an empty string is valid and results in the value zero (0), while a null-string is invalid. *Note* that an empty string is valid and results in the value zero (0), while a null-string is invalid.
+ `rn_is_valid`: + `rn_is_valid`:
Determines whether or not the given `RomanNumber` is valid. It is, if it is neither 0 nor was created from an invalid roman number. Determines whether or not the given `RomanNumber` is valid. It is, if it is neither 0 nor was created from an invalid roman number.
+ `rn_get_value`: + `rn_get_value`:
Provides the numerical value of the given `RomanNumber` as stored in the ADTs data or a value less than zero, if the `RomanNumber` is not valid. Provides the numerical value of the given `RomanNumber` as stored in the ADTs data or a value less than zero, if the `RomanNumber` is not valid.
+ `rn_gcd`: + `rn_gcd`:
Calculates the greatest common divisor of two `RomanNumber`s and returns the result again as `RomanNumber`. Calculates the greatest common divisor of two `RomanNumber`s and returns the result again as `RomanNumber`.
__Important note:__ This function DOES NOT actually calculate the GCD but uses the function `int_gcd` for this purpose. It only 'converts' the roman number back and forth. __Important note:__ This function DOES NOT actually calculate the GCD but uses the function `int_gcd` for this purpose. It only 'converts' the roman number back and forth.
+ `int_gcd`: + `int_gcd`:
Actually calculates the greatest common divisor (GCD) of two positive integer values __using recursion__. It MAY be trusted, that the given integers are always positive. Actually calculates the greatest common divisor (GCD) of two positive integer values __using recursion__. It MAY be trusted, that the given integers are always positive.
The GCD is defined as: The GCD is defined as:
- `gcd(x, 0) = x;` - `gcd(x, 0) = x;`
- `gcd(x, y) = gcd(y, x % y);` - `gcd(x, y) = gcd(y, x % y);`
where `%` is the modulo operator. where `%` is the modulo operator.
_Note_ the this function is independently tested and can be implemented even if the ADT is not working. _Note_ the this function is independently tested and can be implemented even if the ADT is not working.
## Tasks ## Tasks
+ Create a skeleton implmentation of the ADT to make the unit tests compilable. + Create a skeleton implmentation of the ADT to make the unit tests compilable.
+ Implement all required functions to make the unit tests pass. + Implement all required functions to make the unit tests pass.
+ A main application is not required. + A main application is not required.
### Good Luck! ### Good Luck!

View file

@ -1,20 +1,20 @@
/*---------------------------------------------------------- /*----------------------------------------------------------
* HTBLA-Leonding * HTBLA-Leonding
* --------------------------------------------------------- * ---------------------------------------------------------
* Exercise Number: n/a * Exercise Number: n/a
* Title: general.h * Title: general.h
* Author: P. Bauer, S. Schraml * Author: P. Bauer, S. Schraml
* ---------------------------------------------------------- * ----------------------------------------------------------
* Description: * Description:
* General usable definitions and types. * General usable definitions and types.
* ---------------------------------------------------------- * ----------------------------------------------------------
*/ */
#ifndef ___GENERAL_H #ifndef ___GENERAL_H
#define ___GENERAL_H #define ___GENERAL_H
/** Convenience macro do get maximum of two numbers */ /** Convenience macro do get maximum of two numbers */
#define MAX(x, y) ((x) > (y) ? (x) : (y)) #define MAX(x, y) ((x) > (y) ? (x) : (y))
/** Convenience macro do get maximum of two numbers */ /** Convenience macro do get maximum of two numbers */
#define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MIN(x, y) ((x) < (y) ? (x) : (y))
#endif #endif

View file

@ -1,19 +1,19 @@
/*---------------------------------------------------------- /*----------------------------------------------------------
* HTBLA-Leonding * HTBLA-Leonding
* --------------------------------------------------------- * ---------------------------------------------------------
* Author: S. Schraml * Author: S. Schraml
* ---------------------------------------------------------- * ----------------------------------------------------------
* Description: * Description:
* The Roman Number Calculator. * The Roman Number Calculator.
* ---------------------------------------------------------- * ----------------------------------------------------------
*/ */
/** /**
* The main entry point of the application. * The main entry point of the application.
*/ */
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
/* No implementation needed */ /* No implementation needed */
return 0; return 0;
} }

200
makefile
View file

@ -1,100 +1,100 @@
CC = gcc CC = gcc
CCLINK = g++ CCLINK = g++
LIBS = LIBS =
CCOPTIONS = -Wall -pedantic -std=c11 -g CCOPTIONS = -Wall -pedantic -std=c11 -g
LDOPTIONS = LDOPTIONS =
BUILD_DIR = build BUILD_DIR = build
TEST = test_roman_number TEST = test_roman_number
PROGRAM = roman_number PROGRAM = roman_number
COMMON_HDRS = general.h COMMON_HDRS = general.h
LIBRARY_FILES = shortcut LIBRARY_FILES = shortcut
ASSIGNMENT_HDRS = ASSIGNMENT_HDRS =
ASSIGNMENT_FILES = roman_number ASSIGNMENT_FILES = roman_number
MAIN_DRIVER = main_driver MAIN_DRIVER = main_driver
TEST_DRIVER = test_driver TEST_DRIVER = test_driver
LIBRARY_H = $(addsuffix .h, $(LIBRARY_FILES)) LIBRARY_H = $(addsuffix .h, $(LIBRARY_FILES))
ASSIGNMENT_H = $(addsuffix .h, $(ASSIGNMENT_FILES)) $(ASSIGNMENT_HDRS) ASSIGNMENT_H = $(addsuffix .h, $(ASSIGNMENT_FILES)) $(ASSIGNMENT_HDRS)
ASSIGNMENT_C = $(addsuffix .c, $(ASSIGNMENT_FILES)) ASSIGNMENT_C = $(addsuffix .c, $(ASSIGNMENT_FILES))
HDRS = $(ASSIGNEMT_H) $(SHARED_HDRS) $(COMMON_HDRS) $(LIBRARY_H) HDRS = $(ASSIGNEMT_H) $(SHARED_HDRS) $(COMMON_HDRS) $(LIBRARY_H)
TESTOBJECT = $(addprefix $(BUILD_DIR)/, $(TEST_DRIVER).o) TESTOBJECT = $(addprefix $(BUILD_DIR)/, $(TEST_DRIVER).o)
MAINOBJECT = $(addprefix $(BUILD_DIR)/, $(MAIN_DRIVER).o) MAINOBJECT = $(addprefix $(BUILD_DIR)/, $(MAIN_DRIVER).o)
LIBRARY_OBJS = $(addprefix $(BUILD_DIR)/, $(addsuffix .o, $(LIBRARY_FILES))) LIBRARY_OBJS = $(addprefix $(BUILD_DIR)/, $(addsuffix .o, $(LIBRARY_FILES)))
TEST_OBJS = $(addprefix $(BUILD_DIR)/, $(addprefix test_, $(addsuffix .o, $(ASSIGNMENT_FILES)))) TEST_OBJS = $(addprefix $(BUILD_DIR)/, $(addprefix test_, $(addsuffix .o, $(ASSIGNMENT_FILES))))
MAIN_OBJ = $(addprefix $(BUILD_DIR)/, $(addsuffix .o, $(ASSIGNMENT_FILES))) MAIN_OBJ = $(addprefix $(BUILD_DIR)/, $(addsuffix .o, $(ASSIGNMENT_FILES)))
OBJS = $(LIBRARY_OBJS) $(MAIN_OBJ) $(TEST_OBJS) OBJS = $(LIBRARY_OBJS) $(MAIN_OBJ) $(TEST_OBJS)
DOXY = doxygen DOXY = doxygen
all: $(PROGRAM) all: $(PROGRAM)
./$(PROGRAM) ./$(PROGRAM)
./$(PROGRAM) -arg1 -arg2 -arg3 ./$(PROGRAM) -arg1 -arg2 -arg3
./$(PROGRAM) -arg1 -arg2 -arg3 --change ./$(PROGRAM) -arg1 -arg2 -arg3 --change
./$(PROGRAM) -arg1 a r g --change -argument -ARG5 -another6 77 -arg8 ./$(PROGRAM) -arg1 a r g --change -argument -ARG5 -another6 77 -arg8
$(TEST): $(BUILD_DIR) $(OBJS) $(TESTOBJECT) $(TEST): $(BUILD_DIR) $(OBJS) $(TESTOBJECT)
$(CCLINK) -o $@ $(LDOPTIONS) $(OBJS) $(TESTOBJECT) $(CCLINK) -o $@ $(LDOPTIONS) $(OBJS) $(TESTOBJECT)
$(PROGRAM): $(BUILD_DIR) $(OBJS) $(MAINOBJECT) $(PROGRAM): $(BUILD_DIR) $(OBJS) $(MAINOBJECT)
$(CCLINK) -o $@ $(LDOPTIONS) $(OBJS) $(MAINOBJECT) $(CCLINK) -o $@ $(LDOPTIONS) $(OBJS) $(MAINOBJECT)
.PHONY: clean cleanall doxy test setsample setassignment definesample defineassignment assignmentfolder .PHONY: clean cleanall doxy test setsample setassignment definesample defineassignment assignmentfolder
clean: clean:
rm -f $(PROGRAM) $(TEST) $(TESTOBJECT) $(MAINOBJECT) $(OBJS) rm -f $(PROGRAM) $(TEST) $(TESTOBJECT) $(MAINOBJECT) $(OBJS)
rm -rf $(BUILD_DIR) rm -rf $(BUILD_DIR)
rm -f *.o rm -f *.o
cleanall: clean cleanall: clean
rm -f index.html rm -f index.html
rm -rf html rm -rf html
doxy: doxy:
$(DOXY) $(DOXY)
rm -f index.html rm -f index.html
ln -s html/index.html index.html ln -s html/index.html index.html
test: $(TEST) test: $(TEST)
./$(TEST) ./$(TEST)
cleantest: clean cleantest: clean
clear clear
make test make test
$(BUILD_DIR): $(BUILD_DIR):
mkdir -p $(BUILD_DIR) mkdir -p $(BUILD_DIR)
$(BUILD_DIR)/%.o: %.c $(BUILD_DIR)/%.o: %.c
$(CC) $(CCOPTIONS) -c -o $@ $< $(CC) $(CCOPTIONS) -c -o $@ $<
#sets project as sample solution #sets project as sample solution
setsample: setsample:
$(foreach name, $(ASSIGNMENT_H) $(ASSIGNMENT_C), cp $(name).sample $(name);) $(foreach name, $(ASSIGNMENT_H) $(ASSIGNMENT_C), cp $(name).sample $(name);)
#sets project as assignment #sets project as assignment
setassignment: setassignment:
$(foreach name, $(ASSIGNMENT_H) $(ASSIGNMENT_C), cp $(name).assignment $(name);) $(foreach name, $(ASSIGNMENT_H) $(ASSIGNMENT_C), cp $(name).assignment $(name);)
# defines current state of project as sample solution # defines current state of project as sample solution
definesample: definesample:
$(foreach name, $(ASSIGNMENT_H) $(ASSIGNMENT_C), cp $(name) $(name).sample;) $(foreach name, $(ASSIGNMENT_H) $(ASSIGNMENT_C), cp $(name) $(name).sample;)
# defines current sate of project as assignment # defines current sate of project as assignment
defineassignment : defineassignment :
$(foreach name, $(ASSIGNMENT_H) $(ASSIGNMENT_C), cp $(name) $(name).assignment;) $(foreach name, $(ASSIGNMENT_H) $(ASSIGNMENT_C), cp $(name) $(name).assignment;)
# creates a folder which can serve as a publishable assignment # creates a folder which can serve as a publishable assignment
assignmentfolder: assignmentfolder:
make setassignment make setassignment
rm -rf ../assignment rm -rf ../assignment
mkdir ../assignment mkdir ../assignment
cp -R * ../assignment cp -R * ../assignment
cp .gitignore ../assignment cp .gitignore ../assignment
rm ../assignment/*.sample rm ../assignment/*.sample
rm ../assignment/*.assignment rm ../assignment/*.assignment
make cleanall make cleanall

View file

@ -1,193 +1,193 @@
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
* HTBLA-Leonding * HTBLA-Leonding
*----------------------------------------------------------------------------- *-----------------------------------------------------------------------------
* Author(s): Marc Tismonar * Author(s): Marc Tismonar
*----------------------------------------------------------------------------- *-----------------------------------------------------------------------------
* Description: * Description:
* Encapsulates roman numbers and provides basic mathematical operations. * Encapsulates roman numbers and provides basic mathematical operations.
*----------------------------------------------------------------------------- *-----------------------------------------------------------------------------
*/ */
#include "roman_number.h" #include "roman_number.h"
// the maximum number of RomanNumbers // the maximum number of RomanNumbers
#define MAX_ROMAN_NUMBER_COUNT 32 #define MAX_ROMAN_NUMBER_COUNT 32
struct RomanNumberData { struct RomanNumberData {
char* number; char* number;
int length; int length;
}; };
static RomanNumber romanNumber = {0}; static RomanNumber romanNumber = {0};
/* Determines whether or not the letter followed by next_letter is a valid subtraction pattern of roman numbers */ /* Determines whether or not the letter followed by next_letter is a valid subtraction pattern of roman numbers */
static bool rn_is_valid_subtraction(char letter, char next_letter) { static bool rn_is_valid_subtraction(char letter, char next_letter) {
return (letter == 'I' && (next_letter == 'V' || next_letter == 'X' || next_letter == 'L' || next_letter == 'C' || next_letter == 'D' || next_letter == 'M')) return (letter == 'I' && (next_letter == 'V' || next_letter == 'X' || next_letter == 'L' || next_letter == 'C' || next_letter == 'D' || next_letter == 'M'))
|| (letter == 'X' && (next_letter == 'L' || next_letter == 'C' || next_letter == 'D' || next_letter == 'M')) || (letter == 'X' && (next_letter == 'L' || next_letter == 'C' || next_letter == 'D' || next_letter == 'M'))
|| (letter == 'C' && (next_letter == 'D' || next_letter == 'M')); || (letter == 'C' && (next_letter == 'D' || next_letter == 'M'));
} }
static int roman_char_to_int(char romanNumber) { static int roman_char_to_int(char romanNumber) {
switch(romanNumber) { switch(romanNumber) {
case 'I': case 'I':
{ {
return 1; return 1;
} }
case 'V': case 'V':
{ {
return 5; return 5;
} }
case 'X': case 'X':
{ {
return 10; return 10;
} }
case 'L': case 'L':
{ {
return 50; return 50;
} }
case 'C': case 'C':
{ {
return 100; return 100;
} }
case 'D': case 'D':
{ {
return 500; return 500;
} }
case 'M': case 'M':
{ {
return 1000; return 1000;
} }
default: default:
{ {
return 0; return 0;
} }
} }
} }
static char int_to_roman_char(int number) { static char int_to_roman_char(int number) {
switch(number) { switch(number) {
case 1: case 1:
{ {
return 'I'; return 'I';
} }
case 5: case 5:
{ {
return 'V'; return 'V';
} }
case 10: case 10:
{ {
return 'X'; return 'X';
} }
case 50: case 50:
{ {
return 'L'; return 'L';
} }
case 100: case 100:
{ {
return 'C'; return 'C';
} }
case 500: case 500:
{ {
return 'D'; return 'D';
} }
case 1000: case 1000:
{ {
return 'M'; return 'M';
} }
default: default:
{ {
return 0; return 0;
} }
} }
} }
static bool rn_is_valid_number_str(char* string_number) { static bool rn_is_valid_number_str(char* string_number) {
if(string_number == 0) { if(string_number == 0) {
return 0; return 0;
} }
for(int i = 0; i < strlen(string_number); i++) { for(int i = 0; i < strlen(string_number); i++) {
if(roman_char_to_int(string_number[i]) == 0) { if(roman_char_to_int(string_number[i]) == 0) {
return false; return false;
} }
} }
return true; return true;
} }
RomanNumber rn_create(char* value) { RomanNumber rn_create(char* value) {
if(value == 0) { if(value == 0) {
return 0; return 0;
} }
romanNumber = (RomanNumber)calloc(1, sizeof(struct RomanNumberData)); romanNumber = (RomanNumber)calloc(1, sizeof(struct RomanNumberData));
romanNumber->number = value; romanNumber->number = value;
romanNumber->length = strlen(value); romanNumber->length = strlen(value);
return romanNumber; return romanNumber;
} }
int rn_get_value(RomanNumber number) { int rn_get_value(RomanNumber number) {
if(number == 0 || !rn_is_valid(number)) { if(number == 0 || !rn_is_valid(number)) {
return -1; return -1;
} }
int next_number = 0; int next_number = 0;
int current_number = 0; int current_number = 0;
int total_number = 0; int total_number = 0;
for(int i = 0; i < strlen(number->number); i++) { for(int i = 0; i < strlen(number->number); i++) {
current_number = roman_char_to_int(number->number[i]); current_number = roman_char_to_int(number->number[i]);
if (number->number[i+1] == '\0') { if (number->number[i+1] == '\0') {
total_number += current_number; total_number += current_number;
return total_number; return total_number;
} }
next_number = roman_char_to_int(number->number[i+1]); next_number = roman_char_to_int(number->number[i+1]);
if(current_number < next_number) { if(current_number < next_number) {
if(!rn_is_valid_subtraction(number->number[i], number->number[i+1])) { if(!rn_is_valid_subtraction(number->number[i], number->number[i+1])) {
return -1; return -1;
} }
total_number -= current_number; total_number -= current_number;
} else { } else {
total_number += current_number; total_number += current_number;
} }
} }
return total_number; return total_number;
} }
bool rn_is_valid(RomanNumber number) { bool rn_is_valid(RomanNumber number) {
if(number == 0) { if(number == 0) {
return 0; return 0;
} }
for(int i = 0; i < number->length; i++) { for(int i = 0; i < number->length; i++) {
if(roman_char_to_int(number->number[i]) == 0) { if(roman_char_to_int(number->number[i]) == 0) {
return false; return false;
} }
} }
return true; return true;
} }
RomanNumber rn_gcd(RomanNumber x, RomanNumber y) { RomanNumber rn_gcd(RomanNumber x, RomanNumber y) {
if(x == 0 || y == 0) { if(x == 0 || y == 0) {
return -1; return -1;
} }
int intX = rn_get_value(x); int intX = rn_get_value(x);
int intY = rn_get_value(y); int intY = rn_get_value(y);
int result = int_gcd(intX, intY); int result = int_gcd(intX, intY);
} }
int int_gcd(int x, int y) { int int_gcd(int x, int y) {
return 0; return 0;
} }

View file

@ -1,67 +1,67 @@
/*---------------------------------------------------------- /*----------------------------------------------------------
* HTBLA-Leonding * HTBLA-Leonding
* --------------------------------------------------------- * ---------------------------------------------------------
* Author: Marc Tismonar * Author: Marc Tismonar
* ---------------------------------------------------------- * ----------------------------------------------------------
* Description: * Description:
* Encapsulates roman numbers and provides basic mathematical operations. * Encapsulates roman numbers and provides basic mathematical operations.
* ---------------------------------------------------------- * ----------------------------------------------------------
*/ */
#ifndef ___ROMAN_NUMBER #ifndef ___ROMAN_NUMBER
#define ___ROMAN_NUMBER #define ___ROMAN_NUMBER
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
typedef struct RomanNumberData* RomanNumber; typedef struct RomanNumberData* RomanNumber;
/** /**
* Creates a roman number from the given string. * Creates a roman number from the given string.
* If the given string is not valid or not a valid * If the given string is not valid or not a valid
* number, an invalid RomanNumber is provided. * number, an invalid RomanNumber is provided.
* *
* @param value The roman number string. * @param value The roman number string.
* @return RomanNumber * @return RomanNumber
*/ */
RomanNumber rn_create(char* value); RomanNumber rn_create(char* value);
/** /**
* Determines whether or not the RomanNumber is valid. * Determines whether or not the RomanNumber is valid.
* *
* @param number The number to test. * @param number The number to test.
* @return true if the number is valid, false otherwise. * @return true if the number is valid, false otherwise.
*/ */
bool rn_is_valid(RomanNumber number); bool rn_is_valid(RomanNumber number);
/** /**
* Provides the value of the RomanNumber or a value less * Provides the value of the RomanNumber or a value less
* than zero, if the number is not valid. * than zero, if the number is not valid.
* *
* @param number The number to convert. * @param number The number to convert.
* @return The integral value of the roman number or a value * @return The integral value of the roman number or a value
* less than 0; * less than 0;
*/ */
int rn_get_value(RomanNumber number); int rn_get_value(RomanNumber number);
/** /**
* Calculates the greatest common divisor of two roman numbers. * Calculates the greatest common divisor of two roman numbers.
* *
* @param x The first number. * @param x The first number.
* @param y The second number. * @param y The second number.
* @return The result or an invalid roman number, if at least one * @return The result or an invalid roman number, if at least one
* of the given values is invalid. * of the given values is invalid.
*/ */
RomanNumber rn_gcd(RomanNumber x, RomanNumber y); RomanNumber rn_gcd(RomanNumber x, RomanNumber y);
/** /**
* Calculates the greatest common divisor of two integers. * Calculates the greatest common divisor of two integers.
* *
* @param x The first operand. * @param x The first operand.
* @param y The second operand * @param y The second operand
* @return The result. * @return The result.
*/ */
int int_gcd(int x, int y); int int_gcd(int x, int y);
#endif #endif

View file

@ -1,151 +1,151 @@
/*---------------------------------------------------------- /*----------------------------------------------------------
* HTBLA-Leonding * HTBLA-Leonding
* --------------------------------------------------------- * ---------------------------------------------------------
* Title: shortcut.c * Title: shortcut.c
* Author: P. Bauer * Author: P. Bauer
* Date: November 08, 2010 * Date: November 08, 2010
* ---------------------------------------------------------- * ----------------------------------------------------------
* Description: * Description:
* Test driver. * Test driver.
* ---------------------------------------------------------- * ----------------------------------------------------------
*/ */
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include "shortcut.h" #include "shortcut.h"
#define MAX_TEST_FUNCTIONS 256 #define MAX_TEST_FUNCTIONS 256
static char assert_msg_buffer[1024]; static char assert_msg_buffer[1024];
static int tc_count = 0; static int tc_count = 0;
static int tc_fail_count = 0; static int tc_fail_count = 0;
static struct TestCase test_cases[MAX_TEST_FUNCTIONS]; static struct TestCase test_cases[MAX_TEST_FUNCTIONS];
const char* version() const char* version()
{ {
return "ShortCut v. 1.3.0"; return "ShortCut v. 1.3.0";
} }
char* format_msg(char* format, ...) { char* format_msg(char* format, ...) {
va_list args; va_list args;
va_start (args, format); va_start (args, format);
vsprintf(assert_msg_buffer, format, args); vsprintf(assert_msg_buffer, format, args);
return assert_msg_buffer; return assert_msg_buffer;
} }
void assert_true(bool bool_expr, struct TestCase *tc, const char *msg, void assert_true(bool bool_expr, struct TestCase *tc, const char *msg,
const char* file, int line) const char* file, int line)
{ {
if (!bool_expr) { if (!bool_expr) {
if (tc->success) { if (tc->success) {
tc->success = false; tc->success = false;
tc_fail_count++; tc_fail_count++;
} }
printf("\n\tFailure (file: %s, line %d): %s: %s", file, line, tc->name, msg); printf("\n\tFailure (file: %s, line %d): %s: %s", file, line, tc->name, msg);
} }
} }
void assert_false(bool bool_expr, struct TestCase *tc, const char *msg, void assert_false(bool bool_expr, struct TestCase *tc, const char *msg,
const char* file, int line) const char* file, int line)
{ {
assert_true(!bool_expr, tc, msg, file, line); assert_true(!bool_expr, tc, msg, file, line);
} }
static void assert_string_failure(const char *expected, char *actual, struct TestCase *tc, static void assert_string_failure(const char *expected, char *actual, struct TestCase *tc,
const char *msg, const char* file, int line); const char *msg, const char* file, int line);
void assert_equals_str(const char *expected, char *actual, struct TestCase *tc, void assert_equals_str(const char *expected, char *actual, struct TestCase *tc,
const char *msg, const char* file, int line) const char *msg, const char* file, int line)
{ {
if (expected == actual) { if (expected == actual) {
return; return;
} }
if (expected == 0 || actual == 0) { if (expected == 0 || actual == 0) {
assert_string_failure(expected, actual, tc, msg, file, line); assert_string_failure(expected, actual, tc, msg, file, line);
return; return;
} }
if (strcmp(actual, expected) != 0) { if (strcmp(actual, expected) != 0) {
assert_string_failure(expected, actual, tc, msg, file, line); assert_string_failure(expected, actual, tc, msg, file, line);
return; return;
} }
} }
#define MAX_MSG_LEN 128 #define MAX_MSG_LEN 128
static void assert_string_failure(const char *expected, char *actual, struct TestCase *tc, static void assert_string_failure(const char *expected, char *actual, struct TestCase *tc,
const char *msg, const char* file, int line) const char *msg, const char* file, int line)
{ {
char new_msg[MAX_MSG_LEN]; char new_msg[MAX_MSG_LEN];
sprintf(new_msg, "Expected \"%s\", actual \"%s\". %s", expected, actual, msg); sprintf(new_msg, "Expected \"%s\", actual \"%s\". %s", expected, actual, msg);
assert_true(false, tc, new_msg, file, line); assert_true(false, tc, new_msg, file, line);
} }
void assert_equals(int expected, int actual, struct TestCase *tc, void assert_equals(int expected, int actual, struct TestCase *tc,
const char *msg, const char* file, int line) const char *msg, const char* file, int line)
{ {
char new_msg[MAX_MSG_LEN]; char new_msg[MAX_MSG_LEN];
sprintf(new_msg, "Expected %d, actual %d. %s", expected, actual, msg); sprintf(new_msg, "Expected %d, actual %d. %s", expected, actual, msg);
assert_true(expected == actual, tc, new_msg, file, line); assert_true(expected == actual, tc, new_msg, file, line);
} }
void assert_equals_f(double expected, double actual, double tolerance, struct TestCase* tc, void assert_equals_f(double expected, double actual, double tolerance, struct TestCase* tc,
const char* msg, const char* file, int line) const char* msg, const char* file, int line)
{ {
char new_msg[MAX_MSG_LEN]; char new_msg[MAX_MSG_LEN];
sprintf(new_msg, "Expected %f, actual %f. %s", expected, actual, msg); sprintf(new_msg, "Expected %f, actual %f. %s", expected, actual, msg);
double min_val = expected - tolerance; double min_val = expected - tolerance;
double max_val = expected + tolerance; double max_val = expected + tolerance;
assert_true(min_val <= actual && actual <= max_val, tc, new_msg, file, line); assert_true(min_val <= actual && actual <= max_val, tc, new_msg, file, line);
} }
int get_test_count() int get_test_count()
{ {
return tc_count; return tc_count;
} }
bool add_test(void (*test_function)(struct TestCase *tc), const char *test_name) bool add_test(void (*test_function)(struct TestCase *tc), const char *test_name)
{ {
if (tc_count == MAX_TEST_FUNCTIONS) { if (tc_count == MAX_TEST_FUNCTIONS) {
return false; return false;
} }
else { else {
test_cases[tc_count].success = true; test_cases[tc_count].success = true;
test_cases[tc_count].name = test_name; test_cases[tc_count].name = test_name;
test_cases[tc_count].test_function = test_function; test_cases[tc_count].test_function = test_function;
tc_count++; tc_count++;
return true; return true;
} }
} }
void run_tests() void run_tests()
{ {
int i; int i;
printf("\n%s: Running tests\n", version()); printf("\n%s: Running tests\n", version());
for (i = 0; i < get_test_count(); i++) { for (i = 0; i < get_test_count(); i++) {
printf("Running test %s ...", test_cases[i].name); printf("Running test %s ...", test_cases[i].name);
test_cases[i].test_function(&test_cases[i]); test_cases[i].test_function(&test_cases[i]);
if (test_cases[i].success) { if (test_cases[i].success) {
printf("\033[32m OK\033[m"); printf("\033[32m OK\033[m");
} }
else { else {
printf("\033[31m ... FAIL\033[m"); printf("\033[31m ... FAIL\033[m");
} }
printf("\n"); printf("\n");
} }
printf("\nTotal tests run: %d\n", tc_count); printf("\nTotal tests run: %d\n", tc_count);
if (tc_fail_count > 0) { if (tc_fail_count > 0) {
printf("\033[31mTests failed: %d\033[m\n", tc_fail_count); printf("\033[31mTests failed: %d\033[m\n", tc_fail_count);
} }
else { else {
printf("\033[32mAll tests run successfully\033[m\n"); printf("\033[32mAll tests run successfully\033[m\n");
} }
} }

View file

@ -1,110 +1,110 @@
/*---------------------------------------------------------- /*----------------------------------------------------------
* HTBLA-Leonding * HTBLA-Leonding
* --------------------------------------------------------- * ---------------------------------------------------------
* Title: shortcut * Title: shortcut
* Author: P. Bauer * Author: P. Bauer
* Date: November 03, 2010 * Date: November 03, 2010
* ---------------------------------------------------------- * ----------------------------------------------------------
* Description: * Description:
* A simple unit testing frame work for C. * A simple unit testing frame work for C.
* ---------------------------------------------------------- * ----------------------------------------------------------
*/ */
#ifndef ___SHORTCUT_H #ifndef ___SHORTCUT_H
#define ___SHORTCUT_H #define ___SHORTCUT_H
#include <stdbool.h> #include <stdbool.h>
/** TestCase is the struct to define one test case. A test case can /** TestCase is the struct to define one test case. A test case can
*** be added to a test. If the test is run all added test cases are *** be added to a test. If the test is run all added test cases are
*** run and the result of the run of each test case is checked automatically. *** run and the result of the run of each test case is checked automatically.
*/ */
struct TestCase { struct TestCase {
const char *name; const char *name;
/** true if the test passed, false otherwise. */ /** true if the test passed, false otherwise. */
bool success; bool success;
/** The test function which is executed by the test framework. */ /** The test function which is executed by the test framework. */
void (*test_function)(struct TestCase *tc); void (*test_function)(struct TestCase *tc);
}; };
/** /**
*** @return Version of shortcut as string *** @return Version of shortcut as string
***/ ***/
const char* version(); const char* version();
/** /**
*** @return The fromated string as generated using sprintf(format, ...) *** @return The fromated string as generated using sprintf(format, ...)
***/ ***/
char* format_msg(char* format, ...); char* format_msg(char* format, ...);
/** assert_true checks, whether a boolean expression passed is true or false. /** assert_true checks, whether a boolean expression passed is true or false.
*** in case it is false the test case stating the assertion is marked *** in case it is false the test case stating the assertion is marked
*** as failed and msg is printed. *** as failed and msg is printed.
*** @param bool_expr Expression which is evaluated. *** @param bool_expr Expression which is evaluated.
*** @param tc Pointer to the test case which states this assertion. *** @param tc Pointer to the test case which states this assertion.
*** @param msg Message to be printed if assertion evaluates to false. *** @param msg Message to be printed if assertion evaluates to false.
*** @param file File in which the assert is given. *** @param file File in which the assert is given.
*** @param line Line in which the assert is given. *** @param line Line in which the assert is given.
*/ */
void assert_true(bool bool_expr, struct TestCase *tc, const char *msg, void assert_true(bool bool_expr, struct TestCase *tc, const char *msg,
const char* file, int line); const char* file, int line);
/** assert_false does the same as assert() but the boolean expression /** assert_false does the same as assert() but the boolean expression
*** has to evaluate to false. If it evaluates to true the assertion *** has to evaluate to false. If it evaluates to true the assertion
*** fails. *** fails.
*** @see assert *** @see assert
*/ */
void assert_false(bool bool_expr, struct TestCase* tc, const char* msg, void assert_false(bool bool_expr, struct TestCase* tc, const char* msg,
const char* file, int line); const char* file, int line);
/** assert_equals checks whether two values are equal. Currently the following /** assert_equals checks whether two values are equal. Currently the following
*** data formats are supported: *** data formats are supported:
*** - strings *** - strings
*** - integer *** - integer
*** @param expected The expected string value *** @param expected The expected string value
*** @param actual The actual string value *** @param actual The actual string value
*** @param tc Pointer to the test case which states this assertion. *** @param tc Pointer to the test case which states this assertion.
*** @param msg Message to be printed if assertion evaluates to false. *** @param msg Message to be printed if assertion evaluates to false.
*** @param file File in which the assert is given. *** @param file File in which the assert is given.
*** @param line Line in which the assert is given. *** @param line Line in which the assert is given.
*** @see assert *** @see assert
*/ */
void assert_equals(int expected, int actual, struct TestCase* tc, void assert_equals(int expected, int actual, struct TestCase* tc,
const char* msg, const char* file, int line); const char* msg, const char* file, int line);
void assert_equals_str(const char* expected, char* actual, struct TestCase* tc, void assert_equals_str(const char* expected, char* actual, struct TestCase* tc,
const char* msg, const char* file, int line); const char* msg, const char* file, int line);
void assert_equals_f(double expected, double actual, double tolerance, struct TestCase* tc, void assert_equals_f(double expected, double actual, double tolerance, struct TestCase* tc,
const char* msg, const char* file, int line); const char* msg, const char* file, int line);
/** @return The total number of test cases added to the test. /** @return The total number of test cases added to the test.
*/ */
int get_test_count(); int get_test_count();
/** add_test creates a new test case and adds the a test function to /** add_test creates a new test case and adds the a test function to
*** this test case. *** this test case.
*** @param test_function Pointer to the test function to be added *** @param test_function Pointer to the test function to be added
*** to the newly created test case. *** to the newly created test case.
*** @param test_case_name Name which should be assigned to the newly *** @param test_case_name Name which should be assigned to the newly
*** created test case. *** created test case.
*/ */
bool add_test(void (*test_function)(struct TestCase *tc), const char *test_case_name); bool add_test(void (*test_function)(struct TestCase *tc), const char *test_case_name);
void run_tests(); void run_tests();
#define TEST(testname) void testname(struct TestCase *tc) #define TEST(testname) void testname(struct TestCase *tc)
#define MSG(format, ...) format_msg(format, ##__VA_ARGS__) #define MSG(format, ...) format_msg(format, ##__VA_ARGS__)
#define ASSERT_TRUE(condition, msg) assert_true(condition, tc, msg, __FILE__, __LINE__) #define ASSERT_TRUE(condition, msg) assert_true(condition, tc, msg, __FILE__, __LINE__)
#define ASSERT_FALSE(condition, msg) assert_false(condition, tc, msg, __FILE__, __LINE__) #define ASSERT_FALSE(condition, msg) assert_false(condition, tc, msg, __FILE__, __LINE__)
#define ASSERT_EQUALS(expected, actual) assert_equals(expected, actual, tc, "", __FILE__, __LINE__) #define ASSERT_EQUALS(expected, actual) assert_equals(expected, actual, tc, "", __FILE__, __LINE__)
#define ASSERT_EQUALS_STR(expected, actual) assert_equals_str(expected, actual, tc, "", __FILE__, __LINE__) #define ASSERT_EQUALS_STR(expected, actual) assert_equals_str(expected, actual, tc, "", __FILE__, __LINE__)
#define ASSERT_EQUALS_TOLERANCE(expected, actual, tolerance) assert_equals_f(expected, actual, tolerance, tc, "", __FILE__, __LINE__) #define ASSERT_EQUALS_TOLERANCE(expected, actual, tolerance) assert_equals_f(expected, actual, tolerance, tc, "", __FILE__, __LINE__)
#define ADD_TEST(testfunction) add_test(testfunction, #testfunction) #define ADD_TEST(testfunction) add_test(testfunction, #testfunction)
#endif #endif

View file

@ -1,26 +1,26 @@
/*---------------------------------------------------------- /*----------------------------------------------------------
* HTBLA-Leonding * HTBLA-Leonding
* --------------------------------------------------------- * ---------------------------------------------------------
* Author: S. Schraml * Author: S. Schraml
* ---------------------------------------------------------- * ----------------------------------------------------------
* Description: * Description:
* Executes all unit tests of Roman Number. * Executes all unit tests of Roman Number.
* ---------------------------------------------------------- * ----------------------------------------------------------
*/ */
#include "shortcut.h" #include "shortcut.h"
#include "test_roman_number.h" #include "test_roman_number.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
ADD_TEST(test_create_with_valid_number); ADD_TEST(test_create_with_valid_number);
ADD_TEST(test_create_with_invalid_number); ADD_TEST(test_create_with_invalid_number);
ADD_TEST(test_create_with_null_string); ADD_TEST(test_create_with_null_string);
ADD_TEST(test_get_valid_value); ADD_TEST(test_get_valid_value);
ADD_TEST(test_get_value_from_invalid_number); ADD_TEST(test_get_value_from_invalid_number);
ADD_TEST(test_calc_roman_gcd); ADD_TEST(test_calc_roman_gcd);
ADD_TEST(test_int_gdc); ADD_TEST(test_int_gdc);
run_tests(); run_tests();
return 0; return 0;
} }

View file

@ -1,89 +1,89 @@
/*---------------------------------------------------------- /*----------------------------------------------------------
* HTBLA-Leonding * HTBLA-Leonding
* --------------------------------------------------------- * ---------------------------------------------------------
* Author: S. Schraml * Author: S. Schraml
* ---------------------------------------------------------- * ----------------------------------------------------------
* Description: * Description:
* Test functions for Roman Number. * Test functions for Roman Number.
* ---------------------------------------------------------- * ----------------------------------------------------------
*/ */
#include "test_roman_number.h" #include "test_roman_number.h"
#include "roman_number.h" #include "roman_number.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "shortcut.h" #include "shortcut.h"
char* number_strs[] = {"MDCCCCLXXXIIII", "CMXLIXIV", "V", "", "CXI", "CCLIX"}; char* number_strs[] = {"MDCCCCLXXXIIII", "CMXLIXIV", "V", "", "CXI", "CCLIX"};
int number_vals[] = {1984, 953, 5, 0, 111, 259}; int number_vals[] = {1984, 953, 5, 0, 111, 259};
int number_cnt = sizeof(number_vals) / sizeof(number_vals[0]); int number_cnt = sizeof(number_vals) / sizeof(number_vals[0]);
struct Tuple { struct Tuple {
int x; int x;
int y; int y;
int exp_res; int exp_res;
}; };
static struct Tuple gdc_values[] = { static struct Tuple gdc_values[] = {
{111, 259, 37}, {111, 259, 37},
{ 12, 9, 3}, { 12, 9, 3},
{ 23, 92, 23}, { 23, 92, 23},
{527, 899, 31}, {527, 899, 31},
{ 42, 42, 42}, { 42, 42, 42},
{ 83, 97, 1}, { 83, 97, 1},
}; };
static int gdc_value_cnt = sizeof(gdc_values) / sizeof(gdc_values[0]); static int gdc_value_cnt = sizeof(gdc_values) / sizeof(gdc_values[0]);
TEST(test_create_with_valid_number) { TEST(test_create_with_valid_number) {
for(int i = 0; i < number_cnt; i++) { for(int i = 0; i < number_cnt; i++) {
RomanNumber result = rn_create(number_strs[i]); RomanNumber result = rn_create(number_strs[i]);
ASSERT_TRUE(rn_is_valid(result), MSG("Expected that string '%s' is be a valid number", number_strs[i])); ASSERT_TRUE(rn_is_valid(result), MSG("Expected that string '%s' is be a valid number", number_strs[i]));
} }
} }
TEST(test_create_with_invalid_number) { TEST(test_create_with_invalid_number) {
char* number = "MCDR"; char* number = "MCDR";
RomanNumber result = rn_create(number); RomanNumber result = rn_create(number);
ASSERT_TRUE(!rn_is_valid(result), MSG("Expected that string '%s' is not be a valid number", number)); ASSERT_TRUE(!rn_is_valid(result), MSG("Expected that string '%s' is not be a valid number", number));
} }
TEST(test_create_with_null_string) { TEST(test_create_with_null_string) {
RomanNumber result = rn_create(0); RomanNumber result = rn_create(0);
ASSERT_TRUE(!rn_is_valid(result), MSG("Expected that NULL (%d) string is not a valid number", 0)); ASSERT_TRUE(!rn_is_valid(result), MSG("Expected that NULL (%d) string is not a valid number", 0));
} }
TEST(test_get_valid_value) { TEST(test_get_valid_value) {
for(int i = 0; i < number_cnt; i++) { for(int i = 0; i < number_cnt; i++) {
RomanNumber result = rn_create(number_strs[i]); RomanNumber result = rn_create(number_strs[i]);
int value = rn_get_value(result); int value = rn_get_value(result);
ASSERT_TRUE(value == number_vals[i], MSG("Expected that string '%s' is converted to %d but retrieved %d", number_strs[i], number_vals[i], value)); ASSERT_TRUE(value == number_vals[i], MSG("Expected that string '%s' is converted to %d but retrieved %d", number_strs[i], number_vals[i], value));
} }
} }
TEST(test_get_value_from_invalid_number) { TEST(test_get_value_from_invalid_number) {
char* number = "MCDR"; char* number = "MCDR";
RomanNumber result = rn_create(number); RomanNumber result = rn_create(number);
int value = rn_get_value(result); int value = rn_get_value(result);
ASSERT_TRUE(value < 0, MSG("Expected that value of string '%s' is less than 0, but got %d", number, value)); ASSERT_TRUE(value < 0, MSG("Expected that value of string '%s' is less than 0, but got %d", number, value));
} }
TEST(test_calc_roman_gcd) { TEST(test_calc_roman_gcd) {
int exp_res = 37; int exp_res = 37;
RomanNumber x = rn_create(number_strs[4]); RomanNumber x = rn_create(number_strs[4]);
RomanNumber y = rn_create(number_strs[5]); RomanNumber y = rn_create(number_strs[5]);
RomanNumber result = rn_gcd(x, y); RomanNumber result = rn_gcd(x, y);
ASSERT_TRUE(rn_is_valid(result), MSG("Expected that result (%d) of GCD is a valid roman number", exp_res)); ASSERT_TRUE(rn_is_valid(result), MSG("Expected that result (%d) of GCD is a valid roman number", exp_res));
int value = rn_get_value(result); int value = rn_get_value(result);
ASSERT_TRUE(value == exp_res, MSG("Expected that result of GCD for strings '%s' and '%s' is '%d', but got '%d'", number_strs[4], number_strs[5], exp_res, value)); ASSERT_TRUE(value == exp_res, MSG("Expected that result of GCD for strings '%s' and '%s' is '%d', but got '%d'", number_strs[4], number_strs[5], exp_res, value));
} }
TEST(test_int_gdc) { TEST(test_int_gdc) {
for(int i = 0; i < gdc_value_cnt; i++) { for(int i = 0; i < gdc_value_cnt; i++) {
int act_res = int_gcd(gdc_values[i].x, gdc_values[i].y); int act_res = int_gcd(gdc_values[i].x, gdc_values[i].y);
ASSERT_TRUE(act_res == gdc_values[i].exp_res, MSG("Expected that GDC of %d and %d is %d, but got %d", ASSERT_TRUE(act_res == gdc_values[i].exp_res, MSG("Expected that GDC of %d and %d is %d, but got %d",
gdc_values[i].x, gdc_values[i].y, gdc_values[i].exp_res, act_res)); gdc_values[i].x, gdc_values[i].y, gdc_values[i].exp_res, act_res));
} }
} }

View file

@ -1,23 +1,23 @@
/*---------------------------------------------------------- /*----------------------------------------------------------
* HTBLA-Leonding * HTBLA-Leonding
* --------------------------------------------------------- * ---------------------------------------------------------
* Author: S. Schraml * Author: S. Schraml
* ---------------------------------------------------------- * ----------------------------------------------------------
* Description: * Description:
* Test functions for Roman Number. * Test functions for Roman Number.
* ---------------------------------------------------------- * ----------------------------------------------------------
*/ */
#ifndef ___TEST_ROMAN_NUMBER_H #ifndef ___TEST_ROMAN_NUMBER_H
#define ___TEST_ROMAN_NUMBER_H #define ___TEST_ROMAN_NUMBER_H
#include "shortcut.h" #include "shortcut.h"
TEST(test_create_with_valid_number); TEST(test_create_with_valid_number);
TEST(test_create_with_invalid_number); TEST(test_create_with_invalid_number);
TEST(test_create_with_null_string); TEST(test_create_with_null_string);
TEST(test_get_valid_value); TEST(test_get_valid_value);
TEST(test_get_value_from_invalid_number); TEST(test_get_value_from_invalid_number);
TEST(test_calc_roman_gcd); TEST(test_calc_roman_gcd);
TEST(test_int_gdc); TEST(test_int_gdc);
#endif #endif