diff --git a/Tehtavananto.md b/Tehtavananto.md
index 8105551311a1883df4b02fa14ac55085692cdac7..f51d799671439dcc6ae73503ea9ae545f246214d 100644
--- a/Tehtavananto.md
+++ b/Tehtavananto.md
@@ -124,12 +124,12 @@ halutaan molemmat testata erikseen. Toimiiko lajittelu oikein?
 
 ***B.*** Activation-luokassa on toteutettu rutiini parametricReLU. 
 - Tutustu rutiiniin ja määrittele, mitä asioita testien pitää testata?  
-- Toteuta rutiiniin löytämiesi vaatimusten testaaminen assert lauseella. Toimiiko rutiini määrittelynsä mukaan?
+[- Toteuta rutiiniin löytämiesi vaatimusten testaaminen assert lauseella. Toimiiko rutiini määrittelynsä mukaan?
 
 *Huomaa, että oletuksena assertions ei ole Javassa toiminnassa vaan se pitää kytkeä päälle
 erikseen Java virtuaalikoneen parametrillä -ea Parametrin saa asetettua käyttöön myös IDE-ympäristöissä*
 
-
+]()
 ***C.*** Palindrome-luokassa on rutiini convertToPalindrome, joka tekee syötteenä saamastaan
 merkkijonosta palindromin. 
 - Tutustu rutiiniin ja määrittele, mitä asioita testien pitää testata?
diff --git a/pom.xml b/pom.xml
index c41821efbdf8aeae6637a1281a6f941947e17956..3263009e9faca278515bec224677b04213367064 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,7 +42,7 @@
         <!-- Version numbers for various modules -->
         <jdk.version>17</jdk.version>
         <jqwik.version>1.7.1</jqwik.version>
-        <junit.version>5.9.1</junit.version>
+        <junit.version>5.9.2</junit.version>
         <junitplatform.version>1.9.1</junitplatform.version>
     </properties>
 
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/Activation.java b/src/main/java/fi/utu/tech/ooj/exercise2/Activation.java
deleted file mode 100644
index 6985b09db5e84cc379fd30ad1f210c1290b1f2df..0000000000000000000000000000000000000000
--- a/src/main/java/fi/utu/tech/ooj/exercise2/Activation.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package fi.utu.tech.ooj.exercise2;
-
-public class Activation {
-
-    public static float parametricReLU(float x, float a) {
-        if (x>0) {
-            return x;
-        }
-        return a*x;
-    }
-}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/Main.java b/src/main/java/fi/utu/tech/ooj/exercise2/Main.java
index a51d8d2a7318b9ff030b44fbbfe14adf2fe00cc1..1c4fef7a93ae6d54705541afe16f46d94eee9c6b 100644
--- a/src/main/java/fi/utu/tech/ooj/exercise2/Main.java
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/Main.java
@@ -1,6 +1,11 @@
 package fi.utu.tech.ooj.exercise2;
 
 
