diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..6e2af67a588d0444df6806e258db0fabffc1c81a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,400 @@
+
+# Created by https://www.toptal.com/developers/gitignore/api/python,linux,windows,macos,pycharm,emacs,visualstudiocode,vim
+# Edit at https://www.toptal.com/developers/gitignore?templates=python,linux,windows,macos,pycharm,emacs,visualstudiocode,vim
+
+### Emacs ###
+# -*- mode: gitignore; -*-
+*~
+\#*\#
+/.emacs.desktop
+/.emacs.desktop.lock
+*.elc
+auto-save-list
+tramp
+.\#*
+
+# Org-mode
+.org-id-locations
+*_archive
+
+# flymake-mode
+*_flymake.*
+
+# eshell files
+/eshell/history
+/eshell/lastdir
+
+# elpa packages
+/elpa/
+
+# reftex files
+*.rel
+
+# AUCTeX auto folder
+/auto/
+
+# cask packages
+.cask/
+dist/
+
+# Flycheck
+flycheck_*.el
+
+# server auth directory
+/server/
+
+# projectiles files
+.projectile
+
+# directory configuration
+.dir-locals.el
+
+# network security
+/network-security.data
+
+
+### Linux ###
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### PyCharm ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn.  Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### PyCharm Patch ###
+# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
+
+# *.iml
+# modules.xml
+# .idea/misc.xml
+# *.ipr
+
+# Sonarlint plugin
+# https://plugins.jetbrains.com/plugin/7973-sonarlint
+.idea/**/sonarlint/
+
+# SonarQube Plugin
+# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
+.idea/**/sonarIssues.xml
+
+# Markdown Navigator plugin
+# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
+.idea/**/markdown-navigator.xml
+.idea/**/markdown-navigator-enh.xml
+.idea/**/markdown-navigator/
+
+# Cache file creation bug
+# See https://youtrack.jetbrains.com/issue/JBR-2257
+.idea/$CACHE_FILE$
+
+# CodeStream plugin
+# https://plugins.jetbrains.com/plugin/12206-codestream
+.idea/codestream.xml
+
+### Python ###
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+pip-wheel-metadata/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+pytestdebug.log
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+doc/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+.python-version
+
+# pipenv
+#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+#   However, in case of collaboration, if having platform-specific dependencies or dependencies
+#   having no cross-platform support, pipenv may install dependencies that don't work, or not
+#   install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+pythonenv*
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# profiling data
+.prof
+
+### Vim ###
+# Swap
+[._]*.s[a-v][a-z]
+!*.svg  # comment out if you don't need vector files
+[._]*.sw[a-p]
+[._]s[a-rt-v][a-z]
+[._]ss[a-gi-z]
+[._]sw[a-p]
+
+# Session
+Session.vim
+Sessionx.vim
+
+# Temporary
+.netrwhist
+# Auto-generated tag files
+tags
+# Persistent undo
+[._]*.un~
+
+### VisualStudioCode ###
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+### VisualStudioCode Patch ###
+# Ignore all local history of files
+.history
+.ionide
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# End of https://www.toptal.com/developers/gitignore/api/python,linux,windows,macos,pycharm,emacs,visualstudiocode,vim
diff --git a/lab1/lab1.py b/lab1/lab1.py
new file mode 100644
index 0000000000000000000000000000000000000000..d1906dbcdb46818067d3a8e2bbefb75255336787
--- /dev/null
+++ b/lab1/lab1.py
@@ -0,0 +1,4 @@
+# Här kan du skriva programkod för labb 1.
+# Du behöver strikt sett inte lämna in koden för denna labb,
+# men vi lägger ändå in en förberedd fil för att förenkla.
+# Om du har skrivit labbkoden någon annanstans är det också OK.
diff --git a/lab2/lab2.py b/lab2/lab2.py
new file mode 100644
index 0000000000000000000000000000000000000000..5a532b1c6b37d4f4f0b3c66eb12d14964de48da4
--- /dev/null
+++ b/lab2/lab2.py
@@ -0,0 +1,4 @@
+# Här kan du skriva programkod för labb 2.
+# Du behöver strikt sett inte lämna in koden för denna labb,
+# men vi lägger ändå in en förberedd fil för att förenkla.
+# Om du har skrivit labbkoden någon annanstans är det också OK.
diff --git a/lab3.py b/lab3.py
new file mode 100644
index 0000000000000000000000000000000000000000..d903bec180897302a9d49aac5313e6c466f6f346
--- /dev/null
+++ b/lab3.py
@@ -0,0 +1,22 @@
+def factorial(k):
+    if k == 1:
+        return 1
+    else:
+        return k * factorial(k-1)
+
+
+def c(x,y):
+    if x == y:
+        return x 
+    else:
+        return x* c(x+1,y)
+    
+       
+      
+def choose(n,k):
+    if k == 1 or k == (n-1):
+        return n
+    elif k < (n-k):
+        return c((n-k)+1,n)//factorial(k)
+    elif k > (n-k):
+        return c((n-k)+1,n)//factorial(k)
diff --git a/lab3/lab3.py b/lab3/lab3.py
new file mode 100644
index 0000000000000000000000000000000000000000..a767a5448ecae3f4c4b438fcccd341293242b8f3
--- /dev/null
+++ b/lab3/lab3.py
@@ -0,0 +1,98 @@
+# Här kan du skriva programkod för labb 3.
+#
+# Programkoden kan t.ex. också delas upp i flera filer, men
+# i så fall behöver du se till att de adderas till Git.
+
+def new_board():                                             #new_board skapar en tom dic
+    return {}
+     
+
+def is_free(b,x,y):                                          #is_free kollar om (x,y) platsen i b är ledig eller inte
+    if (x,y) in b:
+        return False
+    else:
+        return True
+
+def place_piece(b,x,y,spelare):                                    #place_piece placerar s i b[x,y] och returnerar om den är ledig eller inte 
+    if is_free(b,x,y) == True:
+        b[x,y] = spelare
+        return True
+    else:
+        return False
+
+def get_piece(b,x,y):                                        #get_piece hämtar en b[x,y] värdet om det finns ett annars returnerar False
+    if is_free(b,x,y) == False:
+        return b[x,y]
+    else:
+        return False
+
+def remove_piece(b,x,y):                                     #remove_piece tar bort värdet b[x,y] om det finns nån
+    if is_free(b,x,y) == False:
+         b.pop((x,y))
+         return True
+    else:
+        return False
+
+def move_piece(b,x,y,ny_pos_x,ny_pos_y):                                   #move_piece flyttar ett b[x,y] till b[c,t] och tar bort b[x,y]
+    g = get_piece(b,x,y)
+    if is_free(b,x,y) == False:
+        place_piece(b,ny_pos_x,ny_pos_y,g)
+        remove_piece(b,x,y)
+        return True
+    else:
+        return False
+
+def count(b,riktning,x,spelare):                                         
+    p=0
+    if riktning == "column":
+        for i in b.keys():
+            if i[0] == x:
+                if get_piece(b,i[0],i[1]) == spelare:
+                    p += 1
+    elif riktning == "row":
+        for i in b.keys():
+            if i[1] == x:
+                if get_piece(b,i[0],i[1]) == spelare:
+                    p += 1
+    return p
+            
+import math         
+def nearest_piece(b,x,y):
+    a = math.inf
+    n = 0
+    for k in b:
+        if (((x-k[0])**2 + (y-k[1])**2)**0.5 < a):
+            a = ((x-k[0])**2 + (y-k[1])**2)**0.5
+            n = (k[0],k[1])
+    return n
+        
+
+
+
+
+def factorial(k):
+    if k == 1:
+        return 1
+    else:
+        return k * factorial(k-1)
+
+    
+
+    
+    
+def c(x,y):
+    if x == y:
+        return x 
+    else:
+        return x* c(x+1,y)
+
+
+def choose(n,k):
+    if k == 1 or k == (n-1):
+        return n
+    elif n == k:
+        return 1
+    elif k < (n-k):
+        return c((n-k)+1,n)//factorial(k)
+    elif k > (n-k):
+        return c((n-k)+1,n)//factorial(k)
diff --git a/lab3/lab3a.py b/lab3/lab3a.py
new file mode 100644
index 0000000000000000000000000000000000000000..7b654988d16fcfbfb478c1b356c19b6d8974b373
--- /dev/null
+++ b/lab3/lab3a.py
@@ -0,0 +1,61 @@
+def new_board():                                             #new_board skapar en tom dic
+    return {}
+     
+
+def is_free(board,x,y):                                          #is_free kollar om (x,y) platsen i b är ledig eller inte
+    if (x,y) in board:
+        return False
+    else:
+        return True
+
+def place_piece(board,x,y,s):                                    #place_piece placerar s i b[x,y] och returnerar om den är ledig eller inte 
+    if is_free(board,x,y) == True:
+        board[x,y] = s
+        return True
+    else:
+        return False
+
+def get_piece(board,x,y):                                        #get_piece hämtar en b[x,y] värdet om det finns ett annars returnerar False
+    if is_free(board,x,y) == False:
+        return board[x,y]
+    else:
+        return False
+
+def remove_piece(board,x,y):                                     #remove_piece tar bort värdet b[x,y] om det finns nån
+    if is_free(board,x,y) == False:
+         board.pop((x,y))
+         return True
+    else:
+        return False
+
+def move_piece(board,x,y,c,t):                                   #move_piece flyttar ett b[x,y] till b[c,t] och tar bort b[x,y]
+    if is_free(board,x,y) == False and is_free(board,c,t) == True:
+        place_piece(board,c,t,get_piece(board,x,y))
+        remove_piece(board,x,y)
+        return True
+    else:
+        return False
+
+def count(board,o,x,s):                                          #count r
+    z=0
+    if o == "column":
+        for i in board.keys():
+            if i[0] == x:
+                if get_piece(board,i[0],i[1]) == s:
+                    z = z + 1
+    elif o == "row":
+        for i in board.keys():
+            if i[1] == x:
+                if get_piece(board,i[0],i[1]) == s:
+                    z = z + 1
+    return z
+            
+import math         
+def nearest_piece(board,x,y):
+    a = math.inf
+    n = 0
+    for k in board:
+        if (((x-k[0])**2 + (y-k[1])**2)**0.5 < a):
+            a = ((x-k[0])**2 + (y-k[1])**2)**0.5
+            n = (k[0],k[1])
+    return n
diff --git a/lab3/lab3b.py b/lab3/lab3b.py
new file mode 100644
index 0000000000000000000000000000000000000000..7740ca35bf48f75a6142275ba8f08f3922930a20
--- /dev/null
+++ b/lab3/lab3b.py
@@ -0,0 +1,21 @@
+def factorial(k):
+    if k == 1:
+        return 1
+    else:
+        return k * factorial(k-1)        
+    
+def range_product(x,y):
+    if x == y:
+        return x 
+    else:
+        return x* range_product(x+1,y)
+
+def choose(n,k):
+    if k == 1 or k == (n-1):
+        return n
+    elif n == k:
+        return 1
+    elif k < (n-k):
+        return range_product((n-k)+1,n)//factorial(k)
+    elif k > (n-k):
+        return range_product((n-k)+1,n)//factorial(k)
diff --git a/lab3/test3.py b/lab3/test3.py
new file mode 100644
index 0000000000000000000000000000000000000000..40de413ac55964fdaa71651fc39bc396bb6131e1
--- /dev/null
+++ b/lab3/test3.py
@@ -0,0 +1,274 @@
+#!/usr/bin/env python3
+"""
+Unit testing with asserts for lab 3
+
+Note:
+If you have downloaded the scripts from the website it might not
+have the access right. To solve this run:
+$ chmod +x <path_to_test_3.py>
+
+Usage (case insensitive for parameters to --test):
+$ ./test_3.py --test a <path_to_lab>
+to test lab 3A
+$ ./test_3.py --test b <path_to_lab>
+to test lab 3B
+to test whole lab 3
+
+Initial version by Erik Hansson <erik.b.hansson@liu.se>
+Updated for TDDE23 by Frans Skarman <frask812@student.liu.se>
+
+Changelog:
+ * 18/12-2019: Follow PEP8/PEP257, made it slightly easier to read
+ * 9/9-2017: Changed isfree to is_free to match instructions, and the
+   call to choose(1000, 800)
+ * 31/8-2016: Updated the printed traceback in case that the given file
+   can not be imported.
+"""
+
+from argparse import ArgumentParser
+from importlib.machinery import SourceFileLoader
+from traceback import format_exc
+import sys
+
+
+def test_board():
+    """
+    Tests the board functions
+    """
+    extra_printout = (
+        "\nSee test code line 140 for which functions that "
+        + "have been called before."
+    )
+    board = lab3.new_board()
+    res = lab3.place_piece(board, 500, 100, "spelare1")
+    assert res is True, (
+        'place_piece(500, 100, "spelare1"): expected True got '
+        + str(res)
+        + extra_printout
+    )
+
+    board = lab3.new_board()
+    res = lab3.is_free(board, 500, 100)
+    assert res is True, (
+        "is_free(500, 100): expected True got "
+        + str(res)
+        + extra_printout
+    )
+
+    res = lab3.place_piece(board, 500, 100, "spelare1")
+    assert res is True, (
+        'place_piece(500, 100, "spelare1"): expected True got '
+        + str(res)
+        + extra_printout
+    )
+
+    res = lab3.place_piece(board, 1, 100, "spelare2")
+    assert res is True, (
+        'place_piece(1, 100, "spelare2"): expected True got '
+        + str(res)
+        + extra_printout
+    )
+
+    res = lab3.place_piece(board, 500, 100, "spelare2")
+    assert res is False, (
+        'place_piece(500, 100, "spelare2"): expected False got '
+        + str(res)
+        + extra_printout
+    )
+
+    res = lab3.place_piece(board, 500, 200, "spelare2")
+    assert res is True, (
+        'place_piece(500, 200, "spelare2"): expected True got '
+        + str(res)
+        + extra_printout
+    )
+
+    res = lab3.nearest_piece(board, 500, 200)
+    assert res == (500, 200), (
+        "nearest_piece(500, 200): expected (500, 200) got "
+        + str(res)
+        + extra_printout
+    )
+
+    res = lab3.is_free(board, 500, 100)
+    assert res is False, (
+        "is_free(500, 100): expected False got "
+        + str(res)
+        + extra_printout
+    )
+
+    res = lab3.get_piece(board, 500, 100)
+    assert res == "spelare1", (
+        'get_piece(500, 100): expected "spelare1" got '
+        + str(res)
+        + extra_printout
+    )
+
+    res = lab3.get_piece(board, 666, 666)
+    assert res is False, (
+        "get_piece(666, 666): expected False got "
+        + str(res)
+        + extra_printout
+    )
+
+    res = lab3.remove_piece(board, 500, 100)
+    assert res is True, (
+        "remove_piece(500, 100): expected True got " + str(res) + extra_printout
+    )
+
+    res = lab3.remove_piece(board, 1, 1)
+    assert res is False, (
+        "remove_piece(1, 1): expected False got " + str(res) + extra_printout
+    )
+
+    res = lab3.is_free(board, 500, 100)
+    assert res is True, (
+        "is_free(500, 100): expected True got "
+        + str(res)
+        + extra_printout
+    )
+
+    res = lab3.move_piece(board, 500, 200, 500, 100)
+    assert res is True, (
+        "move_piece(500, 200, 500, 100): expected True got " + str(res) + extra_printout
+    )
+
+    res = lab3.get_piece(board, 500, 100)
+    assert res == "spelare2", (
+        'get_piece(500, 100): expected "spelare2" got '
+        + str(res)
+        + extra_printout
+    )
+
+    res = lab3.count(board, "column", 500, "spelare2")
+    assert res == 1, (
+        'count("column", 500, "spelare2"): expected 1 got '
+        + str(res)
+        + extra_printout
+    )
+
+    res = lab3.count(board, "row", 100, "spelare2")
+    assert res == 2, (
+        'count("row", 100, "spelare2"): expected 2 got '
+        + str(res)
+        + extra_printout
+    )
+
+    res = lab3.nearest_piece(board, 500, 105)
+    assert res == (500, 100), (
+        "nearest_piece(500, 105): expected (500, 100) got "
+        + str(lab3.nearest_piece(board, 500, 105))
+        + extra_printout
+    )
+
+    board = lab3.new_board()
+    res = lab3.nearest_piece(board, 500, 100)
+    assert res in (False, ()), (
+        "nearest_piece(500, 100): expected False or () got "
+        + str(lab3.nearest_piece(board, 500, 100))
+        + extra_printout
+    )
+
+
+def test_choose():
+    """
+    Tests the choose function
+    """
+    assert lab3.choose(5, 3) == 10, "choose(5, 3): expected 10 got " + str(
+        lab3.choose(5, 3)
+    )
+
+    assert lab3.choose(1000, 1) == 1000, "choose(1000, 1): expected 1000 got " + str(
+        lab3.choose(1000, 1)
+    )
+
+    assert lab3.choose(52, 5) == 2598960, "choose(52, 5): expected 2598960 got " + str(
+        lab3.choose(52, 5)
+    )
+
+    assert (
+        lab3.choose(1000, 4) == 41417124750
+    ), "choose(1000, 4): expected 41417124750 got " + str(lab3.choose(1000, 4))
+
+    result = lab3.choose(1000, 800)
+    assert result == int(
+        "6617155560659303656271633461324588318973217030176386"
+        "6936478813470889179595672641105780128558391316378180"
+        "6953211915554723373931451847059830252175887712457307"
+        "5476493541354606192963838829578971618896362805771558"
+        "89117185"
+    ), (
+        "choose(1000, 800): expected 66171555606593036562716334613245883189732\
+        1703017638669364788134708891795956726411057801285583913163781806953211\
+        9155547233739314518470598302521758877124573075476493541354606192963838\
+        82957897161889636280577155889117185 got "
+        + str(result)
+    )
+
+    assert (
+        lab3.choose(1000, 999) == 1000
+    ), "choose(1000, 999): expected 1000 got " + str(lab3.choose(1000, 999))
+
+    assert lab3.choose(5, 5) == 1, "choose(5, 5): expected 1 got " + str(
+        lab3.choose(5, 5)
+    )
+
+    assert lab3.choose(0, 0) == 1, "choose(1, 1): expected 1 got " + str(
+        lab3.choose(0, 0)
+    )
+
+
+if __name__ == "__main__":
+    arg_parser = ArgumentParser()
+    arg_parser.add_argument(
+        "--test", choices=["a", "A", "b", "B"], default="", required=False
+    )
+    arg_parser.add_argument("file")
+    args = arg_parser.parse_args()
+    if args.file.rfind("/") != -1:
+        sys.path.append(args.file[: args.file.rfind("/")])
+        potential_name = args.file[args.file.rfind("/") + 1 :]
+    else:
+        sys.path.append(".")
+        potential_name = args.file
+    if potential_name.rfind("."):
+        name = potential_name[: potential_name.rfind(".")]
+    else:
+        name = potential_name
+    try:
+        lab3 = SourceFileLoader(name, args.file).load_module()
+    except FileNotFoundError:
+        print("Could not import lab: " + args.file)
+        print("See traceback for further information:")
+        print()
+        stack_trace = format_exc().split("\n")
+        importlib_has_started = False
+        importlib_has_ended = False
+        for line in stack_trace:
+            if (
+                not importlib_has_ended
+                and importlib_has_started
+                and line.lstrip().startswith("File")
+                and "importlib" not in line
+            ):
+                importlib_has_ended = True
+            if importlib_has_ended:
+                print(line)
+            elif (
+                not importlib_has_started
+                and line.lstrip().startswith("File")
+                and "importlib" in line
+            ):
+                importlib_has_started = True
+        exit(1)
+    if args.test.upper() == "A":
+        test_board()
+    elif args.test.upper() == "B":
+        test_choose()
+    elif args.test == "":
+        test_board()
+        test_choose()
+    else:
+        print("Unknown arguemnt for --test: " + args.test)
+        exit(2)
+    print("The code passed all the tests")
diff --git a/lab4.py b/lab4.py
new file mode 100644
index 0000000000000000000000000000000000000000..f49df19fc9a902b3a031918d00418d2ea9c1c229
--- /dev/null
+++ b/lab4.py
@@ -0,0 +1,40 @@
+def split_it(x):
+    low = []
+    upp = []
+    teckenupp = [' ','|']
+    teckenlow = ['_','.']
+    for y in range(len(x)):
+        if x[y].islower():
+            low.append(x[y])
+        else:
+            for element in teckenlow:
+                if x[y]==element:
+                    low.append(x[y])
+                    
+        if x[y].isupper():
+            upp.append(x[y])
+        else:
+            for element in teckenupp:
+                if x[y]==element:
+                    upp.append(x[y])
+                    
+    return ''.join(low), ''.join(upp)
+                    
+  
+def sppuu(y):
+    if len(y) == 0:
+        return ''
+    elif y[0].isupper() or y[0] == ' ' or y[0] == '|':
+        return y[0] + sppuu(y[1:])
+    return sppuu(y[1:])
+
+def spp(x):
+    if len(x) == 0:
+        return ''
+    elif x[0].islower() or x[0] == '_' or x[0] == '.':
+        return x[0] + spp(x[1:])
+    return spp(x[1:])
+
+def split_rec(x):
+    return spp(x),sppuu(x)
+   
diff --git a/lab4/budskap.py b/lab4/budskap.py
new file mode 100644
index 0000000000000000000000000000000000000000..d5ecc0be6ba4ddddea385df2b178f5df0d9bca92
--- /dev/null
+++ b/lab4/budskap.py
@@ -0,0 +1,7 @@
+test1 = "fTlH-yE((g 48aQnUdI#5eC_K§b 29äB>cRkO7-aW-0sN#i 0>nFeOrX=!_ =!sJöUkM1aP(>_Sh wOiV,lE4aR3_ pTåH_Em jLuA§kZ`aY>_ @tDuO5vGo$r1"
+
+test2 = "vJ4&eO1m _E`äNr #!_S´-dÄ=eR+nD!-nE1eL>?_EtS+e <$sK9#cLoO_Ka lK6(lAaT9;_T;t>a0l1´&a76?r$_15o,m§"
+
+test3 = "WhEa'n<=R_´Ek o§Nm&O_,7 s!So1TmR_%Ae%Nt#@GtE_/Ry`2Sr>! v*;Tä>Od<< eLr44O_Ve0En)|_/+ a6YpOrUi/& lKa9Nf§Ot<Wo$ n1*T_8Ho`7Ec/= hR_?0Uh0La8<EdSe32 _`1Ae%0Nt*Dt _35ShOö2 gDaOn ä,Is|k rAu; s>$F_7UiL_@Le8 n&C_`%Os;Mv6+Må2In+Tg<<Mr=5Ee,Nm+T_&'oSm _0(Wh#HaAl*5Ts e5In5'.M_`? c&>Tl,Ha@5IrNa9(K_Io#$Nc$?Gh5 _5?OlFo!|t tYeOnU_? vWoOr(´Uo34L_)Di>Nn(3'n6@Te?> _=Gm#2Ee;Td§$ _7Ts,HkIö>St! e#Fk3Ra0On=M_ a%At!)Nt!8Y_? hOäTm%1Ht74Ea+R_8; hGo=Un-*Yo)|m´& _<§Ip&4 åJ_UdSa=´Tl% a-+Wr)AöN_Nb=5Ar yTg)Eg4LaL; _Ym7Oe&Un _3§Hd/Oe%8Wt5 _Id-3'r=/Mö j?!FdEe1E_Le73IvNiGg?1|h<% e´(Gt@*Oe,Tr%T,A_ iMn6An`§Ka2En3! _47Yd5´OeU_ k@Uo-NmDmEo%R_SiT_&<Ab)NåDt69|."
+
+test4 = "+Y_8O&d5§(Uon5 A'`LtL7_ §bS5e_T3sI>oLL_ haH(Ar´=dV7E_o+ >Zn´§_OI2$yDo1*uB0E6rsReG4#!l3#f ,7_1*/f´ry.2_y!8`o-u§?;9_lo-5st-_/´§*t4*h8;e_)46w>om/=a;n-78_7o8f$_*y`/o7u3r;_>d`0#7r$§-e>am´@,s,&$<_(;=bu42t1_`3yo;<u_s>5=t`i´0l(l(_1h,@a$4*v3/61e_z>6o=3i#d@ber(0g<-."
diff --git a/lab4/lab4.py b/lab4/lab4.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lab4/lab4a.py b/lab4/lab4a.py
new file mode 100644
index 0000000000000000000000000000000000000000..86a52c0f9c2ef6314824b4b2cdcb942c04bdd6b9
--- /dev/null
+++ b/lab4/lab4a.py
@@ -0,0 +1,41 @@
+  
+def split_it(sträng):
+    """ Splits a string into lowercase letters and uppercase letters itratively  """
+    småbokstäver = []
+    storbokstäver = []
+    tecken = [[' ','|'],['_','.']]
+    
+    for y in range(len(sträng)):
+        if sträng[y].islower():
+            småbokstäver.append(sträng[y])
+        else:
+            for element in tecken[1]:
+                if sträng[y]==element:
+                    småbokstäver.append(sträng[y])
+                    
+        if sträng[y].isupper():
+            storbokstäver.append(sträng[y])
+        else:
+            for element in tecken[0]:
+                if sträng[y]==element:
+                    storbokstäver.append(sträng[y])
+                    
+    return ''.join(småbokstäver), ''.join(storbokstäver)
+                    
+  
+
+def split_rec(sträng):
+    """ Splits a string into lowercase letters and uppercase letters recursively """
+    if len(sträng) == 0:
+        return ('','')
+    småbokstäver,storbokstäver=split_rec(sträng[1:])
+    
+    if sträng[0].islower() or sträng[0] == '_' or sträng[0] == '.':
+        return sträng[0]+småbokstäver,storbokstäver
+    
+    elif sträng[0].isupper() or sträng[0] == ' ' or sträng[0] == '|':
+        return småbokstäver,sträng[0]+storbokstäver
+    
+    else:
+        return småbokstäver,storbokstäver
+    
diff --git a/lab4/lab4b.py b/lab4/lab4b.py
new file mode 100644
index 0000000000000000000000000000000000000000..b5909434f642f385222614f7af66f5579f0ea9b4
--- /dev/null
+++ b/lab4/lab4b.py
@@ -0,0 +1,45 @@
+def interpret(uttryck,tabell):
+    """ Evaluates a logical expression represented as a list or a string."""
+
+    if type(uttryck) == list:
+        if len(uttryck) == 3:
+            if uttryck[1] == "OR":
+                return OR(uttryck,tabell)
+            elif uttryck[1] == "AND":
+                return AND(uttryck,tabell)
+        elif len(uttryck) == 2:
+            return NOT(uttryck,tabell)
+    elif type(uttryck) == str:
+        if uttryck == "true":
+            return "true"
+        elif uttryck == "false":
+            return "false"
+        elif tabell[uttryck] == "true":
+            return "true"
+        else:
+            return "false"
+
+def OR(uttryck,tabell):
+    """ Evaluates a logical expression with the "OR" operator. """
+
+    if interpret(uttryck[0],tabell) == "true" or interpret(uttryck[2],tabell) == "true":
+        return "true"
+    else:
+        return "false"
+
+def AND(uttryck,tabell):
+    """ Evaluates a logical expression with the "AND" operator. """
+
+    if interpret(uttryck[0],tabell) == "true" and interpret(uttryck[2],tabell) == "true":
+            return "true"
+    else:
+        return "false"
+       
+def NOT(uttryck,tabell):
+    """ Evaluates a logical expression with the "NOT" operator. """
+    
+    if interpret(uttryck[1],tabell) == "true":
+        return "false"
+    elif interpret(uttryck[1],tabell) == "false":
+        return "true"
+        
diff --git a/lab4/test4.py b/lab4/test4.py
new file mode 100644
index 0000000000000000000000000000000000000000..b0a78b00baf39e02b6487b56ebc0f645494252c4
--- /dev/null
+++ b/lab4/test4.py
@@ -0,0 +1,248 @@
+#!/usr/bin/env python3
+"""
+Unit testing for lab 4 built on the unittest module.
+
+Note:
+If you have downloaded the scripts from the website it might not
+have the access right. To solve this run:
+$ chmod +x <path_to_test_4.py>
+
+Usage (case insensitive for parameters to --test):
+$ ./test_4.py --test a <path_to_lab>
+to test lab 4A
+$ ./test_4.py --test b <path_to_lab>
+to test lab 4B
+$ ./test_4.py <path_to_lab>
+to test whole lab 4
+
+Initial version by Erik Hansson <erik.b.hansson@liu.se>
+Updated for TDDE23 by Frans Skarman <frask812@student.liu.se>
+
+Changelog:
+ * 31/8-2016: Updated the printed traceback in case that the given file
+   can not be imported.
+ * 11/10-2019: Add more tests to avoid getting incorrect solutions.
+ * 18/12-2019: Follow PEP8/PEP257
+"""
+
+
+from argparse import ArgumentParser
+from importlib.machinery import SourceFileLoader
+import sys
+from traceback import format_exc
+from copy import deepcopy
+
+
+def is_same_list(a, b):
+    """
+    Check if two lists have the same values
+    and nested lists.
+    """
+    if not isinstance(a, list) or not isinstance(b, list):
+        return a == b
+
+    for a_elem, b_elem in zip(a, b):
+        if isinstance(a_elem, list) or isinstance(b_elem, list):
+            if not is_same_list(a_elem, b_elem):
+                return False
+        else:
+            if not a == b:
+                return False
+    return True
+
+
+def is_same_dict(a, b):
+    """
+    Check if two dictionaries have the same
+    elements in them.
+    """
+
+    def equality(key):
+        return key in a and key in b and a[key] == b[key]
+
+    return all(map(equality, a.keys())) and all(map(equality, b.keys()))
+
+
+message_a = [
+    "hTEeSj_CO",
+    "'lMiED)teD5E,_hLAe;Nm,0@Dli&Eg ,#4aI?rN@T§&e7#4E #<(S0A?<)NT8<0'",
+    "fTlH-yE((g 48aQnUdI#5eC_K§b 29äB>cRkO7-aW-0sN#i 0>nFeOrX=!_ =!sJöUkM1aP(>"
+    "_Sh wOiV,lE4aR3_ pTåH_Em jLuA§kZ`aY>_ @tDuO5vGo$r1",
+    "vJ4&eO1m _E`äNr #!_S´-dÄ=eR+nD!-nE1eL>?_EtS+e <$sK9#cLoO_Ka lK6(lAaT9;_T;"
+    "t>a0l1´&a76?r$_15o,m§",
+    "WhEa'n<=R_´Ek o§Nm&O_,7 s!So1TmR_%Ae%Nt#@GtE_/Ry`2Sr>! v*;Tä>Od<< eLr44O_"
+    "Ve0En)|_/+ a6YpOrUi/& lKa9Nf§Ot<Wo$ n1*T_8Ho`7Ec/= hR_?0Uh0La8<EdSe32 _`1"
+    "Ae%0Nt*Dt _35ShOö2 gDaOn ä,Is|k rAu; s>$F_7UiL_@Le8 n&C_`%Os;Mv6+Må2In+Tg"
+    "<<Mr=5Ee,Nm+T_&'oSm _0(Wh#HaAl*5Ts e5In5'.M_`? c&>Tl,Ha@5IrNa9(K_Io#$Nc$?"
+    "Gh5 _5?OlFo!|t tYeOnU_? vWoOr(´Uo34L_)Di>Nn(3'n6@Te?> _=Gm#2Ee;Td§$ _7Ts,"
+    "HkIö>St! e#Fk3Ra0On=M_ a%At!)Nt!8Y_? hOäTm%1Ht74Ea+R_8; hGo=Un-*Yo)|m´& _"
+    "<§Ip&4 åJ_UdSa=´Tl% a-+Wr)AöN_Nb=5Ar yTg)Eg4LaL; _Ym7Oe&Un _3§Hd/Oe%8Wt5 "
+    "_Id-3'r=/Mö j?!FdEe1E_Le73IvNiGg?1|h<% e´(Gt@*Oe,Tr%T,A_ iMn6An`§Ka2En3! "
+    "_47Yd5´OeU_ k@Uo-NmDmEo%R_SiT_&<Ab)NåDt69|.",
+    "+Y_8O&d5§(Uon5 A'`LtL7_ §bS5e_T3sI>oLL_ haH(Ar´=dV7E_o+ >Zn´§_OI2$yDo1*uB"
+    "0E6rsReG4#!l3#f ,7_1*/f´ry.2_y!8`o-u§?;9_lo-5st-_/´§*t4*h8;e_)46w>om/=a;n"
+    "-78_7o8f$_*y`/o7u3r;_>d`0#7r$§-e>am´@,s,&$<_(;=bu42t1_`3yo;<u_s>5=t`i´0l("
+    "l(_1h,@a$4*v3/61e_z>6o=3i#d@ber(0g<-.",
+]
+
+expected_a = [
+    ("hej_", "TESCO"),
+    ("lite_hemligare", "MEDDELANDE INTE SANT"),
+    (
+        "flygande_bäckasiner_söka_hwila_på_mjuka_tuvor",
+        "THE QUICK BROWN FOX JUM" "PS OVER THE LAZY DOG",
+    ),
+    ("vem_är_denne_tesco_alla_talar_om", "JO EN SÄRDELES KLOK KATT"),
+    (
+        "han_kom_som_ett_yrväder_en_aprilafton_och_hade_ett_höganäskrus_i_en_svån"
+        "grem_om_halsen._clara_och_lotten_voro_inne_med_skötekan_att_hämta_honom_"
+        "på_dalarö_brygga_men_det_dröjde_evigheter_innan_de_kommo_i_båt.",
+        "WERE "
+        "NO STRANGERS TO LOVE| YOU KNOW THE RULES AND SO DO I| A FULL COMMITMENTS"
+        " WHAT IM THINKING OF| YOU WOULDNT GET THIS FROM ANY OTHER GUY| I JUST WA"
+        "NNA TELL YOU HOW IM FEELING| GOTTA MAKE YOU UNDERSTAND|",
+    ),
+    (
+        "_dont_be_so_hard_on_yourself_fry._you_lost_the_woman_of_your_dreams_but_"
+        "you_still_have_zoidberg.",
+        "YOU ALL STILL HAVE ZOIDBERG ",
+    ),
+]
+
+
+def test_split_it():
+    """
+    Tests the split_it function
+    """
+    for msg, expected in zip(message_a, expected_a):
+        assert (
+            lab4.split_it(msg) == expected
+        ), '\nsplit_it("{}"):\nexpected: {}\ngot: {}'.format(
+            msg, str(expected), str(lab4.split_it(msg))
+        )
+
+
+def test_split_rec():
+    """
+    Tests the split_rec function
+    """
+    for msg, expected in zip(message_a, expected_a):
+        assert (
+            lab4.split_rec(msg) == expected
+        ), '\nsplit_it("{}"):\nexpected: {}\ngot: {}'.format(
+            msg, str(expected), str(lab4.split_rec(msg))
+        )
+
+
+def test_interpret_wrapper(expr, defs, expected):
+    backup_expr = deepcopy(expr)
+    backup_defs = defs.copy()
+    result = lab4.interpret(expr, defs)
+    assert (
+        result == expected
+    ), '''interpret({}, {}):\nexpected: "{}"\ngot: "{}"'''.format(
+        backup_expr, backup_defs, expected, result
+    )
+    assert is_same_list(expr, backup_expr), "Not allowed to modify arguments."
+    assert is_same_dict(defs, backup_defs), "Not allowed to modify arguments."
+
+
+def test_interpret():
+    """
+    Tests the interpret function
+    """
+    test_interpret_wrapper(
+        ["door_open", "AND", "cat_gone"],
+        {"door_open": "false", "cat_gone": "true", "cat_asleep": "true"},
+        "false",
+    )
+    test_interpret_wrapper(
+        ["cat_asleep", "OR", ["NOT", "cat_gone"]],
+        {"door_open": "false", "cat_gone": "true", "cat_asleep": "true"},
+        "true",
+    )
+    test_interpret_wrapper(["true", "OR", "true"], {}, "true")
+    test_interpret_wrapper(
+        "cat_gone", {"door_open": "false", "cat_gone": "true"}, "true"
+    )
+    test_interpret_wrapper(
+        ["NOT", ["NOT", ["NOT", ["cat_asleep", "OR", ["NOT", "cat_asleep"]]]]],
+        {"cat_asleep": "false"},
+        "false",
+    )
+    test_interpret_wrapper(["NOT", "AND", "OR"], {"NOT": "true", "OR": "true"}, "true")
+
+    test_interpret_wrapper(["NOT", "true"], {"NOT": "false"}, "false")
+    test_interpret_wrapper(["NOT", "NOT"], {"NOT": "false"}, "true")
+    test_interpret_wrapper(["NOT", "AND"], {"AND": "false"}, "true")
+    test_interpret_wrapper(["AND", "AND", "AND"], {"AND": "true"}, "true")
+    test_interpret_wrapper("true", {"cat_gone": "false"}, "true")
+    test_interpret_wrapper([["true", "OR", "false"], "AND", "true"], {}, "true")
+
+    test_interpret_wrapper(["true", "OR", "true"], {}, "true")
+    test_interpret_wrapper(["false", "OR", "true"], {}, "true")
+    test_interpret_wrapper(["true", "OR", "false"], {}, "true")
+    test_interpret_wrapper(["false", "OR", "false"], {}, "false")
+
+    test_interpret_wrapper(["true", "AND", "true"], {}, "true")
+    test_interpret_wrapper(["false", "AND", "true"], {}, "false")
+    test_interpret_wrapper(["true", "AND", "false"], {}, "false")
+    test_interpret_wrapper(["false", "AND", "false"], {}, "false")
+
+
+if __name__ == "__main__":
+    arg_parser = ArgumentParser()
+    arg_parser.add_argument(
+        "--test", choices=["a", "A", "b", "B"], default="", required=False
+    )
+    arg_parser.add_argument("file")
+    args = arg_parser.parse_args()
+    if args.file.rfind("/") != -1:
+        sys.path.append(args.file[: args.file.rfind("/")])
+        potential_name = args.file[args.file.rfind("/") + 1 :]
+    else:
+        sys.path.append(".")
+        potential_name = args.file
+    if potential_name.rfind("."):
+        name = potential_name[: potential_name.rfind(".")]
+    else:
+        name = potential_name
+    try:
+        lab4 = SourceFileLoader(name, args.file).load_module()
+    except FileNotFoundError:
+        print("Could not import lab: " + args.file)
+        print("See traceback for further information:")
+        print()
+        stack_trace = format_exc().split("\n")
+        importlib_has_started = False
+        importlib_has_ended = False
+        for line in stack_trace:
+            if (
+                not importlib_has_ended
+                and importlib_has_started
+                and line.lstrip().startswith("File")
+                and "importlib" not in line
+            ):
+                importlib_has_ended = True
+            if importlib_has_ended:
+                print(line)
+            elif (
+                not importlib_has_started
+                and line.lstrip().startswith("File")
+                and "importlib" in line
+            ):
+                importlib_has_started = True
+        exit(1)
+    if args.test.upper() == "A":
+        test_split_it()
+        test_split_rec()
+    elif args.test.upper() == "B":
+        test_interpret()
+    elif args.test == "":
+        test_split_it()
+        test_split_rec()
+        test_interpret()
+    else:
+        print("Unknown arguemnt for --test: " + args.test)
+        exit(2)
+    print("The code passed all the tests")
diff --git a/lab4/tree.py b/lab4/tree.py
new file mode 100644
index 0000000000000000000000000000000000000000..f2a96f0886e4f19948045f70c267300b29e5ad11
--- /dev/null
+++ b/lab4/tree.py
@@ -0,0 +1,22 @@
+# encoding: iso-8859-1
+
+# TDDE23 Lab 4: Binary tree
+
+def is_empty_tree(tree):
+    return isinstance(tree, list) and not tree
+
+
+def is_leaf(tree):
+    return isinstance(tree, int)
+
+
+def create_tree(left_tree, key, right_tree):
+    return [left_tree, key, right_tree]
+
+	
+def left_subtree(tree):
+    return tree[0]
+
+
+def right_subtree(tree):
+    return tree[2]
diff --git a/lab5/cvlib.py b/lab5/cvlib.py
new file mode 100644
index 0000000000000000000000000000000000000000..8ad662dbb2fa0d2ce55a1a66782940a8d47f709a
--- /dev/null
+++ b/lab5/cvlib.py
@@ -0,0 +1,56 @@
+"""cvlib module, helper functions for image and tuple manipulation."""
+
+import cv2
+import numpy
+
+# ------------------------------------------
+#  Helper functions
+# ------------------------------------------
+
+
+def multiply_tuple(tpl, mult):
+    """Return a tuple where all elements are scaled by factor 'mult'.
+
+    (a,b,c) * k = (a*k, b*k, c*k)
+    """
+    return tuple(map(lambda x: x*mult, tpl))
+
+
+def add_tuples(tpl1, tpl2):
+    """
+    Return a the element-wise sum of tpl1 and tpl2.
+
+    (a,b,c) + (d,e,f) = (a+d, b+e, c+f)
+    """
+    return tuple(map(lambda t1, t2: t1+t2, tpl1, tpl2))
+
+
+# -------------------------------------------
+#  Converting between python list and images
+# -------------------------------------------
+
+
+def rgblist_to_cvimg(lst, height, width):
+    """Return a width x height OpenCV image with specified pixels."""
+    # A 3d array that will contain the image data
+    img = numpy.zeros((height, width, 3), numpy.uint8)
+
+    for x in range(0, width):
+        for y in range(0, height):
+            pixel = lst[y * width + x]
+            img[y, x, 0] = pixel[0]
+            img[y, x, 1] = pixel[1]
+            img[y, x, 2] = pixel[2]
+
+    return img
+
+
+def greyscale_list_to_cvimg(lst, height, width):
+    """Return a width x height grayscale OpenCV image with specified pixels."""
+    img = numpy.zeros((height, width), numpy.uint8)
+
+    for x in range(0, width):
+        for y in range(0, height):
+            img[y, x] = lst[y * width + x]
+
+    return img
diff --git a/lab5/lab5a1.py b/lab5/lab5a1.py
new file mode 100644
index 0000000000000000000000000000000000000000..315b3ed42a8334f6bc1c32c370141907e48b990a
--- /dev/null
+++ b/lab5/lab5a1.py
@@ -0,0 +1,19 @@
+import cv2
+import numpy
+import array
+from cvlib import *
+
+#img = cv2.imread('b.jpg')
+def cvimg_to_list(img):
+    '''cvimg_to_list är en funktion som tar en OpenCV-bild och returnerar en lista med BGR-tupler '''
+    new_list = []
+    #konverterar en array till en lista med listor i
+    list_of_lists = img.tolist()
+    #sparar alla listor i en enda lista
+    for listt in list_of_lists:
+        for item in listt:
+            new_list.append(item)
+            
+    #konverterar en lista med listor i till en lista med tuples i
+    list_of_tuples = [tuple(i) for i in new_list]
+    return(list_of_tuples)
diff --git a/lab5/lab5a2.py b/lab5/lab5a2.py
new file mode 100644
index 0000000000000000000000000000000000000000..cfb731de43edcd176ae1f05fa72e3f51724c28d0
--- /dev/null
+++ b/lab5/lab5a2.py
@@ -0,0 +1,67 @@
+import math
+import cv2
+import numpy
+from cvlib import *
+
+
+
+def unsharp_mask(n):
+    """ En funktion som tar en parameter N som indata och returnerar en 2D-lista som vi kan använda som indata till arraykonvertering för att öka skärpen i en bild  """
+    result = []
+    N_list = N_to_list_xy(n)
+    combine =[]
+    hjälp_list = []
+    
+    #for_loopen kombinerar N_list
+    for i in range(len(N_list)):
+        for e in range(len(N_list)):
+            combine.append((N_list[i],N_list[e]))
+            
+    #Bestämmer antal rader och kolumner som NxN 
+    for y in range(n**2):
+        hjälp_list.append(Negativ_gaussisk_blur(combine[y][0],combine[y][1]))
+        
+        if len(hjälp_list) == n:
+            
+            result.append(hjälp_list)
+            hjälp_list = []
+            
+    return result
+    
+
+def N_to_list_xy(n):
+    """ En funktion som returnerar en lista av xy värden genom att mata in n värdet """
+    if n == 1:
+        return [0]
+    
+    elif n == 2:
+        return [-1,0]
+    
+    elif n > 2:
+        result = [-1,0]
+        
+        for i in range(3,n+1):
+            #Ifall i var udda läggs absolutbeloppet av den mista siffran i listan i slutet  
+            if i%2 == 1:
+                result.insert(len(result), abs(result[0]))
+                
+            #Ifall i var jämn subtraheras den minsta siffran i listan med 1 och läggs i början av listan  
+            else:
+                result.insert(0, result[0]-1)
+            
+        return result 
+            
+                   
+
+def Negativ_gaussisk_blur(x,y):
+    """ En funktion som gör beräkningen för de Negativ_gaussisk_blur genom att mata in värdet på x och y """
+    
+    pi = math.pi
+    e  = math.e
+    s  = 4.5
+    if x == 0 and y == 0:
+        return 1.5
+    else:
+        return (-(1/ (2 * pi * s**2 ))*   (e**    (-     ((x**2)+(y**2)) / (2*(s**2))    )      ) )
+    
+    
diff --git a/lab5/lab5b1.py b/lab5/lab5b1.py
new file mode 100644
index 0000000000000000000000000000000000000000..3bc357ec3ec32c7b4598de29fa638e7aca4535f7
--- /dev/null
+++ b/lab5/lab5b1.py
@@ -0,0 +1,28 @@
+import cv2
+import numpy
+from övn5a1 import *
+
+def pixel_constraint(hlow, hhigh, slow, shigh, vlow, vhigh):
+    """ En funktion som skapar en ny funktion som givet en pixel returnerar om den ligger mellan low och high för de tre färgkanalerna  """
+    
+    def is_black(hsv):
+        
+        try:
+            if type(hsv) != tuple or 255 < hsv[0] or hsv[0] < 0 or 255 < hsv[1] or hsv[1]< 0 or 255 < hsv[2] or hsv[2]< 0:
+                raise ValueError ('Inmatningen är ingen pixel')
+               
+            
+            
+            if hhigh >= hsv[0] >= hlow and shigh >= hsv[1] >= slow and vhigh >= hsv[2] >= vlow:
+                return 1
+            
+            else:
+                return 0
+        except ValueError as exc:
+            raise ValueError(exc) from exc
+       
+        
+            
+        
+        
+    return is_black
diff --git a/lab5/lab5b2.py b/lab5/lab5b2.py
new file mode 100644
index 0000000000000000000000000000000000000000..662eef2bd2c11b2d7f0e966e1adb8ee10a074dc1
--- /dev/null
+++ b/lab5/lab5b2.py
@@ -0,0 +1,18 @@
+import cv2
+import numpy
+import random
+from övn5a1 import *
+from övn5b1 import *
+
+def generator_from_image(orig_list):
+    """ En funktion som tar en bild som indata och returnerar en funktion som givet ett index för en pixel returnerar bildens färg i den pixeln """
+    
+    def Generator2(i):
+        try:
+            orig_list[i]
+        except IndexError as exc:
+            raise IndexError(exc) from exc
+            
+        return orig_list[i]
+    
+    return Generator2
diff --git a/lab5/lab5b3.py b/lab5/lab5b3.py
new file mode 100644
index 0000000000000000000000000000000000000000..37935a0e4e02086405d56190e71dce1692d7d395
--- /dev/null
+++ b/lab5/lab5b3.py
@@ -0,0 +1,63 @@
+import cv2
+import numpy
+import random
+from lab5a1 import *
+from lab5b1 import *
+from lab5b2 import *
+
+
+
+
+def generator1(index):
+    """ En funktion som gör en bild av en stjärnhimmel"""
+    val = random.random() * 255 if random.random() > 0.99 else 0
+    return (val, val, val)
+
+
+    
+def combine_images(mask, mask_function, image_generator1, image_generator2):
+    
+    """
+    Combines 2 images
+
+    Input:
+    mask: Image to be interpreted by the mask function
+    mask_function: Returns a value between 0 and 1 for any pixel value. A value of 0 means
+            means use only image 1 and a value of 1 means only use image 2. A value between
+            0 and 1 means use (value)*image1 + (1-value)*image2
+    image_generator1: Returns a pixel value for each pixel in an image
+    image_generator2: -||-
+
+    Output:
+    A new image which is a combination of image1 and 2
+    """
+    
+    result = []
+    try:
+        for i in range(len(mask)):
+            if mask_function(mask[i]) == 1:
+                result.append(image_generator1(i))
+                
+            elif mask_function(mask[i]) == 0:
+                result.append(image_generator2(i))
+                
+            else:
+                # generator1*condition + generator2*(1 - condition)
+                condition = mask_function(mask[i])
+                condition2 = 1 - condition
+                
+                blue  = round((image_generator1(i)[0] * condition)+(image_generator2(i)[0] * condition2),0)
+                green = round((image_generator1(i)[1] * condition)+(image_generator2(i)[1] * condition2),0)
+                red   = round((image_generator1(i)[2] * condition)+(image_generator2(i)[2] * condition2),0)
+                
+                bgr = (blue,green,red)
+                
+                
+                result.append(bgr)
+    
+    except Exception as e:
+        print(f'fel inmatning : {e}')
+            
+       
+    return result
+
diff --git a/lab5/lab5b4.py b/lab5/lab5b4.py
new file mode 100644
index 0000000000000000000000000000000000000000..3f8ea36a0b0749e09ed54d822f6259c7f50eda66
--- /dev/null
+++ b/lab5/lab5b4.py
@@ -0,0 +1,13 @@
+import cv2
+import numpy
+import random
+from lab5a1 import *
+from lab5b1 import *
+from lab5b2 import *
+from lab5b3 import *
+
+
+
+def gradient_condition(hsv):
+    ''' Returnerar ett medel värde från 0-1 genom att ta medelvärdet av summan av hsv värdena '''
+    return (hsv[0] + hsv[1] + hsv[2])/ (255 * 3 )
diff --git a/lab5/lab5c12.py b/lab5/lab5c12.py
new file mode 100644
index 0000000000000000000000000000000000000000..004d33fce42199fefc5848c804ee8c3554781599
--- /dev/null
+++ b/lab5/lab5c12.py
@@ -0,0 +1,89 @@
+from övn5b1 import *
+from övn5b2 import *
+from övn5b3 import *
+from övn5b4 import *
+
+
+    
+def test_pixel_constraint():
+    
+    ''' Testar för att kolla hur funktionen beter sig om jag matar in hs som följer intervallerna men inte v '''
+    hsv2 = (150, 60, 9)
+    f2 = pixel_constraint(20, 170, 5, 210, 1, 5)
+    result2 = f2(hsv2)
+    assert(result2 == 0)
+    
+    ''' Testar ifall funktionen returnerar rätt värdet om jag begränsar intervallet till ett färg bara  '''
+    hsv3 = (0, 0, 0)
+    f3 = pixel_constraint(0, 0, 0, 0, 0, 0)
+    result3 = f3(hsv3)
+    assert(result3 == 1)
+    
+    """ Förklaras i motiveringsfilen  """
+    data = (150, 60, -2)
+    f = pixel_constraint(20, 170, 5, 210, 0, 5)
+    try:
+        f(data)
+        assert False
+    except ValueError:
+        assert True
+    
+    print('Done')
+    
+def test_generator_from_image():
+    ''' Testar ifall jag skickar in en lista med tupler istället för att mata in hela bildens info, och se om den funkar rätt '''
+    data = [(0, 1, 0),(34, 66, 0),(50, 3, 1),(0, 0, 0),(1000, 0, 555)]
+    f    = generator_from_image(data)
+    result = f(2)
+    assert(result == data[2])
+    
+    ''' Testar om det funkar med listor istället för tupler '''
+    data2 = [[0, 1, 0],[34, 66, 0],[50, 3, 1],[0, 0, 0],[1000, 0, 555]]
+    f2    = generator_from_image(data2)
+    result2 = f2(2)
+    assert(result2 == data2[2])
+    
+    """ Förklaras i motiveringsfilen  """
+    data3 = [(0, 1, 0),(34, 66, 0),(50, 3, 1),(0, 0, 0),(1000, 0, 555)]    
+    
+
+    try:
+        generator_from_image(data3)(9)
+        assert False
+    except IndexError:
+        assert True
+        
+    print('Done')
+    
+    
+    
+def test_combine_images():
+    
+    """ Testar för att se om den kommer synas någon sort skillnad när man gör beräkningen beroende på gradient bilden. Den som funktionen returnerar är exakt samma bild """
+    gradient_img      = cv2.imread("gradient.jpg")
+    gradient_img_list = cvimg_to_list(gradient_img)
+    mask              = cvimg_to_list(gradient_img)
+
+    flowers_img      = cv2.imread("flowers.jpg")
+    flowers_img_list = cvimg_to_list(flowers_img)
+    generator1     = generator_from_image(flowers_img_list)
+
+
+    condition = pixel_constraint(0, 0, 0, 0, 0, 0)
+    
+    
+    result = combine_images(mask, gradient_condition, generator1, generator1)
+    
+    assert(result == flowers_img_list)
+    
+    
+    """ Förklaras i motiveringsfilen  """
+    
+    try:
+        combine_images(mask, 'gradient_condition', generator1, generator1)
+        assert False
+    except AssertionError:
+        assert True
+
+   
+    print('Done')
diff --git a/lab5/motivering.txt b/lab5/motivering.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9d56a6fe77d1d35b4eea8f13dcb477b79b2c2904
--- /dev/null
+++ b/lab5/motivering.txt
@@ -0,0 +1,23 @@
+pixel_constraint()
+
+i det första testet valde jag ett hsv värde med ett värde på v som ligger över för gränsen för att testa att funktionen funkar som den ska.
+
+i den andra fallet valde jag att ett värde som ligger precis på den angivna gränsen för att se till att funktionen funkar rätt.
+
+
+Syftet med denna undantags testfunktion är att kontrollera hur funktionen "pixel_constraint" hanterar felaktiga värden för en pixel. I detta specifika test, mats in ett värde för ljusstyrkan på pixeln som är utanför det giltiga intervalllet, nämligen "-2". Eftersom en pixelvärde för ljusstyrka ska vara mellan 0 och 255, är värdet "-2" inte en giltig pixel och därför bör funktionen "pixel_constraint" returnera ett felmeddelande. Detta test syftar till att kontrollera att funktionen är korrekt implementerad och hanterar felaktiga värden på ett lämpligt sätt.
+
+test_generator_from_image()
+
+första fallet valde jag att mata en enkel värde f(2)(== data[2]) för att se till att funktionen funkar som den ska.
+
+den andra falleär identisk med den första fast jag matade in en lista med listor i istället för tupler. för att se till att funktionen funkar rätt.
+
+
+Syftet med det här undantags testet är att kontrollera hur en generator hanterar felaktiga index. Jag skickar ett felaktigt index till generatorn genom att anropa "generator_from_image(data3)(9)". Eftersom detta index inte finns i data3 borde detta resultera i ett Error. Detta test syftar till att säkerställa att generatorn är korrekt implementerad och hanterar felaktiga index på ett lämpligt sätt.
+
+test_combine_images()
+
+den första fallet valde jag en bild och kombinerade den med sig själv. Detta är för att se till att funktionen funkar som den ska. då funktionen bör returnera ett identisk bild av de jag skickade. alltså att den går genom gradient.
+
+i det andra fallet tänkte jag använda mig av ett felaktig sökning genom att mata in 'gradient_condition'. detta är för att se till att funktionen inte kommer gå vidare på något sätt när det matas in fel variabler.