diff --git a/Assignment_009/files/bild.bmp.rle b/Assignment_009/files/bild.bmp.rle new file mode 100644 index 0000000..6e6ee16 Binary files /dev/null and b/Assignment_009/files/bild.bmp.rle differ diff --git a/Assignment_009/img/disk_full.jpg b/Assignment_009/img/disk_full.jpg new file mode 100644 index 0000000..b5d5e6b Binary files /dev/null and b/Assignment_009/img/disk_full.jpg differ diff --git a/Assignment_009/readme.md b/Assignment_009/readme.md index 0b7dc8d..6424735 100644 --- a/Assignment_009/readme.md +++ b/Assignment_009/readme.md @@ -1,32 +1,133 @@ -# Eine Menge Mengen, bloß nicht vermengen +# Mist die Festplatte ist schon wieder voll **🎓 Benotetes Assignment 🎓** -📆 **Fällig: 16.12.2025** 📆 +📆 **Fällig: 02.12.2025** 📆 -Die Platte ist komprimiert, Battlefield 6 installiert und Sie zocken vor lauter Glück über Ihr tolles Kompressionsprogramm mehrere Nächte durch. Das Studium leidet, aber im Gegenzug steigen Sie immer weiter in den Rängen des Spiels auf. Wer braucht schon einen Bachelor of Science, wenn man sich durch 100 Ranks leveln kann. +Nachdem Sie den Reptiloiden "Never gonna give you up" vorgesungen haben und ein Bild von Rick Astley entgegen gehalten, sind diese wie zu Stein erstarrt und Sie können aus der Höhle flüchten. Hinter sich hören Sie noch das Grunzen und Schreien der Echsenwesen und glauben auch die Stimme von Mark Zuckerberg zu vernehmen. Nie wieder Facebook, Instagram und WhatsApp – das war einfach zu viel. -Plötzlich passiert es, Sie werden vom Sekundenschlaf erwischt und schlafen mit dem Kopf auf der Tastatur ein. Es war wohl doch zu viele die letzten Tage. Ihre Nase produziert wilde Zeichen im Discord-Chat und die anderen Spieler beschimpfen Sie auf Russisch. Aber Sie bekommen nichts mehr davon mit, so süß sind Ihre Träume. +Geschafft! Sie sind an der Erdoberfläche angekommen und schlagen sich nach Hause durch. Die Reise dauert durchaus eine Weile, auch wegen der ständig verspäteten Züge, und als Sie ankommen, brauchen Sie erst einmal etwas Entspannung. -In Ihrem Traum werden Sie ins Wunderland verschleppt und der verrückte Hutmacher will, dass Sie ein Programm für ihn schreiben. Er wäre nicht der verrückte Hutmacher, wenn dieses Programm nicht sehr spezifische Anforderungen hätte. Oder ist es vielleicht gar nicht der Hutmacher, der Sie da quält? +Was kann mehr entspannen als ... eine Runde Battlefield 6. -## MySet +![](img/disk_full.jpg) -Gehen Sie in das Paket [pr2.generics.set](../sources/src/main/java/pr2/generics/set/). +Doch leider ist die Festplatte voll und der 67 GB Download passt nicht mehr drauf. Sie müssen dringend Platz schaffen! Also suchen Sie nach Möglichkeiten, die Daten auf Ihrer Festplatte, die eigentlich eine SSD ist, zu komprimieren. -Sie sollen das Interface `Set` implementieren, das die Operationen auf einer **Menge** definiert. Das Interface `Set` ist aber noch nicht generisch, d.h. bevor Sie die Implementierung beginnen, müssen Sie das Interface zu einem `Set` umbauen, sodass man typsicher damit umgehen kann. +Da kommt Ihnen die nächste PR2-Aufgabe gerade gelegen. -Ausgehend vom angepassten Interface `Set` implementieren Sie die Methoden in der Klasse `MySet`. +## CompressingOutputStream -Für die interne Speicherung der Daten verwenden Sie bitte die vorgegebene Klasse `Bag`, um sich die Arbeit zu erleichtern und nicht mit Arrays arbeiten zu müssen. +Gehen Sie in das Paket [pr2.io.compressor](../sources/src/main/java/pr2/io/compressor/). -Bitte beachten Sie die folgenden Regeln für die Implementierung: +Implementieren Sie eine Klasse namens `CompressingOutputStream`, die von `FilterOutputStream` abgeleitet ist. Diese Klasse soll eine Komprimierung der Daten durchführen, wobei ein sogenanntes _run length encoding_ verwendet werden soll. Immer wenn ein Byte zwei- oder mehrmals hintereinander vorkommt, soll statt der wiederholten Bytes, die Sequenz `-1`, das Byte und die Anzahl der Wiederholungen geschrieben werden. So wird z.B. aus der Bytefolge `{ 5, 5, 5, 5, 5, 5, 5, 5, 5 }` durch die Kompression die Folge `{ -1, 5, 9 }`. Wiederholt sich ein Byte nicht, wird es einfach direkt übernommen. Es sind maximal 126 Wiederholungen möglich. Kommt ein Byte häufiger als 127-mal hintereinander vor, werden zuerst die ersten 127 Byte komprimiert und dann die nächsten Bytes bearbeitet. (Die Bytewerte sind hier als Dezimalzahlen angegeben, `-1` würde dann in hexadezimaler Darstellung `0xff` entsprechen.) - * Achten Sie bei den Methoden von `Set` darauf, dass diese **möglichst flexibel** mit den Typen umgehen, d.h. verwenden Sie, wo immer möglich, Wildcards anstatt des Typparameters `T`. Denken Sie an die Verwendung von `extends` und `super` im Zusammenhang mit Wildcards `?`. - * Verwenden Sie **keine Klassen aus dem Collection API**, d.h. Sie dürfen nicht auf `java.util.Set`, `java.util.List` etc. zurückgreifen. Ihre darunterliegende Speicherung wird von `Bag` übernommen. - * Implementieren Sie **zuerst die beiden Methoden** `each` und `test`. - * Außerhalb der Methoden `each` und `test` darf es **keine Schleifen (`for`, `while`)** in Ihrer Klasse geben. Sie müssen alle Operationen, welche eine Iteration (Schleife) brauchen, auf diese beiden Methoden abbilden. Hierzu verwenden Sie bitte geschickt Lambdas, die Sie (wo sinnvoll) an die `each`- und `test`-Methode übergeben und darin dann die notwendigen Operationen durchführen. +Beachten Sie den Spezialfall, dass die Eingabedaten eine `-1` (`0xff`) enthalten, in diesem Fall müssen Sie die Escapesequenz `{ -1, -1 }` schreiben, um dies bei der Dekompression erkennen zu können. So wird z.B. aus der Eingabe `{ 3, -1, 5 }` komprimiert `{ 3, -1, -1, 5 }`. -## UnitTests +Testen Sie Ihre Implementierung ausführlich mit Unit-Tests. Sie können folgende Bytefolgen als Startpunkt nehmen, sie sind aber auf keinen Fall ausreichend. -Schreiben Sie JUnit-Tests in der Klasse [pr2.generics.set.TestMySet](../../solutions/src/test/java/pr2/generic/set/TestMySet), welche Ihre Implementierung ausgiebig testen. Hierbei sollten alle Methoden getestet werden, die das Interface vorgibt. \ No newline at end of file +| Input | Komprimiert | +|-----------------------------|-----------------------------| +| `0, 1, 2, 3` | `0, 1, 2, 3` | +| `-1, -1, -1` | `-1, -1, -1, -1, -1, -1` | +| `-1, 1, 2, 3` | `-1, -1, 1, 2, 3` | +| `0, 1, 1, 1, 1, 1, 1` | `0, -1, 1, 6` | +| `5, 5, 5, 5, 5, 5, 5, 5, 5` | `-1, 5, 9` | +| `0, 1, 1, 3, 3, 3, 3` | `0, -1, 1, 2, -1, 3, 4` | +| `1, 1, 3, 3, 3, 3` | `-1, 1, 2, -1, 3, 4` | +| `1, 2, 1, 3, 3, 3` | `1, 2, 1, -1, 3, 3` | +| `1, 1, -1, 1, 2, 3` | `-1, 1, 2, -1, -1, 1, 2, 3` | +| `1, 1, -1` | `-1, 1, 2, -1, -1` | +| `-1` | `-1, -1` | +| `-1, 1, 1` | `-1, -1, -1, 1, 2` | +| `1, 1, 1, 1, 1, 1, 1, 1, 1` | `-1, 1, 9` | +| `1, 1, 1, 1, -1` | `-1, 1, 4, -1, -1` | + + +## DecompressingInputStream + +Implementieren Sie eine Klasse namens `DecompressingInputStream`, die von `InputStream` abgeleitet ist. Diese Klasse soll eine Dekomprimierung der Daten durchführen, die vorher mit einem `CompressingOutputStream` komprimiert wurden. + +Testen Sie Ihre Implementierung ausführlich mit Unit-Tests. Sie können die in der obigen Tabelle dargestellten Bytefolgen als Startpunkt nehmen, sollten aber noch mehr Fälle testen. + +## FileCompressor + +Implementieren Sie eine Klasse namens `FileCompressor`. Diese nimmt von der Kommandozeile zwei Argumente an, die zwei Dateien bezeichnen. Die zuerst angegebene Datei wird mithilfe eines `CompressingOutputStream` komprimiert und das Ergebnis wird in die als zweite Option angegebene Datei geschrieben. Denken Sie an eine sinnvolle Fehlerbehandlung. + +Alternativ kann man die Klasse auch ohne Optionen aufrufen. In diesem Fall werden die Daten von der Standard-Eingabe gelesen und das komprimierte Ergebnis auf die Standard-Ausgabe geschrieben. + +Diese letzte Anforderung bedeutet, dass man z.B. das folgende machen kann: + +```console +$ echo "Duuuuuuuuuuuuduuuuuuuuuu" | \ + java -cp . pr2.io.compressor.tool.FileCompressor > \ + result.rle +``` + + +## FileDecompressor + +Implementieren Sie eine Klasse namens `FileDecompressor`. Diese nimmt von der Kommandozeile zwei Argumente an, die zwei Dateien bezeichnen. Die zuerst angegebene Datei wird mithilfe eines `DecompressingInputStreams` dekomprimiert und das Ergebnis wird in die als zweite Option angegebene Datei geschrieben. Denken Sie an eine sinnvolle Fehlerbehandlung. + +Alternativ kann man die Klasse auch ohne Optionen aufrufen. In diesem Fall werden die Daten von der Standard-Eingabe gelesen und das dekomprimierte Ergebnis auf die Standard-Ausgabe geschrieben. + +Diese letzte Anforderung bedeutet, dass man z.B. das folgende machen kann: + +```console +$ echo "Duuuuuuuuuuuuduuuuuuuuuu" | \ + java -cp . pr2.io.compressor.tool.FileCompressor | \ + java -cp . pr2.io.compressor.tool.FileDecompressor +Duuuuuuuuuuuuduuuuuuuuuu +``` + +## DirectoryCompressor + +Schreiben Sie eine Klasse namens `DirectoryCompressor`. Diese nimmt von der Kommandozeile den Namen eines Verzeichnisses entgegen und komprimiert alle darin gefundenen Dateien mit dem oben beschriebenen `FileCompressor`. Die komprimierten Dateien werden mit der Dateierweiterung `.rle` versehen, z.B. wird aus der Datei `MyFile.txt` die Datei `MyFile.txt.rle`. Die Originaldateien werden nicht verändert. + +Beachten Sie die folgenden möglichen Fehlerfälle: + + * Der Benutzer vergisst, einen Pfad anzugeben. + * Das angegebene Verzeichnis existiert nicht. + * Der angegebene Pfad ist gar kein Verzeichnis. + * Das Verzeichnis existiert, darf aber nicht gelesen werden. + * Das Verzeichnis existiert, darf aber nicht geschrieben werden. + +Darüber hinaus können noch andere Fehler auftreten, die Sie bitte entsprechend abfangen und behandeln sollen. + +Das Programm soll den Benutzer über die bereits komprimierten Dateien informieren. Hierzu soll die Ausgabe ungefähr wie folgt aussehen: + +```console +Komprimiere Dateien in /Users/thomas/Temp/compressme + +f1.txt (342 Bytes) -> f1.txt.rle (23 Bytes) [6%] +f2.txt (1368 Bytes) -> f2.txt.rle (83 Bytes) [6%] +f3.txt (13680 Bytes) -> f3.txt.rle (803 Bytes) [5%] +``` + +Für jede komprimierte Datei wird eine neue Zeile ausgegeben. + +## DirectoryDecompressor + +Implementieren Sie eine Java-Klasse namens `DirectoryDecompressor`, die die Umkehroperation zum `DirectoryCompressor` zur Verfügung stellt. Man gibt ihr über die Kommandozeile einen Verzeichnisnamen und sie dekomprimiert alle darin gefundenen Dateien, die die Endung `.rle` haben. + +Die komprimierten Dateien werden nach einer erfolgreichen Dekompression gelöscht. + +Beachten Sie, dass auch hier unterschiedliche Fehler auftreten könne, die Sie bitte entsprechend abfangen und behandeln sollten. + +Das Programm soll den Benutzer über seine Fortschritte informieren. Hierzu soll die Ausgabe ungefähr wie folgt aussehen: + +```console +Dekomprimiere Dateien in /Users/thomas/Temp/compressme2 + +f1.txt.rle (23 Bytes) -> f1.txt (342 Bytes) +f2.txt.rle (83 Bytes) -> f2.txt (1368 Bytes) +f3.txt.rle (803 Bytes) -> f3.txt (13680 Bytes) +``` + +## Geheime Datei + +Wenn Sie alles fertig implementiert haben, wenden Sie Ihre Software auf die Datei [bild.bmp.rle](files/bild.bmp.rle) an und dekomprimieren Sie sie. + + * Was ist zu sehen? + * Um welchen Faktor wurde die Datei komprimiert? + * Warum ist der Kompressionsfaktor hier so hoch? \ No newline at end of file diff --git a/Assignment_010/readme.md b/Assignment_010/readme.md new file mode 100644 index 0000000..0b7dc8d --- /dev/null +++ b/Assignment_010/readme.md @@ -0,0 +1,32 @@ +# Eine Menge Mengen, bloß nicht vermengen + +**🎓 Benotetes Assignment 🎓** + +📆 **Fällig: 16.12.2025** 📆 + +Die Platte ist komprimiert, Battlefield 6 installiert und Sie zocken vor lauter Glück über Ihr tolles Kompressionsprogramm mehrere Nächte durch. Das Studium leidet, aber im Gegenzug steigen Sie immer weiter in den Rängen des Spiels auf. Wer braucht schon einen Bachelor of Science, wenn man sich durch 100 Ranks leveln kann. + +Plötzlich passiert es, Sie werden vom Sekundenschlaf erwischt und schlafen mit dem Kopf auf der Tastatur ein. Es war wohl doch zu viele die letzten Tage. Ihre Nase produziert wilde Zeichen im Discord-Chat und die anderen Spieler beschimpfen Sie auf Russisch. Aber Sie bekommen nichts mehr davon mit, so süß sind Ihre Träume. + +In Ihrem Traum werden Sie ins Wunderland verschleppt und der verrückte Hutmacher will, dass Sie ein Programm für ihn schreiben. Er wäre nicht der verrückte Hutmacher, wenn dieses Programm nicht sehr spezifische Anforderungen hätte. Oder ist es vielleicht gar nicht der Hutmacher, der Sie da quält? + +## MySet + +Gehen Sie in das Paket [pr2.generics.set](../sources/src/main/java/pr2/generics/set/). + +Sie sollen das Interface `Set` implementieren, das die Operationen auf einer **Menge** definiert. Das Interface `Set` ist aber noch nicht generisch, d.h. bevor Sie die Implementierung beginnen, müssen Sie das Interface zu einem `Set` umbauen, sodass man typsicher damit umgehen kann. + +Ausgehend vom angepassten Interface `Set` implementieren Sie die Methoden in der Klasse `MySet`. + +Für die interne Speicherung der Daten verwenden Sie bitte die vorgegebene Klasse `Bag`, um sich die Arbeit zu erleichtern und nicht mit Arrays arbeiten zu müssen. + +Bitte beachten Sie die folgenden Regeln für die Implementierung: + + * Achten Sie bei den Methoden von `Set` darauf, dass diese **möglichst flexibel** mit den Typen umgehen, d.h. verwenden Sie, wo immer möglich, Wildcards anstatt des Typparameters `T`. Denken Sie an die Verwendung von `extends` und `super` im Zusammenhang mit Wildcards `?`. + * Verwenden Sie **keine Klassen aus dem Collection API**, d.h. Sie dürfen nicht auf `java.util.Set`, `java.util.List` etc. zurückgreifen. Ihre darunterliegende Speicherung wird von `Bag` übernommen. + * Implementieren Sie **zuerst die beiden Methoden** `each` und `test`. + * Außerhalb der Methoden `each` und `test` darf es **keine Schleifen (`for`, `while`)** in Ihrer Klasse geben. Sie müssen alle Operationen, welche eine Iteration (Schleife) brauchen, auf diese beiden Methoden abbilden. Hierzu verwenden Sie bitte geschickt Lambdas, die Sie (wo sinnvoll) an die `each`- und `test`-Methode übergeben und darin dann die notwendigen Operationen durchführen. + +## UnitTests + +Schreiben Sie JUnit-Tests in der Klasse [pr2.generics.set.TestMySet](../../solutions/src/test/java/pr2/generic/set/TestMySet), welche Ihre Implementierung ausgiebig testen. Hierbei sollten alle Methoden getestet werden, die das Interface vorgibt. \ No newline at end of file diff --git a/readme.md b/readme.md index e84209a..dea0b0c 100644 --- a/readme.md +++ b/readme.md @@ -173,7 +173,8 @@ Wichtige Einstellungen für Eclipse sind [hier](help/eclipse.md) beschrieben. | 6. | 22.10.2025 | [Racewars](Assignment_006/readme.md) | **04.11.2025** | | 7. | 05.11.2025 | [Ein Traum in Zucker oder kann ich dem Zufallsgenerator trauen?](Assignment_007/readme.md) | **18.11.2025** | | 8. | 05.11.2025 | [Live-Testat](Assignment_008/readme.md) | **19.11.2025** | -| 9. | 03.12.2025 | [Eine Menge Mengen, bloß nicht vermengen](Assignment_009/readme.md) | **16.12.2025** | +| 9. | 19.11.2025 | [Mist die Festplatte ist schon wieder voll](Assignment_009/readme.md) | **02.12.2025** | +| 10. | 03.12.2025 | [Eine Menge Mengen, bloß nicht vermengen](Assignment_010/readme.md) | **16.12.2025** | ## 😀 Freiwillige Übungen Die freiwilligen Übungen zur Vertiefung der Vorlesungsinhalte finden Sie in einem getrennten Repository: [Freiwillige Übungen](/pr2-lecture/uebungen/src/branch/master/readme.md). diff --git a/sources/src/main/java/pr2/generics/set/Set.java b/sources/src/main/java/pr2/generics/set/Set.java index dd3dc03..5ed1349 100644 --- a/sources/src/main/java/pr2/generics/set/Set.java +++ b/sources/src/main/java/pr2/generics/set/Set.java @@ -18,6 +18,15 @@ public interface Set { */ void add(Object obj); + /** + * Testet das gegebene Predikat gegen alle Elemente und gibt + * {@} + * + * @param p + * @return + */ + void add(Object obj); + /** * Entfernt das Objekt aus der Menge. * diff --git a/sources/src/main/java/pr2/io/compressor/CompressingOutputStream.java b/sources/src/main/java/pr2/io/compressor/CompressingOutputStream.java new file mode 100644 index 0000000..e685458 --- /dev/null +++ b/sources/src/main/java/pr2/io/compressor/CompressingOutputStream.java @@ -0,0 +1,14 @@ +package pr2.io.compressor; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Stream implementation that performs an automatic run-length + * encoding of the written data. + */ +public class CompressingOutputStream { + + // TODO: Klasse implementieren +} diff --git a/sources/src/main/java/pr2/io/compressor/DecompressingInputStream.java b/sources/src/main/java/pr2/io/compressor/DecompressingInputStream.java new file mode 100644 index 0000000..637e0d9 --- /dev/null +++ b/sources/src/main/java/pr2/io/compressor/DecompressingInputStream.java @@ -0,0 +1,13 @@ +package pr2.io.compressor; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Stream that automatically decompresses data that was compressed + * with the {@link CompressingOutputStream}. + */ +public class DecompressingInputStream { + + // TODO: Klasse implementieren +} diff --git a/sources/src/main/java/pr2/io/compressor/tool/DirectoryCompressor.java b/sources/src/main/java/pr2/io/compressor/tool/DirectoryCompressor.java new file mode 100644 index 0000000..d5d43de --- /dev/null +++ b/sources/src/main/java/pr2/io/compressor/tool/DirectoryCompressor.java @@ -0,0 +1,20 @@ +package pr2.io.compressor.tool; + +import java.io.File; +import java.io.FileFilter; + +/** + * Compresses the content of a directory. + */ +public class DirectoryCompressor extends DirectoryToolBase { + + public static void main(String[] args) throws Exception { + + if (args.length != 1) { + System.err.println("Bitte geben Sie ein Verzeichnis an."); + System.exit(1); + } + + // TODO: Methode implementieren + } +} diff --git a/sources/src/main/java/pr2/io/compressor/tool/DirectoryDecompressor.java b/sources/src/main/java/pr2/io/compressor/tool/DirectoryDecompressor.java new file mode 100644 index 0000000..0d1fccb --- /dev/null +++ b/sources/src/main/java/pr2/io/compressor/tool/DirectoryDecompressor.java @@ -0,0 +1,20 @@ +package pr2.io.compressor.tool; + +import java.io.File; +import java.io.FileFilter; + +/** + * Decompresses the content of a directory. + */ +public class DirectoryDecompressor extends DirectoryToolBase { + + public static void main(String[] args) throws Exception { + + if (args.length != 1) { + System.err.println("Bitte geben Sie ein Verzeichnis an."); + System.exit(1); + } + + // TODO: Methode implementieren + } +} diff --git a/sources/src/main/java/pr2/io/compressor/tool/DirectoryToolBase.java b/sources/src/main/java/pr2/io/compressor/tool/DirectoryToolBase.java new file mode 100644 index 0000000..84b31c0 --- /dev/null +++ b/sources/src/main/java/pr2/io/compressor/tool/DirectoryToolBase.java @@ -0,0 +1,11 @@ +package pr2.io.compressor.tool; + +import java.io.File; + +/** + * Base class for all directory based tools. + */ +public class DirectoryToolBase { + + // TODO: Gemeinsame Methoden für DirectoryCompressor und DirectoryDecompressor +} diff --git a/sources/src/main/java/pr2/io/compressor/tool/FileCompressor.java b/sources/src/main/java/pr2/io/compressor/tool/FileCompressor.java new file mode 100644 index 0000000..f1671f9 --- /dev/null +++ b/sources/src/main/java/pr2/io/compressor/tool/FileCompressor.java @@ -0,0 +1,19 @@ +package pr2.io.compressor.tool; + +import pr2.io.compressor.CompressingOutputStream; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Compresses a single file or stdin. + */ +public class FileCompressor { + + public static void main(String... args) throws Exception { + + // TODO: Methode implementieren + } +} diff --git a/sources/src/main/java/pr2/io/compressor/tool/FileDecompressor.java b/sources/src/main/java/pr2/io/compressor/tool/FileDecompressor.java new file mode 100644 index 0000000..dad8374 --- /dev/null +++ b/sources/src/main/java/pr2/io/compressor/tool/FileDecompressor.java @@ -0,0 +1,19 @@ +package pr2.io.compressor.tool; + +import pr2.io.compressor.DecompressingInputStream; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Decompresses a single file or stdin. + */ +public class FileDecompressor { + + public static void main(String... args) throws Exception { + + // TODO: Methode implementieren + } +}