+import fi.utu.tech.ooj.exercise2.tehtava1.Laskutussovellus;
+import fi.utu.tech.ooj.exercise2.tehtava3.Palindrome;
+import fi.utu.tech.ooj.exercise2.tehtava3.Sorting;
+import fi.utu.tech.ooj.exercise2.tehtava4.Kayttoliittyma;
+
 import java.util.Arrays;
 
 public class Main {
@@ -25,5 +30,9 @@ public class Main {
         System.out.println("Sorted array: " + Arrays.toString(someIntegers));
 
         System.out.println(Palindrome.convertToPalindrome("jari"));
+
+        Laskutussovellus laskutussovellus = new Laskutussovellus();
+
+        Kayttoliittyma kayttoliittyma = new Kayttoliittyma();
     }
 }
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/Sorting.java b/src/main/java/fi/utu/tech/ooj/exercise2/Sorting.java
deleted file mode 100644
index 37736f01ead260d9a0af21ee6b38420f7d427158..0000000000000000000000000000000000000000
--- a/src/main/java/fi/utu/tech/ooj/exercise2/Sorting.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package fi.utu.tech.ooj.exercise2;
-
-public class Sorting {
-
-    public static void mergeSort(int[] a) {
-        int n = a.length;
-        if (n < 2) {
-            return;
-        }
-        int mid = n / 2;
-        int[] l = new int[mid];
-        int[] r = new int[n - mid];
-
-        for (int i = 0; i < mid; i++) {
-            l[i] = a[i];
-        }
-        for (int i = mid; i < n; i++) {
-            r[i - mid] = a[i];
-        }
-        mergeSort(l);
-        mergeSort(r);
-
-        merge(a, l, r, mid, n - mid);
-    }
-
-    public static void merge(int[] a, int[] l, int[] r, int left, int right) {
-
-        int i = 0, j = 0, k = 0;
-        while (i < left && j < right) {
-            if (l[i] <= r[j]) {
-                a[k++] = l[i++];
-            }
-            else {
-                a[k++] = r[j++];
-            }
-        }
-        while (i < left) {
-            a[k++] = l[i++];
-        }
-        while (j < right) {
-            a[k++] = r[j++];
-        }
-    }
-}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/Tehtava2ohjelmalogiikka.java b/src/main/java/fi/utu/tech/ooj/exercise2/Tehtava2ohjelmalogiikka.java
deleted file mode 100644
index fd10d37d4f3415ea2010d08bef5bef2cc3b73c55..0000000000000000000000000000000000000000
--- a/src/main/java/fi/utu/tech/ooj/exercise2/Tehtava2ohjelmalogiikka.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package fi.utu.tech.ooj.exercise2;
-
-public class Tehtava2ohjelmalogiikka {
-
-    private Tehtava2tiedostopalvelu tietopalvelu;
-
-    public Tehtava2ohjelmalogiikka(Tehtava2tiedostopalvelu tieto) {
-        this.tietopalvelu = tieto;
-    }
-
-    public void teeJotain() {
-        /*
-            Ohjelmassa käytetään rutiineita, jotka on toteutettu Tehtava2tiedostopalvelu-luokassa.
-            Tässä esimerkissä on kuvattu vain yksi ohjelmalogiikkaan kuuluva luokka. Todellisuudessa
-            luokkia on useita ja ne kaikki riippuvat suoraan konkreetista Tehtava2tiedostopalvelu-luokasta.
-
-            tietopalvelu.haeTiedot();
-            tietopalvelu.lisaaTieto(value);
-            tietopalvelu.muokkaaTieto(value, newValue)
-            tietopalvelu.poistaTieto(value)
-         */
-    }
-}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Asiakas.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Asiakas.java
new file mode 100644
index 0000000000000000000000000000000000000000..580d9e1c8919ccf4342ea712fcdb9111b617e890
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Asiakas.java
@@ -0,0 +1,94 @@
+package fi.utu.tech.ooj.exercise2.tehtava1;
+
+/*  Luokka "Asiakas" sisältää omana yksityisenä kenttänään kaikki tallennetut tiedot.
+    Tallennan kaikki tiedot merkkijonoina, sillä postinumero ja puhelinnumero eivät ole täysin
+    kokonaislukumuotoisia - ne sisältävät nollia luvun alussa, jotka muussa tapauksessa
+    sieventyisivät pois.
+
+    Luokan olisi voinut toteuttaa myös tietueena, mutta se ei mahdollistaisi helposti
+    tulevaisuudessa sitä, että asiakkaan puhelinnumeron tai kotisoitteen voisi vaihtaa
+    (tietue säilyttää sille asetetut arvot muuttumattomina).
+
+    Kaikki tiedot on kapseloitu luokan sisään siten, ettei niitä voi tarkastella ulkopuolelta
+    käsin kuin havainnointimetodeilla. Tämä selkeyttää sovelluksen rakennetta.
+    Konstruktori ei suostu vastaanottamaan null-muotoisia objekteja tai tyhjiä merkkijonoja
+    - tällä haluan ensinnäkin ohjata käyttäjää syöttämään rekisteriin kaikki vaaditut tiedot sekä
+    ennaltaehkäisemään NullReferenceExceptionia ohjelman myöhemmissä suoritusvaiheissa.
+ */
+public class Asiakas {
+    /*  .classInvariant()           GetNimi() != "" && GetKatuosoite() != "" && GetPostinumero() != ""
+                                    && GetPostitoimipaikka() != "" && GetPuhelinnumero() != ""
+        .classInvariantPrivate()    this.nimi != "" && this.katuosoite != "" && this.postinumero != ""
+                                    && this.postitoimipaikka != "" this.puhelinnumero != ""
+                                    && GetNimi() == this.nimi && GetKatuosoite() == this.katuosoite
+                                    && GetPostinumero() == this.postinumero
+                                    && GetPuhelinnumero() == this.puhelinnumero*/
+
+    private String nimi;
+    private String katuosoite;
+    private String postinumero;
+    private String postitoimipaikka;
+    private String puhelinnumero;
+
+    /*  @.pre   nimi!= null && nimi != "" && katuosoite != null && katuosoite != ""
+                && postinumero != null && postinumero != ""
+                && postitoimipaikka != null && postitoimipaikka != ""
+                && puhelinnumero != null && puhelinnumero != ""
+        @.post  GetNimi() == nimi && GetKatuosoite() == katuosoite && GetPostinumero() == postinumero
+                && GetPostitoimipaikka() == postitoimipaikka && GetPuhelinnumero() == puhelinnumero
+        @throws IllegalArgumentException
+                Nostetaan, jos jokin argumenttien merkkijonoista on tyhjä arvo (null)
+                tai tyhjä merkkijono ("")
+     */
+    public Asiakas(String nimi, String katuosoite, String postinumero,
+                   String postitoimipaikka, String puhelinnumero) throws IllegalArgumentException {
+        if ((nimi != null && !nimi.equals("")) && (katuosoite != null && !katuosoite.equals("")) &&
+                (postinumero != null && !postinumero.equals("")) &&
+                (postitoimipaikka != null && !postitoimipaikka.equals("")) &&
+                (puhelinnumero != null && !puhelinnumero.equals(""))) {
+            this.nimi = nimi;
+            this.katuosoite = katuosoite;
+            this.postinumero = postinumero;
+            this.postitoimipaikka = postitoimipaikka;
+            this.puhelinnumero = puhelinnumero;
+        } else {
+            throw new IllegalArgumentException("Metodi vaatii asetetun nimen, katuosoitteen," +
+                    " postinumeron, postitoimipaikan ja puhelinnumeron");
+        }
+    }
+
+    /*  @.pre   true
+        @.post  RESULT = this.nimi
+     */
+    public String GetNimi() {
+        return nimi;
+    }
+
+    /*  @.pre   true
+        @.post  RESULT = this.katuosoite
+     */
+    public String GetKatuosoite() {
+        return katuosoite;
+    }
+
+    /*  @.pre   true
+        @.post  RESULT = this.postinumero
+     */
+    public String GetPostinumero() {
+        return postinumero;
+    }
+
+    /*  @.pre   true
+        @.post  RESULT = this.postitoimipaikka
+     */
+    public String GetPostitoimipaikka() {
+        return postitoimipaikka;
+    }
+
+    /*  @.pre   true
+        @.post  RESULT = this.puhelinnumero
+     */
+    public String GetPuhelinnumero() {
+        return puhelinnumero;
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Asiakasrekisteri.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Asiakasrekisteri.java
new file mode 100644
index 0000000000000000000000000000000000000000..95607412cdae804180f1bc9354d4b78636125a73
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Asiakasrekisteri.java
@@ -0,0 +1,87 @@
+package fi.utu.tech.ooj.exercise2.tehtava1;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.NoSuchElementException;
+
+public class Asiakasrekisteri {
+    /*
+        .classInvariant()
+        .classInvariantPrivate()    asiakkaat != null
+     */
+    private HashMap<Asiakas, Laskut> asiakkaat;
+
+    /*  @.pre true
+        @.post GetAsiakkaat() != null
+     */
+    public Asiakasrekisteri() {
+        asiakkaat = new HashMap<>();
+    }
+
+    /*  @.pre       asiakas != null
+        @.post      this.GetAsiakkaat().CONTAINS(asiakas)
+        @.throws    IllegalArgumentException
+                    Nostetaan, jos asiakas on null (tyhjä arvo)
+     */
+    public void LisaaAsiakas(Asiakas asiakas) throws IllegalArgumentException {
+        if (asiakas != null) {
+            asiakkaat.put(asiakas, new Laskut());
+        } else {
+            throw new IllegalArgumentException("Parametrinä annettua asiakasta ei ole määritelty (arvo on null)");
+        }
+    }
+
+    /*  @.pre       asiakas != null && lasku != null
+        @.post      GetLaskut().CONTAINS(lasku)
+        @.throws    IllegalArgumentException
+                    Nostetaan, jos parametrinä annettua laskua ei ole määritelty (arvon ollessa siten null)
+     */
+    public void LisaaLasku(Asiakas asiakas, Lasku lasku) throws IllegalArgumentException {
+        if (asiakkaat.containsKey(asiakas)) {
+            asiakkaat.get(asiakas).LisaaLasku(lasku);
+        } else {
+            LisaaAsiakas(asiakas);
+        }
+    }
+
+
+    /*  @.pre   true
+        @.post  RESULT = ArrayList<Lasku>()
+ */
+    public ArrayList<Lasku> GetLaskut() {
+        ArrayList<Lasku> palautettavatLaskut = new ArrayList<>();
+        for(Laskut laskut : asiakkaat.values()) {
+            for(Lasku lasku : laskut.GetLaskut()) {
+                palautettavatLaskut.add(lasku);
+            }
+        }
+        return palautettavatLaskut;
+    }
+
+    /*  @.pre   true
+        @.post  RESULT = this.asiakkaat
+    */
+    public ArrayList<Asiakas> GetAsiakkaat() {
+        return new ArrayList<>(asiakkaat.keySet());
+    }
+
+    /*  @.pre       asiakas != null && GetAsiakkaat().CONTAINS(Asiakas)
+        @.post      RESULT == instanceof(Laskut)
+        @.throws    IllegalArgumentException, NoSuchElementException
+                    Nostetaan IllegalArgumentException,
+                    jos metodin parametrinä annettu Asiakas-luokan olio on tyhjä (null)
+                    Nostetaan NoSuchElementException,
+                    jos metodin parametrinä annettua Asiakas-luokan oliota ei löydy luokkaan kapseloidusta kokoelmasta
+    */
+    public Laskut GetAsiakkaanLaskut(Asiakas asiakas) throws IllegalArgumentException, NoSuchElementException {
+        if (asiakas != null) {
+            if (asiakkaat.containsKey(asiakas)) {
+                return asiakkaat.get(asiakas);
+            } else {
+                throw new NoSuchElementException("Parametrinä annettua asiakasta ei ole lisätty asiakaslistaan");
+            }
+        } else {
+            throw new IllegalArgumentException("Parametrinä annettua asiakasta ei ole määritelty (arvo on null)");
+        }
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Lasku.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Lasku.java
new file mode 100644
index 0000000000000000000000000000000000000000..9b4d14cd3acb42bd6d0d7651252140de797a4285
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Lasku.java
@@ -0,0 +1,168 @@
+package fi.utu.tech.ooj.exercise2.tehtava1;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.NoSuchElementException;
+
+/*  Luokka sisältää yksittäisen laskun toiminnallisuuden.
+    Laskussa on rivejä, joista jokainen edustaa yksittäistä tuotetta.
+    Mikäli laskuun yritetään lisätä useita samoja tuotteita, yhdistetään ne samalle riville.
+    Ratkaisun seurauksena kaikille saman tuotteen esiintymille on asetettu yhteinen alennuksensa,
+    joka ei välttämättä ole intuitiivisin ratkaisu, mutta mielestäni ohjelmoinnillisesti tyylikkäin.
+
+    Tuotteet on järjestetty hajautustauluun, jotta tuotteiden hakeminen taulusta nopeutuisi.
+    Luokka sisältää vain yhden kentän, joka on edellämainittu hajautustaulu. Kenttä on kapseloitu
+    osaksi luokkaa.
+
+    Toteutus ei sisällä tietoa laskun
+    omistajasta (asiakkaasta), sillä se säilötään asiakasrekisterin hajautustaulussa. Lisäksi tämä
+    toteutus mahdollistaa Lasku-luokan uudelleenkäytön muissakin yhteyksissä, joihin ei välttämättä
+    liity Asiakas-luokan oliota. Vaihtoehtoisesti vastaavan muunneltavuuden olisi voinut toteuttaa
+    myös siten, että asiakas-luokka olisi periytetty jostain abstraktista luokasta, mutta se olisi
+    ollut tässä yhteydessä nähdäkseni liian työlästä.
+ */
+public class Lasku {
+    /*  @.classInvariant        true
+        @.classInvariantPrivate tuotteet != null && FOR (x : tuotteet.values()) => x = [], x.length = 2
+
+        Arvoksi asetetun taulukon ensimmäinen sarake kertoo tuotteen lukumäärän, toinen alennuksen
+     */
+    private HashMap<Tuote, double[]> tuotteet;
+
+    /*  @.pre   true
+        @.post  GetTuotteet != null
+     */
+    public Lasku() {
+        tuotteet = new HashMap<>();
+    }
+
+    /*  @.pre       (tuote) != null
+        @.post      GetTuotteet().CONTAINS(tuote)
+        @.throws    IllegalArgumentException
+                    Nostetaan, jos argumenteissa annettua muuttujaa "tuote" ei ole määritelty
+                    eli sen arvo on "null"
+     */
+    public void LisaaTuote(Tuote tuote) throws IllegalArgumentException {
+        if (tuote != null) {
+            if (!tuotteet.containsKey(tuote)) {
+                tuotteet.put(tuote, new double[]{1,0});
+            } else {
+                tuotteet.get(tuote)[0]++;
+            }
+        } else {
+            throw new IllegalArgumentException("Parametrinä annettua tuotetta ei ole määritelty (arvo on null");
+        }
+    }
+
+    /*  @.pre       (tuote) != null, (lukumaara) > 0, (alennus) > 0 && (alennus) <= 100
+        @.post      GetTuotteet.CONTAINS(tuote),
+        @.throws    IllegalArgumentException, NoSuchElementException
+                    Nostaa IllegalArgumentException, kun parametrinä annettu tuote on tyhjä arvo
+                    TAI tuotteen lukumääräksi on asetettu 0 tai pienempi luku
+                    Nostaa NoSuchElementException, jos metodin käyttämä SetAlennus-asetusmetodi
+                    ei löydä parametrinä annettua tuotetta hajautustaulusta
+     */
+    public void LisaaTuote(Tuote tuote, int lukumaara, double alennus) throws IllegalArgumentException,
+            NoSuchElementException{
+        if (lukumaara > 0) {
+            LisaaTuote(tuote);
+            tuotteet.get(tuote)[0] += (lukumaara-1);
+            SetAlennus(tuote, alennus);
+        } else {
+            throw new IllegalArgumentException("Lukumäärän on oltava suurempi kuin nolla");
+        }
+    }
+
+    /*  @.pre       (tuote) != null && GetTuotteet().CONTAINS(Tuote)
+        @.post      true
+        @.throws    IllegalArgumentException, NoSuchElementException
+                    Nostaa IllegalArgumentException, kun parametrinä annettu tuote on tyhjä arvo
+                    TAI jos parametriksi "alennus" on syötetty negatiivinen luku.
+                    Nostaa NoSuchElementException, kun tuotetta ei ole lisätty laskulle
+     */
+    public void SetAlennus(Tuote tuote, double alennus) throws IllegalArgumentException,
+            NoSuchElementException {
+        if (tuote != null) {
+            if (tuotteet.containsKey(tuote)) {
+                if(alennus > 0 && alennus <=100) {
+                    tuotteet.get(tuote)[1] = alennus;
+                } else {
+                    throw new IllegalArgumentException("Alennuksen on oltava suurempi kuin nolla");
+                }
+            } else {
+                throw new NoSuchElementException("Tuotetta ei löytynyt laskulta");
+            }
+        } else {
+            throw new IllegalArgumentException("Parametrinä annettua tuotetta ei ole määritelty (arvo on null)");
+        }
+    }
+
+    /*  @.pre       (tuote) != null && GetTuotteet().CONTAINS(Tuote)
+        @.post      true
+        @.throws    IllegalArgumentException, NoSuchElementException
+                    Nostaa IllegalArgumentException, kun parametrinä annettu tuote on tyhjä arvo.
+                    Nostaa NoSuchElementException, kun tuotetta ei ole lisätty laskulle
+     */
+    public double GetAlennus(Tuote tuote) throws IllegalArgumentException, NoSuchElementException {
+        if (tuote != null) {
+            if (tuotteet.containsKey(tuote)) {
+                double aHinta = tuote.GetVerollinenHinta();
+                double lkm = tuotteet.get(tuote)[0];
+                double alennus = tuotteet.get(tuote)[1];
+
+                return alennus;
+            } else {
+                throw new NoSuchElementException("Tuotetta ei löytynyt laskulta");
+            }
+        } else {
+            throw new IllegalArgumentException("Parametrinä annettua tuotetta ei ole määritelty (arvo on null)");
+        }
+    }
+
+    /*  @.pre       (tuote) != null && GetTuotteet().CONTAINS(Tuote)
+        @.post      true
+        @.throws    IllegalArgumentException, NoSuchElementException
+                    Nostaa IllegalArgumentException, kun parametrinä annettu tuote on tyhjä arvo.
+                    Nostaa NoSuchElementException, kun tuotetta ei ole lisätty laskulle
+     */
+    public double GetLukumaara(Tuote tuote) throws IllegalArgumentException, NoSuchElementException {
+        if (tuote != null) {
+            if (tuotteet.containsKey(tuote)) {
+                return tuotteet.get(tuote)[0];
+            } else {
+                throw new NoSuchElementException("Tuotetta ei löytynyt laskulta");
+            }
+        } else {
+            throw new IllegalArgumentException("Parametrinä anenttua tuotetta ei ole määritelty (arvo on null)");
+        }
+    }
+
+    /*  @.pre       (tuote) != null && GetTuotteet().CONTAINS(Tuote)
+        @.post      true
+        @.throws    IllegalArgumentException, NoSuchElementException
+                    Nostaa IllegalArgumentException, kun parametrinä annettu tuote on tyhjä arvo.
+                    Nostaa NoSuchElementException, kun tuotetta ei ole lisätty laskulle
+     */
+    public double GetHinta(Tuote tuote) throws IllegalArgumentException, NoSuchElementException {
+        if (tuote != null) {
+            if (tuotteet.containsKey(tuote)) {
+                double lukumaara = tuotteet.get(tuote)[0];
+                double alennus = tuotteet.get(tuote)[1];
+
+                double hinta = tuote.GetVerollinenHinta() * lukumaara - alennus;
+                return hinta;
+            } else {
+                throw new NoSuchElementException("Tuotetta ei löytynyt laskulta");
+            }
+        } else {
+            throw new IllegalArgumentException("Parametrinä anenttua tuotetta ei ole määritelty (arvo on null)");
+        }
+    }
+
+    /*  @.pre true
+        @.post RESULT == ArrayList<Tuote>
+     */
+    public ArrayList<Tuote> GetTuotteet() {
+        return new ArrayList<>(tuotteet.keySet());
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Laskut.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Laskut.java
new file mode 100644
index 0000000000000000000000000000000000000000..ca0089f74bf0ee81bdf33f7df64379ec33a55b24
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Laskut.java
@@ -0,0 +1,50 @@
+package fi.utu.tech.ooj.exercise2.tehtava1;
+
+import fi.utu.tech.ooj.exercise2.tehtava1.Lasku;
+
+import java.util.ArrayList;
+
+/*
+    Laskut on kapseloitu luokka, joka sisältää yksinkertaisesti ArrayList-muotoisen kentän
+    Lasku-luokan olioiden säilyttämiseen, luokan ArrayList-muotoisen laskut-nimisen
+    olion asetuksen Laskut-luokankonstuktorissa, LisääLasku-asetusmetodin ja GetLaskut-
+    havainnointimetodin. Toteutus ei sisällä tietoa laskujen omistajasta (asiakkaasta), sillä ne
+    säilötään asiakasrekisterin hajautustaulussa. Lisäksi tämä toteutus mahdollistaa Laskut-luokan
+    uudelleenkäytön muissakin yhteyksissä, joihin ei välttämättä liity Asiakas-luokan oliota.
+    Vaihtoehtoisesti vastaavan muunneltavuuden olisi voinut toteuttaa myös siten, että
+    asiakas-luokka olisi periytetty jostain abstraktista luokasta, mutta se olisi ollut tässä
+    yhteydessä nähdäkseni liian työlästä.
+ */
+public class Laskut {
+    /*  @.classInvariant        true
+        @.classInvariantPrivate laskut != null
+     */
+    private ArrayList<Lasku> laskut;
+
+    /*  @.pre true
+        @-post GetLaskut() != null
+     */
+    public Laskut() {
+        laskut = new ArrayList<>();
+    }
+
+    /*  @.pre       lasku != null
+        @.post      GetLaskut().CONTAINS(lasku)
+        @.throws    IllegalArgumentException
+                    Nostetaan, jos parametrinä annettua laskua ei ole määritelty (arvon ollessa siten null)
+     */
+    public void LisaaLasku(Lasku lasku) throws IllegalArgumentException {
+        if (lasku != null) {
+            laskut.add(lasku);
+        } else {
+            throw new IllegalArgumentException("Parametrinä annettua laskua ei ole määritelty (arvo on null)");
+        }
+    }
+
+    /*  @.pre   true
+        @.post  RESULT = ArrayList<Lasku>
+     */
+    public ArrayList<Lasku> GetLaskut() {
+        return laskut;
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Laskutussovellus.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Laskutussovellus.java
new file mode 100644
index 0000000000000000000000000000000000000000..93c23233fa8f4e860e0f3464e84449119b000b06
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Laskutussovellus.java
@@ -0,0 +1,42 @@
+package fi.utu.tech.ooj.exercise2.tehtava1;
+
+public class Laskutussovellus {
+    private Tuotteet tuotteet;
+    private Asiakasrekisteri asiakasrekisteri;
+
+    public Laskutussovellus() {
+        tuotteet = new Tuotteet();
+        asiakasrekisteri = new Asiakasrekisteri();
+
+        Tuote lappari = new Tuote("Läppäri", 900, 24);
+        Tuote hiiri = new Tuote("Hiiri", 45, 24);
+        Tuote energiajuoma = new Tuote("Energiajuoma, 6-pack", 10, 12);
+
+        Asiakas matti = new Asiakas("Matti Meikäläinen", "Ylälahdentie 6b",
+                "58500", "Punkaharju", "0501234567");
+
+        asiakasrekisteri.LisaaAsiakas(matti);
+        Lasku lasku = new Lasku();
+        lasku.LisaaTuote(lappari);
+        lasku.LisaaTuote(hiiri);
+        lasku.LisaaTuote(hiiri);
+        lasku.LisaaTuote(energiajuoma, 2, 5);
+        asiakasrekisteri.LisaaLasku(matti, lasku);
+
+        System.out.println("ESIMERKKI LASKUTUSSOVELLUKSEN TOIMINNASTA:");
+
+        for(Asiakas asiakas : asiakasrekisteri.GetAsiakkaat()) {
+            System.out.println("Asiakas: " + asiakas.GetNimi());
+            for(Lasku asLasku : asiakasrekisteri.GetAsiakkaanLaskut(asiakas).GetLaskut()) {
+                for(Tuote tuote: asLasku.GetTuotteet()) {
+                    System.out.println("Tuotteen nimi: " + tuote.GetNimi() + ", Normaalihinta: " +
+                            tuote.GetNormaalihinta() + ", Vero-%:  " + tuote.GetAlvProsentti()
+                            + ", Verollinen hinta: " + tuote.GetVerollinenHinta() + ", Kappalemäärä: "
+                            + asLasku.GetLukumaara(tuote) + ", Alennus "  + asLasku.GetAlennus(tuote)
+                            + ", Yhteishinta: " + asLasku.GetHinta(tuote));
+                }
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Tuote.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Tuote.java
new file mode 100644
index 0000000000000000000000000000000000000000..de1d69936456d90b58fa0d756d06227b573e906e
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Tuote.java
@@ -0,0 +1,83 @@
+package fi.utu.tech.ooj.exercise2.tehtava1;
+
+/*  Tuote-luokka sisältää kapseloituna nimen, normaalihinnan ja alv-prosentin.
+    Luokan konstuktori heittää IllegalArgumentException-poikkeuksen, jos sen argumentiksi
+    syötetään tyhjä arvo (null) tai tyhjä merkkijono. Normaalihinnan on oltava mielekäs, eli > 0.
+    Alv-prosentin on oltava jokseenkin järkevä, joten olen asettanut sille ehdot 0 <= alvProsentti <= 50
+
+    Tuote-luokassa on myös oma metodinsa, joka laskee tuotteen hinnasta ja alv-prosentista tuotteen
+    verollisen hinnan. Lähden toteutuksessa liikkeelle siitä, että rivikohtainen alennus annetaan
+    nimenomaan verollisesta hinnasta, ei verottomasta.
+ */
+public class Tuote {
+    /*  classInvariant()        GetNimi() != "" && GetNormaalihinta() > 0
+        && GetAlvProsentti() >= 0 && GetAlvProsentti() <= 50
+        classInvariantPrivate() nimi != null
+     */
+    private String nimi;
+
+    private double normaalihinta;
+
+    private double alvProsentti;
+
+    /*  @.pre       nimi != null && !nimi.equals("") && normaalihinta > 0
+                    && alvProsentti >= 0 && alvProsentti <= 50
+        @.post      GetNimi().equals(nimi) && this.toString().equals(nimi) && GetNormaalihinta() == normaalihinta
+                    && GetAlvProsentti() == alvProsentti && GetVerollinenHinta()
+                    == (1 + (alvProsentti/100)) * normaalihinta
+        @.throws    IllegalArgumentException
+                    Nostetaan, jos merkkijonoa "nimi" ei ole määritelty tai se on tyhjä merkkijono
+                    TAI jos "normaalihinta" <= 0 TAI jos alvProsentti ei ole välillä 0-50
+     */
+    public Tuote(String nimi, double normaalihinta, double alvProsentti) throws IllegalArgumentException {
+        if (nimi != null && !nimi.equals("") && normaalihinta > 0 && alvProsentti >= 0 && alvProsentti <= 50) {
+            this.nimi = nimi;
+            this.normaalihinta = normaalihinta;
+            this.alvProsentti = alvProsentti;
+        } else {
+            throw new IllegalArgumentException("Jokin seuraavista tapahtui:" +
+                    "(A) nimeä ei ole määritelty (sen arvo on tyhjä)" +
+                    "tai se on tyhjä merkkijono. (B) Tuotteen normaalihinta ei ole suurempi kuin 0" +
+                    " (C) Tuotteen alv-prosentti ei ole välillä 0-50");
+        }
+        this.nimi = nimi;
+        this.normaalihinta = normaalihinta;
+        this.alvProsentti = alvProsentti;
+    }
+
+    /*  @.pre   true
+        @.post  RESULT == (nimi)
+     */
+    public String GetNimi() {
+        return nimi;
+    }
+
+    /*  @.pre   true
+        @.post  RESULT == (normaalihinta)
+     */
+    public double GetNormaalihinta() {
+        return normaalihinta;
+    }
+
+    /*  @.pre   true
+        @.post  RESULT == (alvProsentti)
+     */
+    public double GetAlvProsentti() {
+        return alvProsentti;
+    }
+
+    /*  @.pre   true
+        @.post  RESULT == (1 + GetAlvProsentti()) * GetNormaalihinta();
+     */
+    public double GetVerollinenHinta() {
+        return (1 + (alvProsentti/100)) * normaalihinta;
+    }
+
+    /*  @.pre true
+        @.post RESULT = GetNimi()
+     */
+    @Override
+    public String toString() {
+        return GetNimi();
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Tuotteet.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Tuotteet.java
new file mode 100644
index 0000000000000000000000000000000000000000..a76ea876100e59fb84b0128d1fb10c8dafa51b68
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava1/Tuotteet.java
@@ -0,0 +1,44 @@
+package fi.utu.tech.ooj.exercise2.tehtava1;
+
+import fi.utu.tech.ooj.exercise2.tehtava1.Tuote;
+
+import java.util.ArrayList;
+
+/*
+    Tuotteet on kapseloitu luokka, joka sisältää yksinkertaisesti ArrayList-muotoisen kentän
+    Tuote-luokan olioiden säilyttämiseen, luokan ArrayList-muotoisen tuotteet-nimisen olion
+    asetuksen Tuotteet-luokan konstuktorissa, LisääTuote-asetusmetodin ja GetTuotteet-havainnointimetodin.
+ */
+public class Tuotteet {
+    /*  @.classInvariant        true
+        @.classInvariantPrivate tuotteet != null
+     */
+    private ArrayList<Tuote> tuotteet;
+
+    /*  @.pre true
+        @-post GetTuotteet() != null
+     */
+    public Tuotteet() {
+        tuotteet = new ArrayList<>();
+    }
+
+    /*  @.pre       tuote != null
+        @.post      GetTuotteet().CONTAINS(tuote)
+        @.throws    IllegalArgumentException
+                    Nostetaan, jos parametrinä annettua tuotetta ei ole määritelty (arvon ollessa siten null)
+     */
+    public void LisaaTuote(Tuote tuote) throws IllegalArgumentException {
+        if (tuote != null) {
+            tuotteet.add(tuote);
+        } else {
+            throw new IllegalArgumentException("Parametrinä annettua tuotetta ei ole määritelty (arvo on null)");
+        }
+    }
+
+    /*  @.pre   true
+        @.post  RESULT = ArrayList<Tuote>
+     */
+    public ArrayList<Tuote> GetTuotteet() {
+        return tuotteet;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava2/Tehtava2ohjelmalogiikka.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava2/Tehtava2ohjelmalogiikka.java
new file mode 100644
index 0000000000000000000000000000000000000000..e57f2d9f6a215aa87114708e7ad67e8a51da5fae
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava2/Tehtava2ohjelmalogiikka.java
@@ -0,0 +1,45 @@
+package fi.utu.tech.ooj.exercise2.tehtava2;
+
+import fi.utu.tech.ooj.exercise2.tehtava2.tietoyhteys.Tiedostopalvelu;
+
+/*
+Toteutin toteutuksen luomalla rajapinnan Tiedotopalvelu, jonka
+Tehtava2tiedostopalvelu- ja Tehtava2tietokanta -luokat toteuttavat.
+Mielestäni rajapinta oli järkevä ratkaisu, sillä jos olisin periyttänyt luokkien yhteisen
+toiminnallisuuden jostain abstraktista luokasta tai konkreetista kattoluokasta,
+tarkoittaisi se sitä, etteivät aliluokat voi periä mitään muita luokkia: Java ei nimittäin
+ohjelmointikielenä tue moniperintää. Onnistuessaankin moniperintä voisi monimutkaistaa koodia
+ja siten johtaa turhiin poikkeuksiin ohjelmakoodia suorittaessa. Tiedostopalvelu-toiminnan periyttäminen
+saattaisi johtaa myöhemmin kehitysvaiheessa arkkitehtuurisiin ongelmiin. Lisäksi oletuksena periytetty
+toiminnallisuus pitäisi ohittaa (override) tai jättää kattoluokassa määrittelemättä (abstraktiksi),
+mikäli alaluokissa halutaan tehdä omia toteutuksia yhteisen toiminnallisuuden suhteen. Rajapinnan
+toteuttaminen olikin siten nähdäkseni ilmeinen ratkaisu. Rajapintatoteutuksen heikkoutena on tietenkin, että se vaatii täysin
+samanlaisenkin toiminnallisuuden ohjelmoinnin useaan kertaan ja siten voi johtaa koodin tarpeettomaan
+kopiointiin. Toisaalta, mikäli alun perin haasteena oli saman toiminnon eriävien toteutuksien käsittely,
+vastaa rajapintojen hyödyntäminen siihen erinomaisesti. Mikäli toiminnallisuudet vain periytettäisiin
+kattoluokasta käsin, voisi alempiin luokkiin periytyä myös sellaisia ominaisuuksia, joille ei ole
+perivissä luokissa käyttöä. Java-kielessä tällaisia toiminnallisuuksia olisi mahdoton piilottaa.
+ */
+public class Tehtava2ohjelmalogiikka {
+
+    private Tiedostopalvelu tietopalvelu;
+
+    /*  Tehtava2ohjelmalogiikka-konstuktorin parametreissä voi asettaa toteuttajaksi
+        Minkä tahansa Tiedostopalvelu-rajapinnan täyttävän luokan */
+    public Tehtava2ohjelmalogiikka(Tiedostopalvelu tieto) {
+        this.tietopalvelu = tieto;
+    }
+
+    public void teeJotain() {
+        /*
+            Ohjelmassa käytetään rutiineita, jotka on toteutettu Tehtava2tiedostopalvelu-luokassa.
+            Tässä esimerkissä on kuvattu vain yksi ohjelmalogiikkaan kuuluva luokka. Todellisuudessa
+            luokkia on useita ja ne kaikki riippuvat suoraan konkreetista Tehtava2tiedostopalvelu-luokasta.
+
+            tietopalvelu.haeTiedot();
+            tietopalvelu.lisaaTieto(value);
+            tietopalvelu.muokkaaTieto(value, newValue)
+            tietopalvelu.poistaTieto(value)
+         */
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/Tehtava2tiedostopalvelu.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava2/tietoyhteys/Tehtava2tiedostopalvelu.java
similarity index 80%
rename from src/main/java/fi/utu/tech/ooj/exercise2/Tehtava2tiedostopalvelu.java
rename to src/main/java/fi/utu/tech/ooj/exercise2/tehtava2/tietoyhteys/Tehtava2tiedostopalvelu.java
index 3eb62bbfd7c2a3fa267a6856711752dd8f6fce8d..764866ffd6338a711433a32fd40fd794aef3f578 100644
--- a/src/main/java/fi/utu/tech/ooj/exercise2/Tehtava2tiedostopalvelu.java
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava2/tietoyhteys/Tehtava2tiedostopalvelu.java
@@ -1,8 +1,8 @@
-package fi.utu.tech.ooj.exercise2;
+package fi.utu.tech.ooj.exercise2.tehtava2.tietoyhteys;
 
 import java.util.List;
 
-public class Tehtava2tiedostopalvelu<T> {
+public class Tehtava2tiedostopalvelu<T> implements Tiedostopalvelu<T> {
 
 
     public void lisaaTieto(T value) {
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/Tehtava2tietokanta1.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava2/tietoyhteys/Tehtava2tietokanta1.java
similarity index 74%
rename from src/main/java/fi/utu/tech/ooj/exercise2/Tehtava2tietokanta1.java
rename to src/main/java/fi/utu/tech/ooj/exercise2/tehtava2/tietoyhteys/Tehtava2tietokanta1.java
index 06f5af4617e33534495d435ce92ac84ed579af94..c2fc3354732ebae955503003ffebd824e105f7d3 100644
--- a/src/main/java/fi/utu/tech/ooj/exercise2/Tehtava2tietokanta1.java
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava2/tietoyhteys/Tehtava2tietokanta1.java
@@ -1,8 +1,8 @@
-package fi.utu.tech.ooj.exercise2;
+package fi.utu.tech.ooj.exercise2.tehtava2.tietoyhteys;
 
 import java.util.List;
 
-public class Tehtava2tietokanta1<T> {
+public class Tehtava2tietokanta1<T> implements Tiedostopalvelu<T> {
 
     public void lisaaTieto(T value) {
         //Toteutus poistettu. Se on merkityksetön tehtävän kannalta.
@@ -16,7 +16,7 @@ public class Tehtava2tietokanta1<T> {
     public  void muokkaaTieto(T value, T newValue) {
         //Toteutus poistettu. Se on merkityksetön tehtävän kannalta.
     }
-    public  void poistaTieto(T value) {
+    public void poistaTieto(T value) {
         //Toteutus poistettu. Se on merkityksetön tehtävän kannalta.
     }
 }
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava2/tietoyhteys/Tiedostopalvelu.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava2/tietoyhteys/Tiedostopalvelu.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c452a753b87c66bbb2f165227342c597cf62cdf
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava2/tietoyhteys/Tiedostopalvelu.java
@@ -0,0 +1,14 @@
+package fi.utu.tech.ooj.exercise2.tehtava2.tietoyhteys;
+
+import java.util.List;
+
+public interface Tiedostopalvelu<T> {
+    void lisaaTieto (T value);
+    List<T> haeTiedot();
+
+    void muokkaaTieto(T value, T newValue);
+
+    void poistaTieto(T value);
+
+
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava3/Activation.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava3/Activation.java
new file mode 100644
index 0000000000000000000000000000000000000000..a96a255d52712acc007faf48f6d69b98315d89bd
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava3/Activation.java
@@ -0,0 +1,20 @@
+package fi.utu.tech.ooj.exercise2.tehtava3;
+
+public class Activation {
+
+    /*
+    Jotta metodi toimisi kuin parametrisen ReLU-funktion kuuluu, sen tulee
+    palauttaa x, jos x > 0 ja muussa tapauksessa parametri a * x.
+    Tätä voidaan testata seuraavilla testeillä
+
+    (1) Palauttaako funktio x:n, kun x = 1?
+    (2) Palauttaako funtio a * x, kun x on 0 tai negatiivinen luku ja
+        a satunnaisluku välillä (0.01-1)?
+     */
+    public static float parametricReLU(float x, float a) {
+        if (x>0) {
+            return x;
+        }
+        return a*x;
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/Car.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava3/Car.java
similarity index 93%
rename from src/main/java/fi/utu/tech/ooj/exercise2/Car.java
rename to src/main/java/fi/utu/tech/ooj/exercise2/tehtava3/Car.java
index caafc984eb47b91480b18aa5e99dbb675fc66991..371bd7198096182f96c8ee10d949d35d6f6e9b42 100644
--- a/src/main/java/fi/utu/tech/ooj/exercise2/Car.java
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava3/Car.java
@@ -1,7 +1,12 @@
-package fi.utu.tech.ooj.exercise2;
+package fi.utu.tech.ooj.exercise2.tehtava3;
 
 import java.time.Year;
 
+/*
+***D.*** Tutustu tehtävä pohjasta löytyvään Car-luokkaan.
+- Suunnittele yksikkötestit, jotka tarkastavat säilyttävätkö luokan rutiinit luokkainvariantin vaatimukset.
+- Toteuta edellä suunnitellut yksikkötestit käyttäen JUnit-kirjastoa.
+ */
 public class Car {
 
     /*
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/Palindrome.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava3/Palindrome.java
similarity index 80%
rename from src/main/java/fi/utu/tech/ooj/exercise2/Palindrome.java
rename to src/main/java/fi/utu/tech/ooj/exercise2/tehtava3/Palindrome.java
index c338723fc971a4349eaacb1fd395afc7ba234699..f9e7b3cb90a62cd988f54c6bf6082b0b87c24ea3 100644
--- a/src/main/java/fi/utu/tech/ooj/exercise2/Palindrome.java
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava3/Palindrome.java
@@ -1,4 +1,4 @@
-package fi.utu.tech.ooj.exercise2;
+package fi.utu.tech.ooj.exercise2.tehtava3;
 
 public class Palindrome {
 
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava3/Sorting.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava3/Sorting.java
new file mode 100644
index 0000000000000000000000000000000000000000..81107681c29d4ab5d605d79df77ade01de012ea2
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava3/Sorting.java
@@ -0,0 +1,70 @@
+package fi.utu.tech.ooj.exercise2.tehtava3;
+
+import java.util.Arrays;
+
+/*
+    mergeSort-rutiini toteuttaa lomituslajittelun, missä järjestämätön
+    taulukko pilkotaan puolivälistään kahteen pienempään taulukkoon,
+    jotka vuorostaan pilkotaan pienempiin n kertaa kunnes jokaiseen taulukkoon
+    jää jäljelle yksi alkio. Tämän jälkeen alkiot järjestetään saman menettelytavan
+    mukaan aina kaksinkertaiseen taulukkoon ja alkioiden kesken toteutetaan niiden
+    lukuarvoon perustuva lajittelu.
+
+    mergeSort-metodi toteuttaa ensin parametrinä saamansa taulukon jakamisen pienempiin
+    taulukkoihin ja lopuksi kutsuu merge-metodia, joka yhdistää pilkotut taulukot yhdeksi taulukoksi
+    järjestäen pilkottujen taulukoiden alkiot suuruusjärjestykseen ottamalla aina saman indeksin kahdesta
+    eri taulukosta ja asettaen ne uuteen taulukkoon siinä järjestyksessä, kumpi on pienempi
+
+mergeSort-metodin osalta voisi selvittää seuraava:
+        taulukosta poimitaan kaksi satunnaista indeksiä. Onhan jokaisen indeksin kanssa seuraava väite tosi:
+    (jos indeksi(1) > indeksi(2) -> luku(1) > luku(2); jos ei, luku(1) <= luku(2));
+
+    merge-,metodin osalta voisi selvittää
+    (1) onhan palautetussa taulukossa yhtä monta alkiota kuin annetussa taulukossa
+    (2) käsitellään kahta eri yhdistettyä taulukkoa keskenään vertaillen saman indeksin saaneita lukuja taulukoissa:
+        (jos taulukko1.arvoIndeksissä(1) < taulukko2.arvoIndeksissä(1) -> yhdistettyTaulukko.alkionIndeksi(taulukko1.arvoIndeksissä(1)) <= taulukko2.arvoIndeksissä(1);
+        jos ei, yhdistettyTaulukko.alkionIndeksi(taulukko1.arvoIndeksissä(1)) > taulukko2.arvoIndeksissä(1))
+ */
+
+public class Sorting {
+
+    public static void mergeSort(int[] a) {
+        int n = a.length;
+        if (n < 2) {
+            return;
+        }
+        int mid = n / 2;
+        int[] l = new int[mid];
+        int[] r = new int[n - mid];
+
+        for (int i = 0; i < mid; i++) {
+            l[i] = a[i];
+        }
+        for (int i = mid; i < n; i++) {
+            r[i - mid] = a[i];
+        }
+
+        mergeSort(l);
+        mergeSort(r);
+
+        merge(a, l, r, mid, n - mid);
+    }
+
+    public static void merge(int[] a, int[] l, int[] r, int left, int right) {
+        int i = 0, j = 0, k = 0;
+        while (i < left && j < right) {
+            if (l[i] <= r[j]) {
+                a[k++] = l[i++];
+            }
+            else {
+                a[k++] = r[j++];
+            }
+        }
+        while (i < left) {
+            a[k++] = l[i++];
+        }
+        while (j < right) {
+            a[k++] = r[j++];
+        }
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Kayttoliittyma.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Kayttoliittyma.java
new file mode 100644
index 0000000000000000000000000000000000000000..58e1000fa13620120ea4c62438d0085b6e20b9aa
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Kayttoliittyma.java
@@ -0,0 +1,112 @@
+package fi.utu.tech.ooj.exercise2.tehtava4;
+
+import java.util.HashMap;
+import java.util.Scanner;
+
+public class Kayttoliittyma {
+    public Kayttoliittyma() {
+        Scanner scanner = new Scanner(System.in);
+
+        Ottelu ottelu = new Ottelu();
+
+        String input = "";
+
+        while (true) {
+            System.out.println("Haluatko lisätä uuden urheilulajin otteluun? Valitse K (kyllä) / E (ei)");
+
+            input = scanner.nextLine();
+
+            if (input.contains("K")) {
+                String lajinNimi = "";
+                int enimmaispisteet = 100;
+                boolean laskevaPisteytys = true;
+
+                System.out.println("Kirjoita lajin nimi ja paina ENTER:");
+                input = scanner.nextLine();
+                lajinNimi = input;
+
+                System.out.println("Kirjoita lajista saatavat enimmäispisteet ja paina ENTER:");
+                input = scanner.nextLine();
+                enimmaispisteet = Integer.valueOf(input);
+
+                System.out.println("Onko pisteytys laskeva? Laskeva tarkoittaa sitä, että suurempi tulos on parempi. Valitse K (Kyllä) / E (Ei");
+                input = scanner.nextLine();
+
+                laskevaPisteytys = (input.equals("K"));
+
+                Urheilulaji urheilulaji = new Urheilulaji(lajinNimi, enimmaispisteet, laskevaPisteytys);
+                ottelu.LisaaUrheilulaji(urheilulaji);
+                System.out.println();
+                System.out.println("Olet lisännyt urheilulajin. Tällä hetkellä lisättyjä lajeja ovat:");
+                for (Urheilulaji laji : ottelu.PalautaUrheilulajit()) {
+                    System.out.println(laji.nimi);
+                }
+            } else if (input.contains("E")) {
+                break;
+            }
+        }
+
+        while (true) {
+            System.out.println("Haluatko lisätä uuden tuloksen otteluun? Valitse K (kyllä) / E (ei)");
+
+            input = scanner.nextLine();
+
+            if (input.contains("K")) {
+                String urheilijanNimi = "";
+
+                System.out.println("Kirjoita urheilijan nimi, jolle lisäät tuloksen ja paina ENTER:");
+                input = scanner.nextLine();
+                urheilijanNimi = input;
+
+                HashMap<Integer, Urheilulaji> numerotJaLajit = new HashMap<>();
+
+                int i = 1;
+                for (Urheilulaji urheilulaji : ottelu.PalautaUrheilulajit()) {
+                    numerotJaLajit.put(i, urheilulaji);
+                    i++;
+                }
+
+                for (Integer lajinumero : numerotJaLajit.keySet()) {
+                    System.out.println(lajinumero + ": " + numerotJaLajit.get(lajinumero));
+                }
+
+                System.out.println("Valitse lajin numero, jolle lisäät tuloksen:");
+
+                int lajinumero = Integer.valueOf(scanner.nextLine());
+
+                System.out.println("Valitsit lajin: " + numerotJaLajit.get(lajinumero));
+
+                System.out.println("Kirjoita urheilijan tulosluku. Tarvittaessa käytä erottimena pistettä:");
+                Double tulos = Double.valueOf(scanner.nextLine().trim());
+
+                System.out.println("Syötit tuloksen: " + tulos);
+
+                ottelu.LisaaTulos(urheilijanNimi, tulos, numerotJaLajit.get(lajinumero).PalautaNimi());
+
+                System.out.println("Olet lisännyt tuloksen. Tällä hetkellä järjestelmään lisättyjä urheilijoita ovat:");
+
+                for(Urheilija urheilija : ottelu.PalautaUrheilijat()) {
+                    System.out.println(urheilija.PalautaNimi());
+                }
+            } else if (input.contains("E")) {
+                break;
+            }
+        }
+
+        System.out.println("OTTELUN TULOKSET:");
+
+        for (Urheilulaji urheilulaji : ottelu.PalautaUrheilulajit()) {
+            urheilulaji.TulostaTulokset();
+        }
+
+        System.out.println("LOPULLINEN PISTEYTYS:");
+
+        HashMap<Urheilija, Double> pisteytys = ottelu.PisteytaUrheilijat();
+
+        for(Urheilija urheilija : pisteytys.keySet()) {
+            System.out.println(urheilija.PalautaNimi() + ", pisteet " + pisteytys.get(urheilija));
+        }
+
+        System.out.println("OTTELU ON OHI");
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Ottelu.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Ottelu.java
new file mode 100644
index 0000000000000000000000000000000000000000..857d35e443e253960cc287cad6fd662a4ec22af5
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Ottelu.java
@@ -0,0 +1,97 @@
+package fi.utu.tech.ooj.exercise2.tehtava4;
+
+import java.util.*;
+
+public class Ottelu {
+    private HashMap<String, Urheilulaji> urheilulajit;
+    private HashMap<String, Urheilija> lisatytUrheilijat;
+
+    public Ottelu() {
+        urheilulajit = new HashMap<>();
+        lisatytUrheilijat = new HashMap<>();
+    }
+
+    public Ottelu(Urheilulaji[] urheilulajit) {
+        this.urheilulajit = new HashMap<>();
+        for(Urheilulaji urheilulaji : urheilulajit) {
+            this.urheilulajit.put(urheilulaji.PalautaNimi(), urheilulaji);
+        }
+    }
+
+    public Ottelu(ArrayList<Urheilulaji> urheilulajit) {
+        this.urheilulajit = new HashMap<>();
+        for(Urheilulaji urheilulaji : urheilulajit) {
+            this.urheilulajit.put(urheilulaji.PalautaNimi(), urheilulaji);
+        }
+    }
+
+    public boolean LisaaUrheilulaji(Urheilulaji lisattavaLaji) {
+        if (!urheilulajit.containsKey(lisattavaLaji)) {
+            urheilulajit.put(lisattavaLaji.PalautaNimi(), lisattavaLaji);
+            return true;
+        }
+        return false;
+    }
+
+    public ArrayList<Urheilulaji> PalautaUrheilulajit() {
+        ArrayList<Urheilulaji> palautettavaLista = new ArrayList<>();
+
+        for (Urheilulaji urheilulaji : urheilulajit.values()) {
+            palautettavaLista.add(urheilulaji);
+        }
+
+        return palautettavaLista;
+    }
+
+    public ArrayList<Urheilija> PalautaUrheilijat() {
+        ArrayList<Urheilija> urheilijat = new ArrayList<>();
+        for (Urheilija urheilija : lisatytUrheilijat.values()) {
+            urheilijat.add(urheilija);
+        }
+
+        return urheilijat;
+    }
+
+    public boolean LisaaTulos(Urheilija urheilija, double tulos, Urheilulaji urheilulaji) {
+        if (urheilulajit.containsKey(urheilulaji.PalautaNimi())) {
+            if (lisatytUrheilijat.containsKey(urheilija.PalautaNimi())) {
+                urheilija = lisatytUrheilijat.get(urheilija.PalautaNimi());
+            }
+            urheilulaji.LisaaTulos(urheilija, tulos);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public void LisaaTulos(String urheilijanNimi, double tulos, String urheilulajinNimi) {
+        if (urheilulajit.containsKey(urheilulajinNimi)) {
+            Urheilija loydettyUrheilija = null;
+            if (lisatytUrheilijat.containsKey(urheilijanNimi)) {
+                loydettyUrheilija = lisatytUrheilijat.get(urheilijanNimi);
+                urheilulajit.get(urheilulajinNimi).LisaaTulos(loydettyUrheilija, tulos);
+            } else {
+                Urheilija urheilija = new Urheilija(urheilijanNimi);
+                lisatytUrheilijat.put(urheilijanNimi, urheilija);
+                urheilulajit.get(urheilulajinNimi).LisaaTulos(urheilija, tulos);
+            }
+        }
+    }
+
+    public HashMap<Urheilija, Double> PisteytaUrheilijat() {
+        HashMap<Urheilija, Double> pisteytetytUrheilijat = new HashMap<>();
+        for(Urheilulaji urheilulaji : urheilulajit.values()) {
+            HashMap<Urheilija, Double> lajikohtaisetPisteet = urheilulaji.PisteytaUrheilijat();
+
+            for(Urheilija urheilija : lajikohtaisetPisteet.keySet()) {
+                if (!pisteytetytUrheilijat.containsKey(urheilija)) {
+                    pisteytetytUrheilijat.put(urheilija, 0.0);
+                }
+
+                pisteytetytUrheilijat.put(urheilija, pisteytetytUrheilijat.get(urheilija) + lajikohtaisetPisteet.get(urheilija));
+            }
+        }
+
+        return pisteytetytUrheilijat;
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Tulostaulukko.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Tulostaulukko.java
new file mode 100644
index 0000000000000000000000000000000000000000..258440f321a772dac8bf2ff8104b937445f59aed
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Tulostaulukko.java
@@ -0,0 +1,167 @@
+package fi.utu.tech.ooj.exercise2.tehtava4;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.HashMap;
+
+public class Tulostaulukko {
+    private HashMap<Urheilija, ArrayList<Double>> tulokset;
+
+    public Tulostaulukko() {
+        tulokset = new HashMap<>();
+    }
+
+    public ArrayList<Urheilija> PalautaUrheilijat() {
+        ArrayList<Urheilija> urheilijat = new ArrayList<>();
+
+        for (Urheilija urheilija : tulokset.keySet()) {
+            urheilijat.add(urheilija);
+        }
+
+        return urheilijat;
+    }
+
+    public Urheilija PalautaUrheilijaNimella(String nimi) {
+        for(Urheilija urheilija : tulokset.keySet()) {
+            if (urheilija.PalautaNimi().equals(nimi)) {
+                return urheilija;
+            }
+        }
+        return null;
+    }
+
+    public void LisaaTulos(Urheilija urheilija, double tulos) {
+        if (!tulokset.containsKey(urheilija)) {
+            tulokset.put(urheilija, new ArrayList<>());
+        }
+
+        tulokset.get(urheilija).add(tulos);
+    }
+
+    public double PalautaSuurinTulos(Urheilija urheilija) {
+        double suurinTulos = Double.MIN_VALUE;
+
+        if (tulokset.containsKey(urheilija)) {
+            for(Double tulos : tulokset.get(urheilija)) {
+                if (tulos > suurinTulos) {
+                    suurinTulos = tulos;
+                }
+            }
+            return suurinTulos;
+        }
+        return -1;
+    }
+
+    public HashMap<Urheilija, Double> PalautaUrheilijoidenSuurimmatTulokset(Urheilija[] urheilijat) {
+        HashMap<Urheilija, Double> suurimmatTulokset = new HashMap<>();
+        for(Urheilija urheilija : urheilijat) {
+            double suurinTulos = PalautaSuurinTulos(urheilija);
+            if (suurinTulos != -1) {
+                suurimmatTulokset.put(urheilija, suurinTulos);
+            }
+        }
+
+        return suurimmatTulokset;
+    }
+
+    public HashMap<Urheilija, Double> PalautaUrheilijoidenSuurimmatTulokset() {
+        return PalautaUrheilijoidenSuurimmatTulokset(Utils.MuunnaObjektitaulukkoUrheilijataulukoksi(tulokset.keySet().toArray()));
+    }
+
+    public HashMap<Urheilija, Double> PalautaUrheilijoidenSuurimmatTulokset(ArrayList<Urheilija> urheilijat) {
+        return PalautaUrheilijoidenSuurimmatTulokset((Utils.MuunnaObjektitaulukkoUrheilijataulukoksi(tulokset.keySet().toArray())));
+    }
+
+    public ArrayList<Double> PalautaTulokset(Urheilija urheilija) {
+        if (tulokset.containsKey(urheilija)) {
+            return tulokset.get(urheilija);
+        } else {
+            return null;
+        }
+    }
+
+    public Urheilija PalautaUrheilijaSuurimmallaTuloksella() {
+        return PalautaUrheilijaSuurimmallaTuloksella(Utils.MuunnaObjektitaulukkoUrheilijataulukoksi(tulokset.keySet().toArray()));
+    }
+
+    public Urheilija PalautaUrheilijaSuurimmallaTuloksella(Urheilija[] urheilijat) {
+       HashMap<Urheilija, Double> suurimmatTulokset = PalautaUrheilijoidenSuurimmatTulokset(urheilijat);
+
+       Urheilija kaikistaSuurinUrheilija = null;
+       double kaikistaSuurinTulos = Double.MIN_VALUE;
+
+       for (Urheilija urheilija : suurimmatTulokset.keySet()) {
+           if (suurimmatTulokset.get(urheilija) > kaikistaSuurinTulos) {
+               kaikistaSuurinUrheilija = urheilija;
+               kaikistaSuurinTulos = suurimmatTulokset.get(urheilija);
+           }
+       }
+
+       return kaikistaSuurinUrheilija;
+    }
+
+    public Urheilija PalautaUrheilijaSuurimmallaTuloksella(ArrayList<Urheilija> urheilijat) {
+        return PalautaUrheilijaSuurimmallaTuloksella(Utils.MuunnaObjektitaulukkoUrheilijataulukoksi(tulokset.keySet().toArray()));
+    }
+
+    public double PalautaPieninTulos(Urheilija urheilija) {
+        double pieninTulos = Double.MAX_VALUE;
+
+        if (tulokset.containsKey(urheilija)) {
+            for(Double tulos : tulokset.get(urheilija)) {
+                if (tulos < pieninTulos) {
+                    pieninTulos = tulos;
+                }
+            }
+            return pieninTulos;
+        }
+        return -1;
+    }
+
+    public HashMap<Urheilija, Double> PalautaUrheilijoidenPienimmatTulokset() {
+        return PalautaUrheilijoidenPienimmatTulokset(Utils.MuunnaObjektitaulukkoUrheilijataulukoksi(tulokset.keySet().toArray()));
+    }
+
+    public HashMap<Urheilija, Double> PalautaUrheilijoidenPienimmatTulokset(Urheilija[] urheilijat) {
+        HashMap<Urheilija, Double> pienimmatTulokset = new HashMap<>();
+        for(Urheilija urheilija : urheilijat) {
+            double pieninTulos = PalautaPieninTulos(urheilija);
+
+            if (pieninTulos != -1) {
+                pienimmatTulokset.put(urheilija, pieninTulos);
+            }
+        }
+
+        return pienimmatTulokset;
+    }
+
+    public HashMap<Urheilija, Double> PalautaUrheilijoidenPienimmatTulokset(ArrayList<Urheilija> urheilijat) {
+        return PalautaUrheilijoidenPienimmatTulokset(Utils.MuunnaObjektitaulukkoUrheilijataulukoksi(tulokset.keySet().toArray()));
+    }
+
+    public Urheilija PalautaUrheilijaPienimmallaTuloksella() {
+        return PalautaUrheilijaPienimmallaTuloksella(Utils.MuunnaObjektitaulukkoUrheilijataulukoksi(tulokset.keySet().toArray()));
+    }
+
+    public Urheilija PalautaUrheilijaPienimmallaTuloksella(Urheilija[] urheilijat) {
+        HashMap<Urheilija, Double> pienimmatTulokset = PalautaUrheilijoidenSuurimmatTulokset(urheilijat);
+
+        Urheilija kaikistaPieninUrheilija = null;
+        double kaikistaPieninTulos = Double.MAX_VALUE;
+
+        for (Urheilija urheilija : pienimmatTulokset.keySet()) {
+            if (pienimmatTulokset.get(urheilija) < kaikistaPieninTulos) {
+                kaikistaPieninUrheilija = urheilija;
+                kaikistaPieninTulos = pienimmatTulokset.get(urheilija);
+            }
+        }
+
+        return kaikistaPieninUrheilija;
+    }
+
+    public Urheilija PalautaUrheilijaPienimmallaTuloksella(ArrayList<Urheilija> urheilijat) {
+        return PalautaUrheilijaPienimmallaTuloksella(Utils.MuunnaObjektitaulukkoUrheilijataulukoksi(tulokset.keySet().toArray()));
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Urheilija.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Urheilija.java
new file mode 100644
index 0000000000000000000000000000000000000000..9d5b6e5b4ed41fc2c400d4149ab4b9d84a02899b
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Urheilija.java
@@ -0,0 +1,18 @@
+package fi.utu.tech.ooj.exercise2.tehtava4;
+
+public class Urheilija {
+    private String nimi;
+
+    public Urheilija(String nimi) {
+        this.nimi = nimi;
+    }
+
+    public String PalautaNimi() {
+        return nimi;
+    }
+
+    @Override
+    public String toString() {
+        return nimi;
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Urheilulaji.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Urheilulaji.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d34022b54b01ed136f5c1c5ed3e601223d1cb73
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Urheilulaji.java
@@ -0,0 +1,123 @@
+package fi.utu.tech.ooj.exercise2.tehtava4;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class Urheilulaji {
+
+    protected String nimi;
+
+    protected int enimmaismaaraPisteita;
+
+    protected Boolean laskevaPisteytysjarjestys;
+
+    protected Tulostaulukko tulostaulukko;
+
+    public Urheilulaji(String nimi) {
+        this.nimi = nimi;
+        this.tulostaulukko = new Tulostaulukko();
+        this.enimmaismaaraPisteita = 100;
+        this.laskevaPisteytysjarjestys = true;
+    }
+
+    public Urheilulaji(String nimi, int enimmaismaaraPisteita, Boolean laskevaPisteytysjarjestys) {
+        this.nimi = nimi;
+        this.tulostaulukko = new Tulostaulukko();
+        this.enimmaismaaraPisteita = enimmaismaaraPisteita;
+        this.laskevaPisteytysjarjestys = laskevaPisteytysjarjestys;
+    }
+
+    public String PalautaNimi() {
+        return nimi;
+    }
+
+    public int PalautaEnimmaispisteet() {
+        return enimmaismaaraPisteita;
+    }
+
+    public boolean OnkoPisteytysjarjestysLaskeva() {
+        return laskevaPisteytysjarjestys;
+    }
+
+    public ArrayList<Urheilija> PalautaUrheilijat() {
+        return tulostaulukko.PalautaUrheilijat();
+    }
+
+    public void LisaaTulos(Urheilija urheilija, Double tulos) {
+        tulostaulukko.LisaaTulos(urheilija, tulos);
+    }
+
+    public boolean LisaaTulos(String nimi, Double tulos) {
+        Urheilija urheilija = tulostaulukko.PalautaUrheilijaNimella(nimi);
+
+        if (urheilija != null) {
+            tulostaulukko.LisaaTulos(urheilija, tulos);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public HashMap<Urheilija, Double> PisteytaUrheilijat() {
+        HashMap<Urheilija, Double> tulokset = new HashMap<>();
+
+        if (laskevaPisteytysjarjestys) {
+            tulokset = tulostaulukko.PalautaUrheilijoidenSuurimmatTulokset();
+        } else {
+            tulokset = tulostaulukko.PalautaUrheilijoidenPienimmatTulokset();
+        }
+
+        HashMap<Object, Double> parametritaulukko = new HashMap<>();
+
+        for(Urheilija urheilija : tulokset.keySet()) {
+            parametritaulukko.put(urheilija, tulokset.get(urheilija));
+        }
+
+        ArrayList<Object> urheilijatJarjestyksessa = Utils.PalautaHashMapinObjektitJarjestyksessa(parametritaulukko, laskevaPisteytysjarjestys);
+
+        HashMap<Urheilija, Double> pistetaulukko = new HashMap<>();
+
+        int i = 0;
+        double n = parametritaulukko.size();
+        for (Object objekti : urheilijatJarjestyksessa) {
+            double pisteet = enimmaismaaraPisteita - ((i / n) * enimmaismaaraPisteita);
+            pistetaulukko.put((Urheilija) objekti, pisteet);
+            i++;
+        }
+
+        return pistetaulukko;
+    }
+
+    public void TulostaTulokset() {
+        System.out.println("Tulokset lajista " + PalautaNimi() + "\n");
+
+        HashMap<Urheilija, Double> pisteytetytUrheilijat = PisteytaUrheilijat();
+
+        if (laskevaPisteytysjarjestys) {
+            System.out.println("VOITTAJA: " + tulostaulukko.PalautaUrheilijaSuurimmallaTuloksella().PalautaNimi());
+        } else {
+            System.out.println("VOITTAJA: " + tulostaulukko.PalautaUrheilijaPienimmallaTuloksella().PalautaNimi());
+        }
+
+        System.out.println();
+
+        if (laskevaPisteytysjarjestys) {
+            int i = 1;
+            for(Urheilija urheilija : pisteytetytUrheilijat.keySet()) {
+                System.out.println(i + ": " + urheilija.PalautaNimi() + ", tulos: " + tulostaulukko.PalautaSuurinTulos(urheilija) + ", pisteet: " + pisteytetytUrheilijat.get(urheilija));
+                i++;
+            }
+        } else {
+            int i = 1;
+            for(Urheilija urheilija : pisteytetytUrheilijat.keySet()) {
+                System.out.println(i + ": " + urheilija.PalautaNimi() + ", tulos: " + tulostaulukko.PalautaPieninTulos(urheilija) + ", pisteet: " + pisteytetytUrheilijat.get(urheilija));
+                i++;
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        return nimi;
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Utils.java b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Utils.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4bf380884ed8563174f7b479b9373abfad0e22a
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise2/tehtava4/Utils.java
@@ -0,0 +1,57 @@
+package fi.utu.tech.ooj.exercise2.tehtava4;
+
+import java.util.*;
+
+public class Utils {
+    public static ArrayList<Object> PalautaHashMapinObjektitJarjestyksessa(HashMap<Object, Double> objektit, Boolean laskevaJarjestys) {
+        Object[] a = objektit.entrySet().toArray();
+
+        Arrays.sort(a, new Comparator() {
+            public int compare(Object o1, Object o2) {
+                return ((Map.Entry<Object, Double>) o2).getValue()
+                        .compareTo(((Map.Entry<Object, Double>) o1).getValue());
+            }
+        });
+
+        ArrayList<Object> palautettavaLista = new ArrayList<>();
+
+        for (Object e : a) {
+            palautettavaLista.add(((Map.Entry<Object, Integer>) e).getKey());
+        }
+
+        if (laskevaJarjestys) {
+            return palautettavaLista;
+        } else {
+            ArrayList<Object> uusiLista = new ArrayList<Object>();
+
+            for(Object objekti1 : palautettavaLista) {
+                Object pieninObjekti = null;
+                Double pieninArvo = Double.MAX_VALUE;
+                for(Object objekti2 : palautettavaLista) {
+                    if (!uusiLista.contains(objekti2)) {
+                        if (objektit.get(objekti2) < pieninArvo) {
+                            pieninArvo = objektit.get(objekti2);
+                            pieninObjekti = objekti2;
+                        }
+                    }
+                }
+
+                if (pieninObjekti != null) {
+                    uusiLista.add(pieninObjekti);
+                }
+            }
+
+            return uusiLista;
+        }
+    }
+
+    public static Urheilija[] MuunnaObjektitaulukkoUrheilijataulukoksi(Object[] objectArray) {
+        Urheilija[] urheilijaArray = new Urheilija[objectArray.length];
+
+        for (int i = 0; i < objectArray.length; i++) {
+            urheilijaArray[i] = (Urheilija) objectArray[i];
+        }
+
+        return urheilijaArray;
+    }
+}
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index a28d3fda3d0ac52988b5dd271cc9f0572a970026..b08c15eeed19e3eca9138f177e8e324fc47c4f20 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -2,4 +2,14 @@ module fi.utu.tech.ooj.exercise2 {
     exports fi.utu.tech.ooj.exercise2;
 
     opens fi.utu.tech.ooj.exercise2;
+    exports fi.utu.tech.ooj.exercise2.tehtava2.tietoyhteys;
+    opens fi.utu.tech.ooj.exercise2.tehtava2.tietoyhteys;
+    exports fi.utu.tech.ooj.exercise2.tehtava1;
+    opens fi.utu.tech.ooj.exercise2.tehtava1;
+    exports fi.utu.tech.ooj.exercise2.tehtava2;
+    opens fi.utu.tech.ooj.exercise2.tehtava2;
+    exports fi.utu.tech.ooj.exercise2.tehtava3;
+    opens fi.utu.tech.ooj.exercise2.tehtava3;
+    exports fi.utu.tech.ooj.exercise2.tehtava4;
+    opens fi.utu.tech.ooj.exercise2.tehtava4;
 }
diff --git a/src/test/java/fi/utu/tech/ooj/exercise2/ActivationTest.java b/src/test/java/fi/utu/tech/ooj/exercise2/ActivationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..341844c137fcfb620fe79baac9e4aa7d24858b5b
--- /dev/null
+++ b/src/test/java/fi/utu/tech/ooj/exercise2/ActivationTest.java
@@ -0,0 +1,56 @@
+package fi.utu.tech.ooj.exercise2;
+
+import fi.utu.tech.ooj.exercise2.tehtava3.Activation;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Random;
+
+/*
+ Jotta metodi toimisi kuin parametrisen ReLU-funktion kuuluu, sen tulee
+ palauttaa x, jos x > 0 ja muussa tapauksessa parametri a * x.
+ Tätä voidaan testata seuraavilla testeillä
+
+ (1) Palauttaako funktio x:n, kun x = 1?
+ (2) Palauttaako funtio a * x, kun x on 0 tai negatiivinen luku ja
+     a satunnaisluku välillä (0.01-1)?
+
+  Testien valossa METODI VAIKUTTAISI TOIMIVAN MÄÄRITTELYNSÄ MUKAAN!
+  */
+class ActivationTest {
+
+    Random random = new Random();
+
+    @Test
+    void testaaParametricReLU() {
+        float x = 0;
+        float a = 0;
+        parametricReLU(x, a);
+    }
+
+    @Test
+    void parametricReLU(float x, float a) {
+        float result = 0;
+
+        x = 0;
+        a = random.nextFloat(0,1);
+
+        result = Activation.parametricReLU(x, a);
+
+        Assertions.assertEquals(result, x * a);
+
+        x = 1;
+
+        result = Activation.parametricReLU(x, a);
+
+        Assertions.assertEquals(result, x);
+
+        for (int i = 0; i < 10; i++) {
+            x = random.nextFloat(-10,0);
+            a = random.nextFloat(0,1);
+            result = Activation.parametricReLU(x,a);
+
+            Assertions.assertEquals(result, x * a);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/fi/utu/tech/ooj/exercise2/CarTest.java b/src/test/java/fi/utu/tech/ooj/exercise2/CarTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..7d72e33454423753a840ec95fd3c338ac9f30aa1
--- /dev/null
+++ b/src/test/java/fi/utu/tech/ooj/exercise2/CarTest.java
@@ -0,0 +1,216 @@
+package fi.utu.tech.ooj.exercise2;
+
+import fi.utu.tech.ooj.exercise2.tehtava3.Car;
+import org.junit.jupiter.api.Test;
+
+import java.time.Year;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/*
+    Luokka Car EI TOIMI TÄYSIN LUOKKAINVARIANTTINSA MUKAAN.
+    Jos asetan setRegistered-metodissa auton rekisteröinnin arvoksi
+    arvon "tosi", ei luokalle muodostu omaa, välilyönnit poislukien
+    vähintään 6-merkkistä rekisteritunnusta. Luokassa voi siis
+    todellisuudessa olla rekisteröitynä auto, jolla ei ole rekisteri-
+    tunnusta. Tämä ei ole luokkainvariantin mukaista, jossa
+    määritellään:
+        ( isRegistered == true && registerNumber.trim.length >=6 )
+
+    Muilta osin yhdeksän kymmenestä testistä menee läpi.
+ */
+
+class CarTest {
+
+    @Test
+    void getManufacturer() {
+    }
+
+    /*
+        Seuraava testimetodi testaa seuraavat asiat:
+            (1) Jos auton valmistajaksi asetetaan tyhjä arvo (null), auton valmistaja ei ole null
+            (2) Jos auton valmistajaksi asetetaan luokkainvariantissa määriteltyä
+            lyhyempi lyhyt merkkijono "W", auton valmistaja ei ole "W"
+            (3) Jos auton valmistajaksi asetetaan "BMW", auton valmistaja on "BMW"
+
+        Virheellisistä parametreistä johtuvat virhetilanteet ohitetaan virhekäsittelynä, sillä
+        silloin testi onnistui.
+     */
+
+    @Test
+    void setManufacturer() {
+        Car car = new Car("Testi", "Testi", 1950);
+        try {
+            car.setManufacturer(null);
+            assertNotEquals(null, car.getManufacturer());
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            car.setManufacturer("W");
+            assertNotEquals("W", car.getManufacturer());
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            car.setManufacturer("BMW");
+            assertEquals("BMW", car.getManufacturer());
+        } catch (IllegalArgumentException e) {
+        }
+    }
+
+    @Test
+    void getModel() {
+    }
+
+    /*
+       Seuraava testimetodi testaa seuraavat asiat:
+           (1) Jos auton malliksi malliksi tyhjä arvo (null), auton malli ei ole null
+           (2) Jos auton malliksi asetetaan luokkainvariantissa määritettyä lyhyempi
+           merkkijono "A", auton malli ei ole "A"
+           (3) Jos auton malliksi asetetaan "A1", auton malli on "A1"
+
+       Virheellisistä parametreistä johtuvat virhetilanteet ohitetaan virhekäsittelynä, sillä
+       silloin testi onnistui.
+    */
+    @Test
+    void setModel() {
+        Car car = new Car("Testi", "Testi", 1950);
+        try {
+            car.setModel(null);
+            assertNotEquals(null, car.getModel());
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            car.setModel("A");
+            assertNotEquals("A", car.getModel());
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            car.setModel("A1");
+            assertEquals("A1", car.getModel());
+        } catch (IllegalArgumentException e) {
+        }
+    }
+
+    @Test
+    void getModelYear() {
+    }
+
+    /*
+      Seuraava testimetodi testaa seuraavat asiat:
+          (1) Jos auton valmistusvuodeksi asetetaan tyhjä arvo (null), auton valmistusvuosi ei ole null
+          (2) Jos auton valmistusvuodeksi asetetaan luokkainvariantissa määritetyn alueen ulkopuolelta
+          vuosi 1800, auton valmistuvuosi ei ole 1800
+          (3) Jos auton valmistusvuodeksi asetaan luokkainvariantissa määritetyn alueen ulkopuolelta
+          vuosi (tämä vuosi) + 1, auton valmistusvuosi ei ole (tämä vuosi) + 1
+
+      Virheellisistä parametreistä johtuvat virhetilanteet ohitetaan virhekäsittelynä, sillä
+      silloin testi onnistui.
+   */
+    @Test
+    void setModelYear() {
+        Car car = new Car("Testi", "Testi", 1950);
+        try {
+            car.setModelYear(null);
+            assertNotEquals(null, car.getModelYear());
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            car.setModelYear(1800);
+            assertNotEquals(1800, car.getModelYear());
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            car.setModelYear(Year.now().getValue() + 1);
+            assertNotEquals(Year.now().getValue() + 1, car.getModelYear());
+        } catch (IllegalArgumentException e) {
+        }
+    }
+
+    @Test
+    void getRegistered() {
+    }
+
+    /*
+    Seuraava testimetodi testaa seuraavat asiat:
+        (1) Jos auton rekisteröinnin arvoksi asetetaan tyhjä arvo (null), auton rekisteröinnin arvo ei ole null
+        (2) Jos auton rekisteröinnin arvoksi asetaan tosi arvo, auton rekisterinumerossa on oltava
+        vähintään 6 merkkiä poislukien välilyönnit
+        (3) Jos auton rekisteröinnin arvoksi asetetaan epätosi arvo, auton rekisterinumeron on oltava tyhjä merkkijono
+
+    Virheellisistä parametreistä johtuvat virhetilanteet ohitetaan virhekäsittelynä, sillä
+    silloin testi onnistui.
+ */
+    @Test
+    void setRegistered() {
+        Car car = new Car("Testi", "Testi", 1950);
+        try {
+            car.setRegistered(null);
+            assertNotEquals(null, car.getRegistered());
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            car.setRegistered(true);
+            assertTrue(car.getRegisterNumber().trim().length() >= 6);
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            car.setRegistered(false);
+            assertTrue(car.getRegisterNumber().isBlank());
+        } catch (IllegalArgumentException e) {
+        }
+    }
+
+    @Test
+    void getRegisterNumber() {
+    }
+
+    /*
+      Seuraava testimetodi testaa seuraavat asiat:
+        (1) Jos auton rekisterinumeron arvoksi asetetaan tyhjä arvo (null), auton rekisterinumeron arvo ei ole null
+        (2) Jos auton rekisterinumeroksi asetaan luokkainvariantissa sallitun kokoinen merkkijono, jonka pituus
+        on 11 merkkiä, auton rekisteröinnin arvoksi asetetaan tosi
+        (3) Jos auton rekisterinumeroksi asetetaan luokkainvariantissa määritellyn vastainen merkkijono, jonka pituus
+        on 5 merkkiä, JOKO auton rekisteröinnin arvo on epätosi TAI auton rekisterinumero on muutettu muotoon, jossa
+        sen pituus on luokkainvariantissa määritellyn mukainen >= 6 merkkiä, poislukien välilyönnit
+        (4) Jos auton rekisterinumeroksi asetetaan tyhjä merkkijono, auton rekisteröinnin arvoksi asetetaan
+        epätosi arvo
+
+        Virheellisistä parametreistä johtuvat virhetilanteet ohitetaan virhekäsittelynä, sillä
+        silloin testi onnistui.
+    */
+    @Test
+    void setRegisterNumber() {
+        Car car = new Car("Testi", "Testi", 1950);
+        try {
+            car.setRegisterNumber(null);
+            assertNotEquals(null, car.getRegisterNumber());
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            car.setRegisterNumber("BMW12345678");
+            assertTrue(car.getRegistered());
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            car.setRegisterNumber("BMW12");
+            assertTrue(!car.getRegistered() || car.getRegisterNumber().trim().length() >= 6);
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            car.setRegisterNumber("");
+            assertFalse(car.getRegistered());
+        } catch (IllegalArgumentException e) {
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/fi/utu/tech/ooj/exercise2/MergeSortTest.java b/src/test/java/fi/utu/tech/ooj/exercise2/MergeSortTest.java
deleted file mode 100644
index eb52e35dd16f8d226071e22aa10c700f57000f43..0000000000000000000000000000000000000000
--- a/src/test/java/fi/utu/tech/ooj/exercise2/MergeSortTest.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package fi.utu.tech.ooj.exercise2;
-
-public class MergeSortTest {
-
-}
diff --git a/src/test/java/fi/utu/tech/ooj/exercise2/PalindromeTest.java b/src/test/java/fi/utu/tech/ooj/exercise2/PalindromeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0df9c20e4aa83b572b1811a131b9ab5b6469e393
--- /dev/null
+++ b/src/test/java/fi/utu/tech/ooj/exercise2/PalindromeTest.java
@@ -0,0 +1,43 @@
+package fi.utu.tech.ooj.exercise2;
+
+import fi.utu.tech.ooj.exercise2.tehtava3.Palindrome;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/*
+    Alla olevat metodit testaavat Palindrome-luokan
+    convertToPalindrome-metodin palauttamien merkkijonojen
+    osalta niiden palindromisuuden. Toisin sanoen, ovatko merkkijonon
+    merkit n ja (merkkijonon pituus) - n merkit samat. Jos ovat, testi
+    menee läpi, jos eivät, testi ei mene läpi.
+
+    Merkkijonot eivät vaikuta testin perusteella olevan liittymäkohdiltaan palindromisia,
+    joten METODI EI TOIMI OIKEIN.
+ */
+class PalindromeTest {
+
+
+    @Test
+    void testaaConvertToPalindrome() {
+        String merkkijono = "Tämä on testimerkkijono";
+        convertToPalindrome(merkkijono);
+    }
+
+    @Test
+    void convertToPalindrome(String str) {
+        String palindromi = Palindrome.convertToPalindrome(str);
+        int palindrominKeskiosa = (palindromi.length() / 2);
+        System.out.println(palindromi);
+        int downIndex = 0;
+        int upIndex = palindromi.length() - 1;
+
+        for (int x = 0; x < palindrominKeskiosa + 1; x++) {
+            char downCharacter = palindromi.charAt(downIndex);
+            char upCharacter = palindromi.charAt(upIndex);
+            Assertions.assertEquals(downCharacter, upCharacter);
+
+            downIndex++;
+            upIndex--;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/fi/utu/tech/ooj/exercise2/SortingTest.java b/src/test/java/fi/utu/tech/ooj/exercise2/SortingTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..045816f6e708ab222a950e26c9ff24e2bccacee6
--- /dev/null
+++ b/src/test/java/fi/utu/tech/ooj/exercise2/SortingTest.java
@@ -0,0 +1,128 @@
+package fi.utu.tech.ooj.exercise2;
+
+import fi.utu.tech.ooj.exercise2.tehtava3.Sorting;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Random;
+
+/* mergeSort-metodin osalta voisi selvittää seuraava:
+        taulukosta poimitaan kaksi satunnaista indeksiä. Onhan jokaisen indeksin kanssa seuraava väite tosi:
+    (jos indeksi(1) > indeksi(2) -> luku(1) > luku(2); jos ei, luku(1) <= luku(2));
+
+    merge-,metodin osalta voisi selvittää
+    (1) onhan palautetussa taulukossa yhtä monta alkiota kuin annetussa taulukossa
+    (2) käsitellään kahta eri yhdistettyä taulukkoa keskenään vertaillen saman indeksin saaneita lukuja taulukoissa:
+        (jos taulukko1.arvoIndeksissä(1) < taulukko2.arvoIndeksissä(1) -> yhdistettyTaulukko.alkionIndeksi(taulukko1.arvoIndeksissä(1)) <= taulukko2.arvoIndeksissä(1);
+        jos ei, yhdistettyTaulukko.alkionIndeksi(taulukko1.arvoIndeksissä(1)) > taulukko2.arvoIndeksissä(1))
+
+    Testien perusteella LAJITTELU VAIKUTTAA TOIMIVAN OIKEIN!
+ */
+class SortingTest {
+
+    Random random = new Random();
+
+    @Test
+    void testaaMergeSort() {
+        int taulukonKoko = random.nextInt(1,50);
+        int[] taulukko = new int[taulukonKoko];
+
+        for (int i = 0; i < taulukko.length; i++) {
+            taulukko[i] = random.nextInt(101) - 50;
+        }
+
+        mergeSort(taulukko);
+    }
+    @Test
+    void mergeSort(int[] taulukko) {
+
+        Sorting.mergeSort(taulukko);
+        // valitaan 10 kertaa kaksi satunnaista alkiota
+        for (int x = 0; x < 10; x++) {
+            int[][] testiarvot = new int[2][2];
+            for (int i = 0; i < 2; i++) {
+                testiarvot[i][0] = random.nextInt(0, taulukko.length);
+                testiarvot[i][1] = taulukko[testiarvot[i][0]];
+            }
+
+            if (testiarvot[0][0] > testiarvot[1][0]) {
+                // ensimmäisen indeksi suurempi eli ensimmäisen pitäisi olla suurempi
+                Assertions.assertTrue(testiarvot[0][1] > testiarvot[1][1]);
+            } else if (testiarvot[0][0] == testiarvot[1][0]) {
+                // indeksit samankokoisia eli sama luku!
+                Assertions.assertTrue(testiarvot[0][1] == testiarvot[1][1]);
+            } else if (testiarvot[0][0] < testiarvot[1][0]) {
+                // ensimmäisen indeksi pienempi eli ensimmäisen pitäisi olla pienempi
+                Assertions.assertTrue(testiarvot[0][1] < testiarvot[1][1]);
+            }
+        }
+    }
+
+    @Test
+    void testaaMerge() {
+        int taulukon1Koko = random.nextInt(1,5);
+        int[] taulukko1 = new int[taulukon1Koko];
+        int taulukon2Koko = random.nextInt(1,5);
+        int[] taulukko2 = new int[taulukon2Koko];
+
+        int[] yhdistettyTaulukko = new int[taulukon1Koko + taulukon2Koko];
+
+        for (int i = 0; i < taulukko1.length; i++) {
+            taulukko1[i] = random.nextInt(101);
+            yhdistettyTaulukko[i] = taulukko1[i];
+        }
+
+        for (int i = 0; i < taulukko2.length; i++) {
+            taulukko2[i] = random.nextInt(101) - 50;
+            yhdistettyTaulukko[taulukon1Koko + i] = taulukko2[i];
+        }
+
+        merge(yhdistettyTaulukko, taulukko1, taulukko2, taulukon1Koko, yhdistettyTaulukko.length - taulukon1Koko);
+    }
+
+    /*
+         (jos taulukko1.arvoIndeksissä(1) < taulukko2.arvoIndeksissä(1) -> yhdistettyTaulukko.alkionIndeksi(taulukko1.arvoIndeksissä(1)) <= yhdistettyTaulukko.alkionIndeksi(taulukko2.arvoIndeksissä(1));
+        jos ei, yhdistettyTaulukko.alkionIndeksi(taulukko1.arvoIndeksissä(1)) > taulukko2.arvoIndeksissä(1))
+        && (yhdistettyTaulukko.pituusMetodinAlussa() == yhdistettyTaulukko.pituusMetodinLopussa())
+     */
+    @Test
+    void merge(int[] a, int[] l, int[] r, int left, int right) {
+
+        int pituusAlussa = a.length;
+
+        Sorting.merge(a, l, r, left, right);
+
+        int lyhyempiPituus = 0;
+
+        if (l.length < r.length) {
+            lyhyempiPituus = l.length;
+        } else {
+            lyhyempiPituus = r.length;
+        }
+
+        for (int i = 0; i < lyhyempiPituus; i++) {
+            int vasTaulukonArvo = l[i];
+            int oikTaulukonArvo = r[i];
+
+            if (vasTaulukonArvo < oikTaulukonArvo) {
+                Assertions.assertTrue(etsiArvonIndeksi(a, vasTaulukonArvo) < etsiArvonIndeksi(a, oikTaulukonArvo));
+            } else if (vasTaulukonArvo == oikTaulukonArvo) {
+                // koodin mukaan tässä asetetaan ensiksi vasemmanpuoleinen arvo, vaikka päätös on mielivaltainen
+                Assertions.assertTrue(etsiArvonIndeksi(a, vasTaulukonArvo) < etsiArvonIndeksi(a, oikTaulukonArvo));
+            } else if (vasTaulukonArvo > oikTaulukonArvo) {
+                Assertions.assertTrue(etsiArvonIndeksi(a, vasTaulukonArvo) > etsiArvonIndeksi(a, oikTaulukonArvo));
+            }
+        }
+
+        Assertions.assertEquals(a.length, pituusAlussa);
+    }
+
+    int etsiArvonIndeksi(int[] taulukko, int etsittavaArvo) {
+        for (int i = 0; i < taulukko.length; i++) {
+            if (taulukko[i] == etsittavaArvo) {
+                return i;
+            }
+        }
+        return -1;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/fi/utu/tech/ooj/exercise2/tehtava4/KayttoliittymaTest.java b/src/test/java/fi/utu/tech/ooj/exercise2/tehtava4/KayttoliittymaTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d3d382531c7992d7d7b431601b52a7c64555367
--- /dev/null
+++ b/src/test/java/fi/utu/tech/ooj/exercise2/tehtava4/KayttoliittymaTest.java
@@ -0,0 +1,10 @@
+package fi.utu.tech.ooj.exercise2.tehtava4;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class KayttoliittymaTest {
+
+
+}
\ No newline at end of file
diff --git a/src/test/java/fi/utu/tech/ooj/exercise2/tehtava4/UtilsTest.java b/src/test/java/fi/utu/tech/ooj/exercise2/tehtava4/UtilsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d4e70bff3e89eecc3712ad4ddaee27649b2e12c
--- /dev/null
+++ b/src/test/java/fi/utu/tech/ooj/exercise2/tehtava4/UtilsTest.java
@@ -0,0 +1,24 @@
+package fi.utu.tech.ooj.exercise2.tehtava4;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import static org.junit.jupiter.api.Assertions.*;
+class UtilsTest {
+
+    @Test
+    void jarjestaHashMap() {
+        HashMap<Object, Double> tulokset = new HashMap<>();
+        tulokset.put(new Urheilija("Laura"), 1000.0);
+        tulokset.put(new Urheilija("Mikko"), 150.0);
+        tulokset.put(new Urheilija("Bjarne"), 750.0);
+
+        ArrayList<Object> lista = Utils.PalautaHashMapinObjektitJarjestyksessa(tulokset, true);
+
+        for(Object objekti : lista) {
+            System.out.println(objekti + " " + tulokset.get(objekti));
+        }
+    }
+}
\ No newline at end of file